@herowcode/utils 1.3.1 → 1.3.5

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/README.md CHANGED
@@ -515,17 +515,15 @@ generateYoutubeURL({
515
515
  - `origin`, `playlist`: Additional parameters
516
516
  - `params`: Custom query parameters
517
517
 
518
- #### `useGetYoutubeVideoDuration(): (videoUrl: string) => Promise<string | null>`
518
+ #### `getYoutubeVideoDuration(): (videoUrl: string) => Promise<string | null>`
519
519
  React hook that returns a function to get YouTube video duration using the YouTube IFrame API.
520
520
 
521
521
  ```typescript
522
- import { useGetYoutubeVideoDuration } from '@herowcode/utils/youtube';
522
+ import { getYoutubeVideoDuration } from '@herowcode/utils/youtube';
523
523
 
524
524
  function VideoComponent() {
525
- const getVideoDuration = useGetYoutubeVideoDuration();
526
-
527
525
  const handleGetDuration = async () => {
528
- const duration = await getVideoDuration('https://youtu.be/dQw4w9WgXcQ');
526
+ const duration = await getYoutubeVideoDuration('https://youtu.be/dQw4w9WgXcQ');
529
527
  console.log(duration); // "03:32" or null if failed
530
528
  };
531
529
 
@@ -0,0 +1,7 @@
1
+ /**
2
+ * getYoutubeVideoDuration
3
+ * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
4
+ * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
5
+ */
6
+ export declare function getYoutubeVideoDuration(videoUrl: string): Promise<string | null>;
7
+ //# sourceMappingURL=get-video-duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-video-duration.d.ts","sourceRoot":"","sources":["../../../src/youtube/get-video-duration.ts"],"names":[],"mappings":"AAuCA;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqGxB"}
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getYoutubeVideoDuration = getYoutubeVideoDuration;
4
+ /** biome-ignore-all lint/suspicious/noExplicitAny: Window is any */
5
+ const string_1 = require("../string");
6
+ const extract_youtube_video_id_1 = require("./extract-youtube-video-id");
7
+ const validate_youtube_link_1 = require("./validate-youtube-link");
8
+ let YtApiLoading = null;
9
+ function loadYouTubeIFrameAPI() {
10
+ var _a;
11
+ if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player)
12
+ return Promise.resolve();
13
+ if (YtApiLoading)
14
+ return YtApiLoading;
15
+ YtApiLoading = new Promise((resolve) => {
16
+ const existing = document.querySelector('script[src="https://www.youtube.com/iframe_api"]');
17
+ if (existing) {
18
+ // Poll until the API is ready
19
+ const poll = setInterval(() => {
20
+ var _a;
21
+ if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player) {
22
+ clearInterval(poll);
23
+ resolve();
24
+ }
25
+ }, 50);
26
+ return;
27
+ }
28
+ const tag = document.createElement("script");
29
+ tag.src = "https://www.youtube.com/iframe_api";
30
+ tag.async = true;
31
+ document.body.appendChild(tag);
32
+ window.onYouTubeIframeAPIReady = () => {
33
+ resolve();
34
+ };
35
+ });
36
+ return YtApiLoading;
37
+ }
38
+ /**
39
+ * getYoutubeVideoDuration
40
+ * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
41
+ * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
42
+ */
43
+ async function getYoutubeVideoDuration(videoUrl) {
44
+ const videoId = (0, extract_youtube_video_id_1.extractYouTubeId)(videoUrl);
45
+ if (!videoId)
46
+ return null;
47
+ const videoIsValid = await (0, validate_youtube_link_1.validateYoutubeLink)(videoUrl);
48
+ if (!videoIsValid)
49
+ return null;
50
+ try {
51
+ await loadYouTubeIFrameAPI();
52
+ }
53
+ catch (_a) {
54
+ return null;
55
+ }
56
+ return await new Promise((resolve) => {
57
+ const iframeId = `yt-duration-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
58
+ const iframe = document.createElement("iframe");
59
+ // create a minimal offscreen iframe for the player
60
+ iframe.id = iframeId;
61
+ iframe.style.position = "fixed";
62
+ iframe.style.left = "-9999px";
63
+ iframe.style.width = "1px";
64
+ iframe.style.height = "1px";
65
+ iframe.style.opacity = "0";
66
+ iframe.style.pointerEvents = "none";
67
+ // embed URL with enablejsapi so we can construct YT.Player
68
+ const origin = window.location.origin;
69
+ iframe.src = `https://www.youtube.com/embed/${encodeURIComponent(videoId)}?enablejsapi=1&origin=${encodeURIComponent(origin)}`;
70
+ let resolved = false;
71
+ let player = null;
72
+ let cleanupTimeout = null;
73
+ function cleanupAndResolve(result) {
74
+ if (resolved)
75
+ return;
76
+ resolved = true;
77
+ try {
78
+ if (player && typeof player.destroy === "function")
79
+ player.destroy();
80
+ }
81
+ catch (_a) {
82
+ /* ignore */
83
+ }
84
+ try {
85
+ if (iframe.parentNode)
86
+ iframe.parentNode.removeChild(iframe);
87
+ }
88
+ catch (_b) {
89
+ /* ignore */
90
+ }
91
+ if (cleanupTimeout)
92
+ window.clearTimeout(cleanupTimeout);
93
+ resolve(result);
94
+ }
95
+ // timeout fallback
96
+ cleanupTimeout = window.setTimeout(() => {
97
+ cleanupAndResolve(null);
98
+ }, 10000); // 10s timeout
99
+ document.body.appendChild(iframe);
100
+ // Construct player
101
+ try {
102
+ player = new window.YT.Player(iframeId, {
103
+ events: {
104
+ onReady: (e) => {
105
+ try {
106
+ let duration = e.target.getDuration();
107
+ if (!duration || duration === 0) {
108
+ // Sometimes duration is 0 immediately; try a few short retries
109
+ let attempts = 0;
110
+ const tryInterval = setInterval(() => {
111
+ attempts += 1;
112
+ try {
113
+ duration = e.target.getDuration();
114
+ if (duration && duration > 0) {
115
+ clearInterval(tryInterval);
116
+ cleanupAndResolve((0, string_1.formatSecondsToHMS)(duration));
117
+ }
118
+ else if (attempts >= 8) {
119
+ clearInterval(tryInterval);
120
+ cleanupAndResolve(null);
121
+ }
122
+ }
123
+ catch (_a) {
124
+ if (attempts >= 8) {
125
+ clearInterval(tryInterval);
126
+ cleanupAndResolve(null);
127
+ }
128
+ }
129
+ }, 300);
130
+ }
131
+ else {
132
+ cleanupAndResolve((0, string_1.formatSecondsToHMS)(duration));
133
+ }
134
+ }
135
+ catch (_a) {
136
+ cleanupAndResolve(null);
137
+ }
138
+ },
139
+ onError: () => {
140
+ cleanupAndResolve(null);
141
+ },
142
+ },
143
+ });
144
+ }
145
+ catch (_a) {
146
+ cleanupAndResolve(null);
147
+ }
148
+ });
149
+ }
150
+ //# sourceMappingURL=get-video-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-video-duration.js","sourceRoot":"","sources":["../../../src/youtube/get-video-duration.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  export * from "./extract-youtube-video-id";
2
2
  export * from "./generate-youtube-url";
