@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 +3 -5
- package/dist/cjs/youtube/get-video-duration.d.ts +7 -0
- package/dist/cjs/youtube/get-video-duration.d.ts.map +1 -0
- package/dist/cjs/youtube/get-video-duration.js +150 -0
- package/dist/cjs/youtube/get-video-duration.js.map +1 -0
- package/dist/cjs/youtube/index.d.ts +1 -1
- package/dist/cjs/youtube/index.d.ts.map +1 -1
- package/dist/cjs/youtube/index.js +1 -1
- package/dist/cjs/youtube/index.js.map +1 -1
- package/dist/esm/youtube/get-video-duration.js +146 -0
- package/dist/esm/youtube/index.js +1 -1
- package/dist/youtube/get-video-duration.d.ts +7 -0
- package/dist/youtube/get-video-duration.d.ts.map +1 -0
- package/dist/youtube/index.d.ts +1 -1
- package/dist/youtube/index.d.ts.map +1 -1
- package/package.json +2 -3
- package/dist/cjs/youtube/use-get-video-duration.d.ts +0 -7
- package/dist/cjs/youtube/use-get-video-duration.d.ts.map +0 -1
- package/dist/cjs/youtube/use-get-video-duration.js +0 -154
- package/dist/cjs/youtube/use-get-video-duration.js.map +0 -1
- package/dist/esm/youtube/use-get-video-duration.js +0 -150
- package/dist/youtube/use-get-video-duration.d.ts +0 -7
- package/dist/youtube/use-get-video-duration.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -515,17 +515,15 @@ generateYoutubeURL({
|
|
|
515
515
|
- `origin`, `playlist`: Additional parameters
|
|
516
516
|
- `params`: Custom query parameters
|
|
517
517
|
|
|
518
|
-
#### `
|
|
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 {
|
|
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
|
|
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":";;AA4CA,0DAuGC;AAnJD,oEAAoE;AACpE,sCAA8C;AAC9C,yEAA6D;AAC7D,mEAA6D;AAE7D,IAAI,YAAY,GAAyB,IAAI,CAAA;AAE7C,SAAS,oBAAoB;;IAC3B,IAAI,MAAC,MAAc,CAAC,EAAE,0CAAE,MAAM;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IACxD,IAAI,YAAY;QAAE,OAAO,YAAY,CAAA;IAErC,YAAY,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CACrC,kDAAkD,CACnD,CAAA;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,8BAA8B;YAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;;gBAC5B,IAAI,MAAC,MAAc,CAAC,EAAE,0CAAE,MAAM,EAAE,CAAC;oBAC/B,aAAa,CAAC,IAAI,CAAC,CAAA;oBACnB,OAAO,EAAE,CAAA;gBACX,CAAC;YACH,CAAC,EAAE,EAAE,CAAC,CAAA;YACN,OAAM;QACR,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC5C,GAAG,CAAC,GAAG,GAAG,oCAAoC,CAAA;QAC9C,GAAG,CAAC,KAAK,GAAG,IAAI,CAAA;QAChB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAE7B;QAAC,MAAc,CAAC,uBAAuB,GAAG,GAAG,EAAE;YAC9C,OAAO,EAAE,CAAA;QACX,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,uBAAuB,CAC3C,QAAgB;IAEhB,MAAM,OAAO,GAAG,IAAA,2CAAgB,EAAC,QAAQ,CAAC,CAAA;IAC1C,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,MAAM,YAAY,GAAG,MAAM,IAAA,2CAAmB,EAAC,QAAQ,CAAC,CAAA;IACxD,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAA;IAE9B,IAAI,CAAC;QACH,MAAM,oBAAoB,EAAE,CAAA;IAC9B,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,MAAM,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;QAClD,MAAM,QAAQ,GAAG,eAAe,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;QACtF,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC/C,mDAAmD;QACnD,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAA;QACpB,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAA;QAC/B,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,SAAS,CAAA;QAC7B,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAA;QAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAA;QAC3B,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAA;QAC1B,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,MAAM,CAAA;QAEnC,2DAA2D;QAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAA;QACrC,MAAM,CAAC,GAAG,GAAG,iCAAiC,kBAAkB,CAAC,OAAO,CAAC,yBAAyB,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAA;QAE9H,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,IAAI,MAAM,GAAQ,IAAI,CAAA;QACtB,IAAI,cAAc,GAAkB,IAAI,CAAA;QAExC,SAAS,iBAAiB,CAAC,MAAqB;YAC9C,IAAI,QAAQ;gBAAE,OAAM;YACpB,QAAQ,GAAG,IAAI,CAAA;YACf,IAAI,CAAC;gBACH,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU;oBAAE,MAAM,CAAC,OAAO,EAAE,CAAA;YACtE,CAAC;YAAC,WAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,IAAI,CAAC;gBACH,IAAI,MAAM,CAAC,UAAU;oBAAE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAC9D,CAAC;YAAC,WAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,IAAI,cAAc;gBAAE,MAAM,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;YACvD,OAAO,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC;QAED,mBAAmB;QACnB,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;YACtC,iBAAiB,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC,EAAE,KAAK,CAAC,CAAA,CAAC,cAAc;QAExB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QAEjC,mBAAmB;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,IAAK,MAAc,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE;gBAC/C,MAAM,EAAE;oBACN,OAAO,EAAE,CAAC,CAAM,EAAE,EAAE;wBAClB,IAAI,CAAC;4BACH,IAAI,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;4BACrC,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gCAChC,+DAA+D;gCAC/D,IAAI,QAAQ,GAAG,CAAC,CAAA;gCAChB,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;oCACnC,QAAQ,IAAI,CAAC,CAAA;oCACb,IAAI,CAAC;wCACH,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;wCACjC,IAAI,QAAQ,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;4CAC7B,aAAa,CAAC,WAAW,CAAC,CAAA;4CAC1B,iBAAiB,CAAC,IAAA,2BAAkB,EAAC,QAAQ,CAAC,CAAC,CAAA;wCACjD,CAAC;6CAAM,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;4CACzB,aAAa,CAAC,WAAW,CAAC,CAAA;4CAC1B,iBAAiB,CAAC,IAAI,CAAC,CAAA;wCACzB,CAAC;oCACH,CAAC;oCAAC,WAAM,CAAC;wCACP,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;4CAClB,aAAa,CAAC,WAAW,CAAC,CAAA;4CAC1B,iBAAiB,CAAC,IAAI,CAAC,CAAA;wCACzB,CAAC;oCACH,CAAC;gCACH,CAAC,EAAE,GAAG,CAAC,CAAA;4BACT,CAAC;iCAAM,CAAC;gCACN,iBAAiB,CAAC,IAAA,2BAAkB,EAAC,QAAQ,CAAC,CAAC,CAAA;4BACjD,CAAC;wBACH,CAAC;wBAAC,WAAM,CAAC;4BACP,iBAAiB,CAAC,IAAI,CAAC,CAAA;wBACzB,CAAC;oBACH,CAAC;oBACD,OAAO,EAAE,GAAG,EAAE;wBACZ,iBAAiB,CAAC,IAAI,CAAC,CAAA;oBACzB,CAAC;iBACF;aACF,CAAC,CAAA;QACJ,CAAC;QAAC,WAAM,CAAC;YACP,iBAAiB,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -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,
|
|
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,
|
|
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"}
|
package/dist/youtube/index.d.ts
CHANGED
|
@@ -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,
|
|
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.
|
|
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":";;AA6CA,gEA4GC;AAzJD,oEAAoE;AACpE,iCAAmC;AACnC,sCAA8C;AAC9C,yEAA6D;AAC7D,mEAA6D;AAE7D,IAAI,YAAY,GAAyB,IAAI,CAAA;AAE7C,SAAS,oBAAoB;;IAC3B,IAAI,MAAC,MAAc,CAAC,EAAE,0CAAE,MAAM;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IACxD,IAAI,YAAY;QAAE,OAAO,YAAY,CAAA;IAErC,YAAY,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CACrC,kDAAkD,CACnD,CAAA;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,8BAA8B;YAC9B,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;;gBAC5B,IAAI,MAAC,MAAc,CAAC,EAAE,0CAAE,MAAM,EAAE,CAAC;oBAC/B,aAAa,CAAC,IAAI,CAAC,CAAA;oBACnB,OAAO,EAAE,CAAA;gBACX,CAAC;YACH,CAAC,EAAE,EAAE,CAAC,CAAA;YACN,OAAM;QACR,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC5C,GAAG,CAAC,GAAG,GAAG,oCAAoC,CAAA;QAC9C,GAAG,CAAC,KAAK,GAAG,IAAI,CAAA;QAChB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAE7B;QAAC,MAAc,CAAC,uBAAuB,GAAG,GAAG,EAAE;YAC9C,OAAO,EAAE,CAAA;QACX,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;;;GAIG;AACH,SAAgB,0BAA0B;IACxC,MAAM,uBAAuB,GAAG,IAAA,mBAAW,EACzC,KAAK,EAAE,QAAgB,EAA0B,EAAE;QACjD,MAAM,OAAO,GAAG,IAAA,2CAAgB,EAAC,QAAQ,CAAC,CAAA;QAC1C,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QAEzB,MAAM,YAAY,GAAG,MAAM,IAAA,2CAAmB,EAAC,QAAQ,CAAC,CAAA;QACxD,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAA;QAE9B,IAAI,CAAC;YACH,MAAM,oBAAoB,EAAE,CAAA;QAC9B,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,MAAM,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;YAClD,MAAM,QAAQ,GAAG,eAAe,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;YACtF,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;YAC/C,mDAAmD;YACnD,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAA;YACpB,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAA;YAC/B,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,SAAS,CAAA;YAC7B,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAA;YAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAA;YAC3B,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAA;YAC1B,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,MAAM,CAAA;YAEnC,2DAA2D;YAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAA;YACrC,MAAM,CAAC,GAAG,GAAG,iCAAiC,kBAAkB,CAAC,OAAO,CAAC,yBAAyB,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAA;YAE9H,IAAI,QAAQ,GAAG,KAAK,CAAA;YACpB,IAAI,MAAM,GAAQ,IAAI,CAAA;YACtB,IAAI,cAAc,GAAkB,IAAI,CAAA;YAExC,SAAS,iBAAiB,CAAC,MAAqB;gBAC9C,IAAI,QAAQ;oBAAE,OAAM;gBACpB,QAAQ,GAAG,IAAI,CAAA;gBACf,IAAI,CAAC;oBACH,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU;wBAAE,MAAM,CAAC,OAAO,EAAE,CAAA;gBACtE,CAAC;gBAAC,WAAM,CAAC;oBACP,YAAY;gBACd,CAAC;gBACD,IAAI,CAAC;oBACH,IAAI,MAAM,CAAC,UAAU;wBAAE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;gBAC9D,CAAC;gBAAC,WAAM,CAAC;oBACP,YAAY;gBACd,CAAC;gBACD,IAAI,cAAc;oBAAE,MAAM,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;gBACvD,OAAO,CAAC,MAAM,CAAC,CAAA;YACjB,CAAC;YAED,mBAAmB;YACnB,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;gBACtC,iBAAiB,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC,EAAE,KAAK,CAAC,CAAA,CAAC,cAAc;YAExB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAEjC,mBAAmB;YACnB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAK,MAAc,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE;oBAC/C,MAAM,EAAE;wBACN,OAAO,EAAE,CAAC,CAAM,EAAE,EAAE;4BAClB,IAAI,CAAC;gCACH,IAAI,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;gCACrC,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;oCAChC,+DAA+D;oCAC/D,IAAI,QAAQ,GAAG,CAAC,CAAA;oCAChB,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;wCACnC,QAAQ,IAAI,CAAC,CAAA;wCACb,IAAI,CAAC;4CACH,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;4CACjC,IAAI,QAAQ,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;gDAC7B,aAAa,CAAC,WAAW,CAAC,CAAA;gDAC1B,iBAAiB,CAAC,IAAA,2BAAkB,EAAC,QAAQ,CAAC,CAAC,CAAA;4CACjD,CAAC;iDAAM,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;gDACzB,aAAa,CAAC,WAAW,CAAC,CAAA;gDAC1B,iBAAiB,CAAC,IAAI,CAAC,CAAA;4CACzB,CAAC;wCACH,CAAC;wCAAC,WAAM,CAAC;4CACP,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;gDAClB,aAAa,CAAC,WAAW,CAAC,CAAA;gDAC1B,iBAAiB,CAAC,IAAI,CAAC,CAAA;4CACzB,CAAC;wCACH,CAAC;oCACH,CAAC,EAAE,GAAG,CAAC,CAAA;gCACT,CAAC;qCAAM,CAAC;oCACN,iBAAiB,CAAC,IAAA,2BAAkB,EAAC,QAAQ,CAAC,CAAC,CAAA;gCACjD,CAAC;4BACH,CAAC;4BAAC,WAAM,CAAC;gCACP,iBAAiB,CAAC,IAAI,CAAC,CAAA;4BACzB,CAAC;wBACH,CAAC;wBACD,OAAO,EAAE,GAAG,EAAE;4BACZ,iBAAiB,CAAC,IAAI,CAAC,CAAA;wBACzB,CAAC;qBACF;iBACF,CAAC,CAAA;YACJ,CAAC;YAAC,WAAM,CAAC;gBACP,iBAAiB,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,EACD,EAAE,CACH,CAAA;IAED,OAAO,uBAAuB,CAAA;AAChC,CAAC"}
|
|
@@ -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"}
|