@capgo/capacitor-jw-player 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CapgoCapacitorJwPlayer.podspec +18 -0
  2. package/Package.swift +30 -0
  3. package/README.md +493 -0
  4. package/android/build.gradle +83 -0
  5. package/android/src/main/AndroidManifest.xml +37 -0
  6. package/android/src/main/java/ee/forgr/capacitor_jw_player/JwPlayerPlugin.java +529 -0
  7. package/android/src/main/java/ee/forgr/capacitor_jw_player/PlayerActivity.java +469 -0
  8. package/android/src/main/res/.gitkeep +0 -0
  9. package/android/src/main/res/drawable/close_button_background.xml +5 -0
  10. package/android/src/main/res/layout/activity_player.xml +28 -0
  11. package/android/src/main/res/values/colors.xml +16 -0
  12. package/android/src/main/res/values/strings.xml +3 -0
  13. package/android/src/main/res/values/styles.xml +32 -0
  14. package/dist/docs.json +459 -0
  15. package/dist/esm/definitions.d.ts +156 -0
  16. package/dist/esm/definitions.js +2 -0
  17. package/dist/esm/definitions.js.map +1 -0
  18. package/dist/esm/index.d.ts +4 -0
  19. package/dist/esm/index.js +7 -0
  20. package/dist/esm/index.js.map +1 -0
  21. package/dist/esm/web.d.ts +57 -0
  22. package/dist/esm/web.js +65 -0
  23. package/dist/esm/web.js.map +1 -0
  24. package/dist/plugin.cjs.js +79 -0
  25. package/dist/plugin.cjs.js.map +1 -0
  26. package/dist/plugin.js +82 -0
  27. package/dist/plugin.js.map +1 -0
  28. package/ios/Sources/JwPlayerPlugin/JwPlayer.swift +24 -0
  29. package/ios/Sources/JwPlayerPlugin/JwPlayerPlugin.swift +749 -0
  30. package/ios/Sources/JwPlayerPlugin/LinkedList.swift +219 -0
  31. package/ios/Sources/JwPlayerPlugin/VideosToPlay.swift +56 -0
  32. package/ios/Tests/JwPlayerPluginTests/JwPlayerPluginTests.swift +15 -0
  33. package/package.json +80 -0