3
+ export * from "./get-video-duration";
3
4
  export * from "./get-youtube-thumbnail";
4
5
  export * from "./get-youtube-video-info";
5
- export * from "./use-get-video-duration";
6
6
  export * from "./validate-youtube-link";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/youtube/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/youtube/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA"}
@@ -16,8 +16,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./extract-youtube-video-id"), exports);
18
18
  __exportStar(require("./generate-youtube-url"), exports);
19
+ __exportStar(require("./get-video-duration"), exports);
19
20
  __exportStar(require("./get-youtube-thumbnail"), exports);
20
21
  __exportStar(require("./get-youtube-video-info"), exports);
21
- __exportStar(require("./use-get-video-duration"), exports);
22
22
  __exportStar(require("./validate-youtube-link"), exports);
23
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/youtube/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA0C;AAC1C,yDAAsC;AACtC,0DAAuC;AACvC,2DAAwC;AACxC,2DAAwC;AACxC,0DAAuC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/youtube/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA0C;AAC1C,yDAAsC;AACtC,uDAAoC;AACpC,0DAAuC;AACvC,2DAAwC;AACxC,0DAAuC"}
@@ -0,0 +1,146 @@
1
+ /** biome-ignore-all lint/suspicious/noExplicitAny: Window is any */
2
+ import { formatSecondsToHMS } from "../string.js";
3
+ import { extractYouTubeId } from "./extract-youtube-video-id.js";
4
+ import { validateYoutubeLink } from "./validate-youtube-link.js";
5
+ let YtApiLoading = null;
6
+ function loadYouTubeIFrameAPI() {
7
+ var _a;
8
+ if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player)
9
+ return Promise.resolve();
10
+ if (YtApiLoading)
11
+ return YtApiLoading;
12
+ YtApiLoading = new Promise((resolve) => {
13
+ const existing = document.querySelector('script[src="https://www.youtube.com/iframe_api"]');
14
+ if (existing) {
15
+ // Poll until the API is ready
16
+ const poll = setInterval(() => {
17
+ var _a;
18
+ if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player) {
19
+ clearInterval(poll);
20
+ resolve();
21
+ }
22
+ }, 50);
23
+ return;
24
+ }
25
+ const tag = document.createElement("script");
26
+ tag.src = "https://www.youtube.com/iframe_api";
27
+ tag.async = true;
28
+ document.body.appendChild(tag);
29
+ window.onYouTubeIframeAPIReady = () => {
30
+ resolve();
31
+ };
32
+ });
33
+ return YtApiLoading;
34
+ }
35
+ /**
36
+ * getYoutubeVideoDuration
37
+ * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
38
+ * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
39
+ */
40
+ export async function getYoutubeVideoDuration(videoUrl) {
41
+ const videoId = extractYouTubeId(videoUrl);
42
+ if (!videoId)
43
+ return null;
44
+ const videoIsValid = await validateYoutubeLink(videoUrl);
45
+ if (!videoIsValid)
46
+ return null;
47
+ try {
48
+ await loadYouTubeIFrameAPI();
49
+ }
50
+ catch (_a) {
51
+ return null;
52
+ }
53
+ return await new Promise((resolve) => {
54
+ const iframeId = `yt-duration-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
55
+ const iframe = document.createElement("iframe");
56
+ // create a minimal offscreen iframe for the player
57
+ iframe.id = iframeId;
58
+ iframe.style.position = "fixed";
59
+ iframe.style.left = "-9999px";
60
+ iframe.style.width = "1px";
61
+ iframe.style.height = "1px";
62
+ iframe.style.opacity = "0";
63
+ iframe.style.pointerEvents = "none";
64
+ // embed URL with enablejsapi so we can construct YT.Player
65
+ const origin = window.location.origin;
66
+ iframe.src = `https://www.youtube.com/embed/${encodeURIComponent(videoId)}?enablejsapi=1&origin=${encodeURIComponent(origin)}`;
67
+ let resolved = false;
68
+ let player = null;
69
+ let cleanupTimeout = null;
70
+ function cleanupAndResolve(result) {
71
+ if (resolved)
72
+ return;
73
+ resolved = true;
74
+ try {
75
+ if (player && typeof player.destroy === "function")
76
+ player.destroy();
77
+ }
78
+ catch (_a) {
79
+ /* ignore */
80
+ }
81
+ try {
82
+ if (iframe.parentNode)
83
+ iframe.parentNode.removeChild(iframe);
84
+ }
85
+ catch (_b) {
86
+ /* ignore */
87
+ }
88
+ if (cleanupTimeout)
89
+ window.clearTimeout(cleanupTimeout);
90
+ resolve(result);
91
+ }
92
+ // timeout fallback
93
+ cleanupTimeout = window.setTimeout(() => {
94
+ cleanupAndResolve(null);
95
+ }, 10000); // 10s timeout
96
+ document.body.appendChild(iframe);
97
+ // Construct player
98
+ try {
99
+ player = new window.YT.Player(iframeId, {
100
+ events: {
101
+ onReady: (e) => {
102
+ try {
103
+ let duration = e.target.getDuration();
104
+ if (!duration || duration === 0) {
105
+ // Sometimes duration is 0 immediately; try a few short retries
106
+ let attempts = 0;
107
+ const tryInterval = setInterval(() => {
108
+ attempts += 1;
109
+ try {
110
+ duration = e.target.getDuration();
111
+ if (duration && duration > 0) {
112
+ clearInterval(tryInterval);
113
+ cleanupAndResolve(formatSecondsToHMS(duration));
114
+ }
115
+ else if (attempts >= 8) {
116
+ clearInterval(tryInterval);
117
+ cleanupAndResolve(null);
118
+ }
119
+ }
120
+ catch (_a) {
121
+ if (attempts >= 8) {
122
+ clearInterval(tryInterval);
123
+ cleanupAndResolve(null);
124
+ }
125
+ }
126
+ }, 300);
127
+ }
128
+ else {
129
+ cleanupAndResolve(formatSecondsToHMS(duration));
130
+ }
131
+ }
132
+ catch (_a) {
133
+ cleanupAndResolve(null);
134
+ }
135
+ },
136
+ onError: () => {
137
+ cleanupAndResolve(null);
138
+ },
139
+ },
140
+ });
141
+ }
142
+ catch (_a) {
143
+ cleanupAndResolve(null);
144
+ }
145
+ });
146
+ }
@@ -1,6 +1,6 @@
1
1
  export * from "./extract-youtube-video-id.js";
