@scarlett-player/native 1.0.1 → 1.1.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.
package/dist/index.cjs CHANGED
@@ -48,9 +48,11 @@ var MIME_TYPES = {
48
48
  };
49
49
  function createNativePlugin(config) {
50
50
  const preload = config?.preload ?? "metadata";
51
+ const load_timeout_ms = config?.loadTimeoutMs ?? 3e4;
51
52
  let api = null;
52
53
  let video = null;
53
54
  let cleanupEvents = null;
55
+ let derived_title = null;
54
56
  const getExtension = (src) => {
55
57
  try {
56
58
  const url = new URL(src, window.location.href);
@@ -279,6 +281,7 @@ function createNativePlugin(config) {
279
281
  }
280
282
  video = null;
281
283
  api = null;
284
+ derived_title = null;
282
285
  },
283
286
  async loadSource(src) {
284
287
  if (!api) throw new Error("Plugin not initialized");
@@ -291,13 +294,18 @@ function createNativePlugin(config) {
291
294
  api.setState("buffering", true);
292
295
  api.setState("mediaType", isAudio ? "audio" : "video");
293
296
  if (isAudio) {
294
- try {
295
- const url = new URL(src, window.location.href);
296
- const filename = url.pathname.split("/").pop() || "Audio";
297
- const title = decodeURIComponent(filename.replace(/\.[^.]+$/, "").replace(/[-_]/g, " "));
298
- api.setState("title", title);
299
- } catch {
300
- api.setState("title", "Audio");
297
+ const current_title = api.getState("title");
298
+ if (!current_title || current_title === derived_title) {
299
+ try {
300
+ const url = new URL(src, window.location.href);
301
+ const filename = url.pathname.split("/").pop() || "Audio";
302
+ const title = decodeURIComponent(filename.replace(/\.[^.]+$/, "").replace(/[-_]/g, " "));
303
+ derived_title = title;
304
+ api.setState("title", title);
305
+ } catch {
306
+ derived_title = "Audio";
307
+ api.setState("title", "Audio");
308
+ }
301
309
  }
302
310
  }
303
311
  api.setState("qualities", []);
@@ -315,9 +323,17 @@ function createNativePlugin(config) {
315
323
  }
316
324
  cleanupEvents = setupEventListeners(videoEl);
317
325
  return new Promise((resolve, reject) => {
318
- const onLoaded = () => {
326
+ let watchdog = null;
327
+ const settle = () => {
319
328
  videoEl.removeEventListener("loadedmetadata", onLoaded);
320
329
  videoEl.removeEventListener("error", onError);
330
+ if (watchdog !== null) {
331
+ clearTimeout(watchdog);
332
+ watchdog = null;
333
+ }
334
+ };
335
+ const onLoaded = () => {
336
+ settle();
321
337
  const muted = api?.getState("muted");
322
338
  const volume = api?.getState("volume");
323
339
  if (muted !== void 0) videoEl.muted = muted;
@@ -329,11 +345,16 @@ function createNativePlugin(config) {
329
345
  resolve();
330
346
  };
331
347
  const onError = () => {
332
- videoEl.removeEventListener("loadedmetadata", onLoaded);
333
- videoEl.removeEventListener("error", onError);
348
+ settle();
334
349
  const error = videoEl.error;
335
350
  reject(new Error(error?.message || "Failed to load video source"));
336
351
  };
352
+ if (load_timeout_ms > 0) {
353
+ watchdog = setTimeout(() => {
354
+ settle();
355
+ reject(new Error("Video took too long to load (network timeout)"));
356
+ }, load_timeout_ms);
357
+ }
337
358
  videoEl.addEventListener("loadedmetadata", onLoaded);
338
359
  videoEl.addEventListener("error", onError);
339
360
  videoEl.src = src;
package/dist/index.d.cts CHANGED
@@ -24,6 +24,12 @@ import { PluginType, IPluginAPI } from '@scarlett-player/core';
24
24
  interface NativePluginConfig {
25
25
  /** Preload behavior: 'none' | 'metadata' | 'auto' */
26
26
  preload?: 'none' | 'metadata' | 'auto';
27
+ /**
28
+ * Watchdog for source loading in milliseconds (default: 30000, 0 disables).
29
+ * Guarantees a load attempt terminates with an error instead of leaving
30
+ * the viewer on a spinner when the network stalls without erroring.
31
+ */
32
+ loadTimeoutMs?: number;
27
33
  }
28
34
  interface INativePlugin {
29
35
  id: string;
package/dist/index.d.ts CHANGED
@@ -24,6 +24,12 @@ import { PluginType, IPluginAPI } from '@scarlett-player/core';
24
24
  interface NativePluginConfig {
25
25
  /** Preload behavior: 'none' | 'metadata' | 'auto' */
26
26
  preload?: 'none' | 'metadata' | 'auto';
27
+ /**
28
+ * Watchdog for source loading in milliseconds (default: 30000, 0 disables).
29
+ * Guarantees a load attempt terminates with an error instead of leaving
30
+ * the viewer on a spinner when the network stalls without erroring.
31
+ */
32
+ loadTimeoutMs?: number;
27
33
  }
28
34
  interface INativePlugin {
29
35
  id: string;
package/dist/index.js CHANGED
@@ -23,9 +23,11 @@ var MIME_TYPES = {
23
23
  };
24
24
  function createNativePlugin(config) {
25
25
  const preload = config?.preload ?? "metadata";
26
+ const load_timeout_ms = config?.loadTimeoutMs ?? 3e4;
26
27
  let api = null;
27
28
  let video = null;
28
29
  let cleanupEvents = null;
30
+ let derived_title = null;
29
31
  const getExtension = (src) => {
30
32
  try {
31
33
  const url = new URL(src, window.location.href);
@@ -254,6 +256,7 @@ function createNativePlugin(config) {
254
256
  }
255
257
  video = null;
256
258
  api = null;
259
+ derived_title = null;
257
260
  },
258
261
  async loadSource(src) {
259
262
  if (!api) throw new Error("Plugin not initialized");
@@ -266,13 +269,18 @@ function createNativePlugin(config) {
266
269
  api.setState("buffering", true);
267
270
  api.setState("mediaType", isAudio ? "audio" : "video");
268
271
  if (isAudio) {
269
- try {
270
- const url = new URL(src, window.location.href);
271
- const filename = url.pathname.split("/").pop() || "Audio";
272
- const title = decodeURIComponent(filename.replace(/\.[^.]+$/, "").replace(/[-_]/g, " "));
273
- api.setState("title", title);
274
- } catch {
275
- api.setState("title", "Audio");
272
+ const current_title = api.getState("title");
273
+ if (!current_title || current_title === derived_title) {
274
+ try {
275
+ const url = new URL(src, window.location.href);
276
+ const filename = url.pathname.split("/").pop() || "Audio";
277
+ const title = decodeURIComponent(filename.replace(/\.[^.]+$/, "").replace(/[-_]/g, " "));
278
+ derived_title = title;
279
+ api.setState("title", title);
280
+ } catch {
281
+ derived_title = "Audio";
282
+ api.setState("title", "Audio");
283
+ }
276
284
  }
277
285
  }
278
286
  api.setState("qualities", []);
@@ -290,9 +298,17 @@ function createNativePlugin(config) {
290
298
  }
291
299
  cleanupEvents = setupEventListeners(videoEl);
292
300
  return new Promise((resolve, reject) => {
293
- const onLoaded = () => {
301
+ let watchdog = null;
302
+ const settle = () => {
294
303
  videoEl.removeEventListener("loadedmetadata", onLoaded);
295
304
  videoEl.removeEventListener("error", onError);
305
+ if (watchdog !== null) {
306
+ clearTimeout(watchdog);
307
+ watchdog = null;
308
+ }
309
+ };
310
+ const onLoaded = () => {
311
+ settle();
296
312
  const muted = api?.getState("muted");
297
313
  const volume = api?.getState("volume");
298
314
  if (muted !== void 0) videoEl.muted = muted;
@@ -304,11 +320,16 @@ function createNativePlugin(config) {
304
320
  resolve();
305
321
  };
306
322
  const onError = () => {
307
- videoEl.removeEventListener("loadedmetadata", onLoaded);
308
- videoEl.removeEventListener("error", onError);
323
+ settle();
309
324
  const error = videoEl.error;
310
325
  reject(new Error(error?.message || "Failed to load video source"));
311
326
  };
327
+ if (load_timeout_ms > 0) {
328
+ watchdog = setTimeout(() => {
329
+ settle();
330
+ reject(new Error("Video took too long to load (network timeout)"));
331
+ }, load_timeout_ms);
332
+ }
312
333
  videoEl.addEventListener("loadedmetadata", onLoaded);
313
334
  videoEl.addEventListener("error", onError);
314
335
  videoEl.src = src;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scarlett-player/native",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "Native Video Provider Plugin for Scarlett Player (MP4, WebM, MOV, MKV)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -17,13 +17,13 @@
17
17
  "dist"
18
18
  ],
19
19
  "peerDependencies": {
20
- "@scarlett-player/core": "^1.0.1"
20
+ "@scarlett-player/core": "^1.0.3"
21
21
  },
22
22
  "devDependencies": {
23
23
  "typescript": "^5.3.0",
24
24
  "tsup": "^8.0.0",
25
25
  "vitest": "^1.6.0",
26
- "@scarlett-player/core": "1.0.1"
26
+ "@scarlett-player/core": "1.1.1"
27
27
  },
28
28
  "keywords": [
29
29
  "video",