@@ -0,0 +1,529 @@
1
+ package ee.forgr.capacitor_jw_player;
2
+
3
+ import android.content.Intent;
4
+ import android.util.Log;
5
+
6
+ import com.getcapacitor.JSArray;
7
+ import com.getcapacitor.JSObject;
8
+ import com.getcapacitor.Plugin;
9
+ import com.getcapacitor.PluginCall;
10
+ import com.getcapacitor.PluginMethod;
11
+ import com.getcapacitor.annotation.CapacitorPlugin;
12
+ import com.jwplayer.pub.api.JWPlayer;
13
+ import com.jwplayer.pub.api.configuration.PlayerConfig;
14
+ import com.jwplayer.pub.api.license.LicenseUtil;
15
+ import com.jwplayer.pub.api.media.playlists.PlaylistItem;
16
+ import com.jwplayer.pub.api.media.audio.AudioTrack;
17
+ import com.jwplayer.pub.api.media.captions.Caption;
18
+
19
+ import java.lang.ref.WeakReference;
20
+ import java.util.List;
21
+ import java.util.ArrayList;
22
+
23
+ @CapacitorPlugin(name = "JwPlayer")
24
+ public class JwPlayerPlugin extends Plugin {
25
+
26
+ private static final String TAG = "JwPlayerPlugin";
27
+ // Static reference to the plugin instance for activity communication
28
+ private static WeakReference<JwPlayerPlugin> staticPluginRef;
29
+ private static JWPlayer staticPlayerInstance; // To control playback from plugin methods
30
+
31
+ @Override
32
+ public void load() {
33
+ super.load();
34
+ staticPluginRef = new WeakReference<>(this);
35
+ Log.i(TAG, "Plugin loaded and static reference set.");
36
+ // License key initialization might be better placed here or in initialize()
37
+ }
38
+
39
+ @PluginMethod
40
+ public void initialize(PluginCall call) {
41
+ String licenseKey = call.getString("licenseKey");
42
+ if (licenseKey == null || licenseKey.isEmpty()) {
43
+ call.reject("licenseKey is required for initialize");
44
+ return;
45
+ }
46
+ Log.i(TAG, "Initializing JW Player SDK with license key.");
47
+ // Initialize license
48
+ new LicenseUtil().setLicenseKey(getContext(), licenseKey);
49
+ // You might want to add success/failure listeners to the license call if available
50
+ call.resolve();
51
+ }
52
+
53
+ @PluginMethod
54
+ public void play(PluginCall call) {
55
+ String mediaUrl = call.getString("mediaUrl");
56
+ String mediaType = call.getString("mediaType");
57
+
58
+ if (mediaUrl == null || mediaUrl.isEmpty()) {
59
+ call.reject("mediaUrl is required for play");
60
+ return;
61
+ }
62
+ if (mediaType == null || mediaType.isEmpty()) {
63
+ call.reject("mediaType is required for play");
64
+ return;
65
+ }
66
+
67
+ Log.d(TAG, "Launching PlayerActivity with URL: " + mediaUrl + ", Type: " + mediaType);
68
+
69
+ Intent intent = new Intent(getContext(), PlayerActivity.class);
70
+ intent.putExtra(PlayerActivity.EXTRA_MEDIA_URL, mediaUrl);
71
+ intent.putExtra(PlayerActivity.EXTRA_MEDIA_TYPE, mediaType);
72
+ getActivity().startActivity(intent);
73
+
74
+ // We resolve immediately, actual playback state comes via events
75
+ call.resolve();
76
+ }
77
+
78
+ @PluginMethod
79
+ public void pause(PluginCall call) {
80
+ Log.d(TAG, "Pause called");
81
+ if (staticPlayerInstance != null) {
82
+ staticPlayerInstance.pause();
83
+ call.resolve();
84
+ } else {
85
+ call.reject("Player not active");
86
+ }
87
+ }
88
+
89
+ @PluginMethod
90
+ public void resume(PluginCall call) {
91
+ Log.d(TAG, "Resume called");
92
+ if (staticPlayerInstance != null) {
93
+ staticPlayerInstance.play();
94
+ call.resolve();
95
+ } else {
96
+ call.reject("Player not active");
97
+ }
98
+ }
99
+
100
+ @PluginMethod
101
+ public void stop(PluginCall call) {
102
+ Log.d(TAG, "Stop called");
103
+ if (staticPlayerInstance != null) {
104
+ staticPlayerInstance.stop();
105
+ // Consider finishing PlayerActivity here? Maybe PlayerActivity should finish itself on stop.
106
+ call.resolve();
107
+ } else {
108
+ call.reject("Player not active");
109
+ }
110
+ }
111
+
112
+ @PluginMethod
113
+ public void seekTo(PluginCall call) {
114
+ Double time = call.getDouble("time");
115
+ if (time == null) {
116
+ call.reject("time parameter is required");
117
+ return;
118
+ }
119
+ if (staticPlayerInstance != null) {
120
+ staticPlayerInstance.seek(time);
121
+ call.resolve();
122
+ } else {
123
+ call.reject("Player not active");
124
+ }
125
+ }
126
+
127
+ @PluginMethod
128
+ public void setVolume(PluginCall call) {
129
+ Double volume = call.getDouble("volume"); // JW Player Android uses 0-100
130
+ if (volume == null) {
131
+ call.reject("volume parameter is required");
132
+ return;
133
+ }
134
+ if (staticPlayerInstance != null) {
135
+ // Cast the result to int
136
+ staticPlayerInstance.setVolume((int) (volume.floatValue() * 100));
137
+ call.resolve();
138
+ } else {
139
+ call.reject("Player not active");
140
+ }
141
+ }
142
+
143
+ @PluginMethod
144
+ public void setSpeed(PluginCall call) {
145
+ Double speed = call.getDouble("speed");
146
+ if (speed == null) {
147
+ call.reject("speed parameter is required");
148
+ return;
149
+ }
150
+ if (staticPlayerInstance != null) {
151
+ staticPlayerInstance.setPlaybackRate(speed.floatValue());
152
+ call.resolve();
153
+ } else {
154
+ call.reject("Player not active");
155
+ }
156
+ }
157
+
158
+ @PluginMethod
159
+ public void getPosition(PluginCall call) {
160
+ if (staticPlayerInstance != null) {
161
+ JSObject ret = new JSObject();
162
+ ret.put("position", staticPlayerInstance.getPosition());
163
+ call.resolve(ret);
164
+ } else {
165
+ call.reject("Player not active");
166
+ }
167
+ }
168
+
169
+ @PluginMethod
170
+ public void getState(PluginCall call) {
171
+ if (staticPlayerInstance != null) {
172
+ JSObject ret = new JSObject();
173
+ // Convert JWPlayerState enum to int like iOS
174
+ int stateInt = mapPlayerStateToInt(staticPlayerInstance.getState());
175
+ ret.put("state", stateInt);
176
+ call.resolve(ret);
177
+ } else {
178
+ call.reject("Player not active", "-1"); // Return -1 or similar on error
179
+ }
180
+ }
181
+
182
+ @PluginMethod
183
+ public void loadPlaylist(PluginCall call) {
184
+ String playlistUrl = call.getString("playlistUrl");
185
+ if (playlistUrl == null || playlistUrl.isEmpty()) {
186
+ call.reject("playlistUrl parameter is required");
187
+ return;
188
+ }
189
+ if (staticPlayerInstance != null) {
190
+ Log.d(TAG, "Loading playlist from URL: " + playlistUrl);
191
+ // Use PlayerConfig with a single playlist item URL
192
+ PlaylistItem playlistItem = new PlaylistItem.Builder()
193
+ .file(playlistUrl)
194
+ .build();
195
+ List<PlaylistItem> playlist = new ArrayList<>();
196
+ playlist.add(playlistItem);
197
+ PlayerConfig config = new PlayerConfig.Builder()
198
+ .playlist(playlist)
199
+ // Autostart false when just loading?
200
+ .autostart(false)
201
+ .build();
202
+ staticPlayerInstance.setup(config); // Use setup for subsequent loads
203
+ call.resolve();
204
+ } else {
205
+ call.reject("Player not active");
206
+ }
207
+ }
208
+
209
+ @PluginMethod
210
+ public void loadPlaylistWithItems(PluginCall call) {
211
+ JSArray itemsJS = call.getArray("playlist");
212
+ if (itemsJS == null) {
213
+ call.reject("playlist parameter is required and must be an array");
214
+ return;
215
+ }
216
+ if (staticPlayerInstance != null) {
217
+ try {
218
+ List<PlaylistItem> playlist = new ArrayList<>();
219
+ for (int i = 0; i < itemsJS.length(); i++) {
220
+ JSObject itemJS = JSObject.fromJSONObject(itemsJS.getJSONObject(i));
221
+ String file = itemJS.getString("file");
222
+ if (file != null) {
223
+ PlaylistItem.Builder itemBuilder = new PlaylistItem.Builder().file(file);
224
+ if (itemJS.has("title")) {
225
+ itemBuilder.title(itemJS.getString("title"));
226
+ }
227
+ playlist.add(itemBuilder.build());
228
+ }
229
+ }
230
+ if (!playlist.isEmpty()) {
231
+ Log.d(TAG, "Loading playlist with " + playlist.size() + " items");
232
+ // Use PlayerConfig for loading multiple items too
233
+ PlayerConfig config = new PlayerConfig.Builder()
234
+ .playlist(playlist)
235
+ .autostart(false) // Or true depending on desired behavior
236
+ .build();
237
+ staticPlayerInstance.setup(config); // Use setup
238
+ call.resolve();
239
+ } else {
240
+ call.reject("No valid playlist items found");
241
+ }
242
+ } catch (Exception e) {
243
+ Log.e(TAG, "Error parsing playlist items", e);
244
+ call.reject("Error parsing playlist items: " + e.getMessage());
245
+ }
246
+ } else {
247
+ call.reject("Player not active");
248
+ }
249
+ }
250
+
251
+ @PluginMethod
252
+ public void setPlaylistIndex(PluginCall call) {
253
+ Integer index = call.getInt("index");
254
+ if (index == null) {
255
+ call.reject("index parameter is required");
256
+ return;
257
+ }
258
+ if (staticPlayerInstance != null) {
259
+ Log.d(TAG, "Setting playlist index to: " + index);
260
+ staticPlayerInstance.playlistItem(index);
261
+ call.resolve();
262
+ } else {
263
+ call.reject("Player not active");
264
+ }
265
+ }
266
+
267
+ @PluginMethod
268
+ public void getAudioTracks(PluginCall call) {
269
+ if (staticPlayerInstance != null) {
270
+ List<AudioTrack> audioTracks = staticPlayerInstance.getAudioTracks();
271
+ JSArray tracksArray = new JSArray();
272
+ if (audioTracks != null) {
273
+ for (AudioTrack track : audioTracks) {
274
+ JSObject trackObj = new JSObject();
275
+ trackObj.put("name", track.getName());
276
+ trackObj.put("language", track.getLanguage());
277
+ // trackObj.put("defaultTrack", track.isDefaultTrack()); // Check API for default
278
+ tracksArray.put(trackObj);
279
+ }
280
+ }
281
+ JSObject result = new JSObject();
282
+ result.put("tracks", tracksArray);
283
+ call.resolve(result);
284
+ } else {
285
+ call.reject("Player not active");
286
+ }
287
+ }
288
+
289
+ @PluginMethod
290
+ public void getCurrentAudioTrack(PluginCall call) {
291
+ if (staticPlayerInstance != null) {
292
+ int index = staticPlayerInstance.getCurrentAudioTrack();
293
+ JSObject result = new JSObject();
294
+ result.put("index", index);
295
+ call.resolve(result);
296
+ } else {
297
+ call.reject("Player not active");
298
+ }
299
+ }
300
+
301
+ @PluginMethod
302
+ public void setCurrentAudioTrack(PluginCall call) {
303
+ Integer index = call.getInt("index");
304
+ if (index == null) {
305
+ call.reject("index parameter is required");
306
+ return;
307
+ }
308
+ if (staticPlayerInstance != null) {
309
+ Log.d(TAG, "Setting audio track index to: " + index);
310
+ staticPlayerInstance.setCurrentAudioTrack(index);
311
+ call.resolve();
312
+ } else {
313
+ call.reject("Player not active");
314
+ }
315
+ }
316
+
317
+ @PluginMethod
318
+ public void getCaptions(PluginCall call) {
319
+ if (staticPlayerInstance != null) {
320
+ List<Caption> captions = staticPlayerInstance.getCaptionsList();
321
+ JSArray captionsArray = new JSArray();
322
+ if (captions != null) {
323
+ for (int i = 0; i < captions.size(); i++) {
324
+ Caption caption = captions.get(i);
325
+ JSObject captionObj = new JSObject();
326
+ captionObj.put("index", i); // Use list index
327
+ captionObj.put("label", caption.getLabel());
328
+ // captionObj.put("language", caption.getLanguage()); // Check API
329
+ captionsArray.put(captionObj);
330
+ }
331
+ }
332
+ JSObject result = new JSObject();
333
+ result.put("captions", captionsArray);
334
+ call.resolve(result);
335
+ } else {
336
+ call.reject("Player not active");
337
+ }
338
+ }
339
+
340
+ @PluginMethod
341
+ public void getCurrentCaptions(PluginCall call) {
342
+ if (staticPlayerInstance != null) {
343
+ int index = staticPlayerInstance.getCurrentCaptions();
344
+ JSObject result = new JSObject();
345
+ result.put("index", index); // 0 typically means off
346
+ call.resolve(result);
347
+ } else {
348
+ call.reject("Player not active");
349
+ }
350
+ }
351
+
352
+ @PluginMethod
353
+ public void setCurrentCaptions(PluginCall call) {
354
+ Integer index = call.getInt("index");
355
+ if (index == null) {
356
+ call.reject("index parameter is required");
357
+ return;
358
+ }
359
+ if (staticPlayerInstance != null) {
360
+ Log.d(TAG, "Setting captions index to: " + index);
361
+ staticPlayerInstance.setCurrentCaptions(index);
362
+ call.resolve();
363
+ } else {
364
+ call.reject("Player not active");
365
+ }
366
+ }
367
+
368
+ @PluginMethod
369
+ public void currentPlaylist(PluginCall call) {
370
+ if (staticPlayerInstance != null) {
371
+ List<PlaylistItem> playlist = staticPlayerInstance.getPlaylist();
372
+ JSArray playlistJS = new JSArray();
373
+ if (playlist != null) {
374
+ for (PlaylistItem item : playlist) {
375
+ JSObject itemJS = new JSObject();
376
+ itemJS.put("file", item.getFile());
377
+ itemJS.put("title", item.getTitle());
378
+ itemJS.put("description", item.getDescription());
379
+ // Add other relevant properties
380
+ playlistJS.put(itemJS);
381
+ }
382
+ }
383
+ JSObject result = new JSObject();
384
+ result.put("playlist", playlistJS);
385
+ call.resolve(result);
386
+ } else {
387
+ call.reject("Player not active");
388
+ }
389
+ }
390
+
391
+ // --- Static methods for PlayerActivity Communication ---
392
+
393
+ public static void setStaticPlayerInstance(JWPlayer player) {
394
+ Log.d(TAG, "Setting static player instance: " + (player != null));
395
+ staticPlayerInstance = player;
396
+ }
397
+
398
+ public static void onPlayerDismissed() {
399
+ Log.d(TAG, "PlayerActivity dismissed, clearing static player instance.");
400
+ staticPlayerInstance = null;
401
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
402
+ if (plugin != null) {
403
+ plugin.notifyListeners("playerDismissed", null);
404
+ }
405
+ }
406
+
407
+ public static void notifyPipChanged(boolean isInPip) {
408
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
409
+ if (plugin != null) {
410
+ JSObject data = new JSObject();
411
+ data.put("isInPictureInPictureMode", isInPip);
412
+ plugin.notifyListeners(isInPip ? "pipStarted" : "pipStopped", data);
413
+ }
414
+ }
415
+
416
+ // Static notification helpers called by PlayerActivity listeners
417
+ public static void notifyReady() {
418
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
419
+ if (plugin != null) {
420
+ plugin.notifyListeners("ready", null);
421
+ }
422
+ }
423
+
424
+ public static void notifyError(String type, String message) {
425
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
426
+ if (plugin != null) {
427
+ JSObject data = new JSObject();
428
+ data.put("message", message);
429
+ // data.put("code", code); // Android SDK might not provide code easily
430
+ plugin.notifyListeners(type, data);
431
+ }
432
+ }
433
+
434
+ public static void notifyWarning(String type, String message) {
435
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
436
+ if (plugin != null) {
437
+ JSObject data = new JSObject();
438
+ data.put("message", message);
439
+ plugin.notifyListeners(type, data);
440
+ }
441
+ }
442
+
443
+ public static void notifyPlaylistItem(int index, PlaylistItem item) {
444
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
445
+ if (plugin != null) {
446
+ JSObject data = new JSObject();
447
+ data.put("index", index);
448
+ if (item != null) {
449
+ data.put("title", item.getTitle());
450
+ // data.put("file", item.getFile()); // Be careful with accessing file directly
451
+ }
452
+ plugin.notifyListeners("playlistItem", data);
453
+ }
454
+ }
455
+
456
+ public static void notifyPlaylistComplete() {
457
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
458
+ if (plugin != null) {
459
+ plugin.notifyListeners("complete", null); // Maybe playlistComplete event?
460
+ }
461
+ }
462
+
463
+ public static void notifyPause(String reason) {
464
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
465
+ if (plugin != null) {
466
+ JSObject data = new JSObject();
467
+ data.put("reason", reason); // Android uses enum names
468
+ plugin.notifyListeners("pause", data);
469
+ }
470
+ }
471
+
472
+ public static void notifyPlay(String reason) {
473
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
474
+ if (plugin != null) {
475
+ JSObject data = new JSObject();
476
+ data.put("reason", reason); // Android uses enum names
477
+ plugin.notifyListeners("play", data);
478
+ }
479
+ }
480
+
481
+ public static void notifyComplete() {
482
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
483
+ if (plugin != null) {
484
+ plugin.notifyListeners("complete", null);
485
+ }
486
+ }
487
+
488
+ public static void notifySeek(double position, double offset) {
489
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
490
+ if (plugin != null) {
491
+ JSObject data = new JSObject();
492
+ data.put("position", position);
493
+ data.put("offset", offset);
494
+ plugin.notifyListeners("seek", data);
495
+ }
496
+ }
497
+
498
+ public static void notifySeeked() {
499
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
500
+ if (plugin != null) {
501
+ plugin.notifyListeners("seeked", null);
502
+ }
503
+ }
504
+
505
+ public static void notifyTime(double position, double duration) {
506
+ JwPlayerPlugin plugin = staticPluginRef != null ? staticPluginRef.get() : null;
507
+ if (plugin != null) {
508
+ JSObject data = new JSObject();
509
+ data.put("position", position);
510
+ data.put("duration", duration);
511
+ plugin.notifyListeners("time", data);
512
+ }
513
+ }
514
+
515
+ // Helper to map Android state enum to iOS-like integers
516
+ private int mapPlayerStateToInt(com.jwplayer.pub.api.PlayerState state) {
517
+ switch (state) {
518
+ case IDLE: return 0;
519
+ case BUFFERING: return 1;
520
+ case PLAYING: return 2;
521
+ case PAUSED: return 3;
522
+ case COMPLETE: return 4;
523
+ default: return -1; // Unknown
524
+ }
525
+ }
526
+
527
+ // TODO: Implement other methods like loadPlaylist, getAudioTracks, setAudioTrack, getCaptions, setCaptions etc.
528
+ // These will require more complex communication with the PlayerActivity or direct player interaction.
529
+ }