2
2
  export * from "./generate-youtube-url.js";
3
+ export * from "./get-video-duration.js";
3
4
  export * from "./get-youtube-thumbnail.js";
4
5
  export * from "./get-youtube-video-info.js";
5
- export * from "./use-get-video-duration.js";
6
6
  export * from "./validate-youtube-link.js";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * getYoutubeVideoDuration
3
+ * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
4
+ * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
5
+ */
6
+ export declare function getYoutubeVideoDuration(videoUrl: string): Promise<string | null>;
7
+ //# sourceMappingURL=get-video-duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-video-duration.d.ts","sourceRoot":"","sources":["../../src/youtube/get-video-duration.ts"],"names":[],"mappings":"AAuCA;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqGxB"}
@@ -1,7 +1,7 @@
1
1
  export * from "./extract-youtube-video-id";
2
2
  export * from "./generate-youtube-url";
3
+ export * from "./get-video-duration";
3
4
  export * from "./get-youtube-thumbnail";
4
5
  export * from "./get-youtube-video-info";
5
- export * from "./use-get-video-duration";
6
6
  export * from "./validate-youtube-link";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/youtube/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/youtube/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,sBAAsB,CAAA;AACpC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herowcode/utils",
3
- "version": "1.3.1",
3
+ "version": "1.3.5",
4
4
  "description": "A lightweight collection of utility functions for everyday JavaScript/TypeScript development",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -84,8 +84,7 @@
84
84
  "license": "MIT",
85
85
  "dependencies": {
86
86
  "axios": "1.12.2",
87
- "dayjs": "^1.11.10",
88
- "react": "19.1.1"
87
+ "dayjs": "^1.11.10"
89
88
  },
90
89
  "devDependencies": {
91
90
  "@biomejs/biome": "2.1.4",
@@ -1,7 +0,0 @@
1
- /**
2
- * useGetYoutubeVideoDuration
3
- * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
4
- * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
5
- */
6
- export declare function useGetYoutubeVideoDuration(): (videoUrl: string) => Promise<string | null>;
7
- //# sourceMappingURL=use-get-video-duration.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-get-video-duration.d.ts","sourceRoot":"","sources":["../../../src/youtube/use-get-video-duration.ts"],"names":[],"mappings":"AAwCA;;;;GAIG;AACH,wBAAgB,0BAA0B,eAErB,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0GnD"}
@@ -1,154 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useGetYoutubeVideoDuration = useGetYoutubeVideoDuration;
4
- /** biome-ignore-all lint/suspicious/noExplicitAny: Window is any */
5
- const react_1 = require("react");
6
- const string_1 = require("../string");
7
- const extract_youtube_video_id_1 = require("./extract-youtube-video-id");
8
- const validate_youtube_link_1 = require("./validate-youtube-link");
9
- let YtApiLoading = null;
10
- function loadYouTubeIFrameAPI() {
11
- var _a;
12
- if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player)
13
- return Promise.resolve();
14
- if (YtApiLoading)
15
- return YtApiLoading;
16
- YtApiLoading = new Promise((resolve) => {
17
- const existing = document.querySelector('script[src="https://www.youtube.com/iframe_api"]');
18
- if (existing) {
19
- // Poll until the API is ready
20
- const poll = setInterval(() => {
21
- var _a;
22
- if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player) {
23
- clearInterval(poll);
24
- resolve();
25
- }
26
- }, 50);
27
- return;
28
- }
29
- const tag = document.createElement("script");
30
- tag.src = "https://www.youtube.com/iframe_api";
31
- tag.async = true;
32
- document.body.appendChild(tag);
33
- window.onYouTubeIframeAPIReady = () => {
34
- resolve();
35
- };
36
- });
37
- return YtApiLoading;
38
- }
39
- /**
40
- * useGetYoutubeVideoDuration
41
- * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
42
- * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
43
- */
44
- function useGetYoutubeVideoDuration() {
45
- const getYoutubeVideoDuration = (0, react_1.useCallback)(async (videoUrl) => {
46
- const videoId = (0, extract_youtube_video_id_1.extractYouTubeId)(videoUrl);
47
- if (!videoId)
48
- return null;
49
- const videoIsValid = await (0, validate_youtube_link_1.validateYoutubeLink)(videoUrl);
50
- if (!videoIsValid)
51
- return null;
52
- try {
53
- await loadYouTubeIFrameAPI();
54
- }
55
- catch (_a) {
56
- return null;
57
- }
58
- return await new Promise((resolve) => {
59
- const iframeId = `yt-duration-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
60
- const iframe = document.createElement("iframe");
61
- // create a minimal offscreen iframe for the player
62
- iframe.id = iframeId;
63
- iframe.style.position = "fixed";
64
- iframe.style.left = "-9999px";
65
- iframe.style.width = "1px";
66
- iframe.style.height = "1px";
67
- iframe.style.opacity = "0";
68
- iframe.style.pointerEvents = "none";
69
- // embed URL with enablejsapi so we can construct YT.Player
70
- const origin = window.location.origin;
71
- iframe.src = `https://www.youtube.com/embed/${encodeURIComponent(videoId)}?enablejsapi=1&origin=${encodeURIComponent(origin)}`;
72
- let resolved = false;
73
- let player = null;
74
- let cleanupTimeout = null;
75
- function cleanupAndResolve(result) {
76
- if (resolved)
77
- return;
78
- resolved = true;
79
- try {
80
- if (player && typeof player.destroy === "function")
81
- player.destroy();
82
- }
83
- catch (_a) {
84
- /* ignore */
85
- }
86
- try {
87
- if (iframe.parentNode)
88
- iframe.parentNode.removeChild(iframe);
89
- }
90
- catch (_b) {
91
- /* ignore */
92
- }
93
- if (cleanupTimeout)
94
- window.clearTimeout(cleanupTimeout);
95
- resolve(result);
96
- }
97
- // timeout fallback
98
- cleanupTimeout = window.setTimeout(() => {
99
- cleanupAndResolve(null);
100
- }, 10000); // 10s timeout
101
- document.body.appendChild(iframe);
102
- // Construct player
103
- try {
104
- player = new window.YT.Player(iframeId, {
105
- events: {
106
- onReady: (e) => {
107
- try {
108
- let duration = e.target.getDuration();
109
- if (!duration || duration === 0) {
110
- // Sometimes duration is 0 immediately; try a few short retries
111
- let attempts = 0;
112
- const tryInterval = setInterval(() => {
113
- attempts += 1;
114
- try {
115
- duration = e.target.getDuration();
116
- if (duration && duration > 0) {
117
- clearInterval(tryInterval);
118
- cleanupAndResolve((0, string_1.formatSecondsToHMS)(duration));
119
- }
120
- else if (attempts >= 8) {
121
- clearInterval(tryInterval);
122
- cleanupAndResolve(null);
123
- }
124
- }
125
- catch (_a) {
126
- if (attempts >= 8) {
127
- clearInterval(tryInterval);
128
- cleanupAndResolve(null);
129
- }
130
- }
131
- }, 300);
132
- }
133
- else {
134
- cleanupAndResolve((0, string_1.formatSecondsToHMS)(duration));
135
- }
136
- }
137
- catch (_a) {
138
- cleanupAndResolve(null);
139
- }
140
- },
141
- onError: () => {
142
- cleanupAndResolve(null);
143
- },
144
- },
145
- });
146
- }
147
- catch (_a) {
148
- cleanupAndResolve(null);
149
- }
150
- });
151
- }, []);
152
- return getYoutubeVideoDuration;
153
- }
154
- //# sourceMappingURL=use-get-video-duration.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-get-video-duration.js","sourceRoot":"","sources":["../../../src/youtube/use-get-video-duration.ts"],"names":[],"mappings":"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"}
@@ -1,150 +0,0 @@
1
- /** biome-ignore-all lint/suspicious/noExplicitAny: Window is any */
2
- import { useCallback } from "react";
3
- import { formatSecondsToHMS } from "../string.js";
4
- import { extractYouTubeId } from "./extract-youtube-video-id.js";
5
- import { validateYoutubeLink } from "./validate-youtube-link.js";
6
- let YtApiLoading = null;
7
- function loadYouTubeIFrameAPI() {
8
- var _a;
9
- if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player)
10
- return Promise.resolve();
11
- if (YtApiLoading)
12
- return YtApiLoading;
13
- YtApiLoading = new Promise((resolve) => {
14
- const existing = document.querySelector('script[src="https://www.youtube.com/iframe_api"]');
15
- if (existing) {
16
- // Poll until the API is ready
17
- const poll = setInterval(() => {
18
- var _a;
19
- if ((_a = window.YT) === null || _a === void 0 ? void 0 : _a.Player) {
20
- clearInterval(poll);
21
- resolve();
22
- }
23
- }, 50);
24
- return;
25
- }
26
- const tag = document.createElement("script");
27
- tag.src = "https://www.youtube.com/iframe_api";
28
- tag.async = true;
29
- document.body.appendChild(tag);
30
- window.onYouTubeIframeAPIReady = () => {
31
- resolve();
32
- };
33
- });
34
- return YtApiLoading;
35
- }
36
- /**
37
- * useGetYoutubeVideoDuration
38
- * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
39
- * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
40
- */
41
- export function useGetYoutubeVideoDuration() {
42
- const getYoutubeVideoDuration = useCallback(async (videoUrl) => {
43
- const videoId = extractYouTubeId(videoUrl);
44
- if (!videoId)
45
- return null;
46
- const videoIsValid = await validateYoutubeLink(videoUrl);
47
- if (!videoIsValid)
48
- return null;
49
- try {
50
- await loadYouTubeIFrameAPI();
51
- }
52
- catch (_a) {
53
- return null;
54
- }
55
- return await new Promise((resolve) => {
56
- const iframeId = `yt-duration-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
57
- const iframe = document.createElement("iframe");
58
- // create a minimal offscreen iframe for the player
59
- iframe.id = iframeId;
60
- iframe.style.position = "fixed";
61
- iframe.style.left = "-9999px";
62
- iframe.style.width = "1px";
63
- iframe.style.height = "1px";
64
- iframe.style.opacity = "0";
65
- iframe.style.pointerEvents = "none";
66
- // embed URL with enablejsapi so we can construct YT.Player
67
- const origin = window.location.origin;
68
- iframe.src = `https://www.youtube.com/embed/${encodeURIComponent(videoId)}?enablejsapi=1&origin=${encodeURIComponent(origin)}`;
69
- let resolved = false;
70
- let player = null;
71
- let cleanupTimeout = null;
72
- function cleanupAndResolve(result) {
73
- if (resolved)
74
- return;
75
- resolved = true;
76
- try {
77
- if (player && typeof player.destroy === "function")
78
- player.destroy();
79
- }
80
- catch (_a) {
81
- /* ignore */
82
- }
83
- try {
84
- if (iframe.parentNode)
85
- iframe.parentNode.removeChild(iframe);
86
- }
87
- catch (_b) {
88
- /* ignore */
89
- }
90
- if (cleanupTimeout)
91
- window.clearTimeout(cleanupTimeout);
92
- resolve(result);
93
- }
94
- // timeout fallback
95
- cleanupTimeout = window.setTimeout(() => {
96
- cleanupAndResolve(null);
97
- }, 10000); // 10s timeout
98
- document.body.appendChild(iframe);
99
- // Construct player
100
- try {
101
- player = new window.YT.Player(iframeId, {
102
- events: {
103
- onReady: (e) => {
104
- try {
105
- let duration = e.target.getDuration();
106
- if (!duration || duration === 0) {
107
- // Sometimes duration is 0 immediately; try a few short retries
108
- let attempts = 0;
109
- const tryInterval = setInterval(() => {
110
- attempts += 1;
111
- try {
112
- duration = e.target.getDuration();
113
- if (duration && duration > 0) {
114
- clearInterval(tryInterval);
115
- cleanupAndResolve(formatSecondsToHMS(duration));
116
- }
117
- else if (attempts >= 8) {
118
- clearInterval(tryInterval);
119
- cleanupAndResolve(null);
120
- }
121
- }
122
- catch (_a) {
123
- if (attempts >= 8) {
124
- clearInterval(tryInterval);
125
- cleanupAndResolve(null);
126
- }
127
- }
128
- }, 300);
129
- }
130
- else {
131
- cleanupAndResolve(formatSecondsToHMS(duration));
132
- }
133
- }
134
- catch (_a) {
135
- cleanupAndResolve(null);
136
- }
137
- },
138
- onError: () => {
139
- cleanupAndResolve(null);
140
- },
141
- },
142
- });
143
- }
144
- catch (_a) {
145
- cleanupAndResolve(null);
146
- }
147
- });
148
- }, []);
149
- return getYoutubeVideoDuration;
150
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * useGetYoutubeVideoDuration
3
- * Returns a function that accepts a YouTube URL (full or short) and returns a Promise
4
- * resolving to the video duration formatted as "HH:MM:SS" or null on failure.
5
- */
6
- export declare function useGetYoutubeVideoDuration(): (videoUrl: string) => Promise<string | null>;
7
- //# sourceMappingURL=use-get-video-duration.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-get-video-duration.d.ts","sourceRoot":"","sources":["../../src/youtube/use-get-video-duration.ts"],"names":[],"mappings":"AAwCA;;;;GAIG;AACH,wBAAgB,0BAA0B,eAErB,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0GnD"}