@growth-labs/video 0.1.0
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/access-registry.d.ts +16 -0
- package/dist/access-registry.d.ts.map +1 -0
- package/dist/access-registry.js +15 -0
- package/dist/access-registry.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/options.d.ts +136 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +53 -0
- package/dist/options.js.map +1 -0
- package/dist/routes/playback.d.ts +3 -0
- package/dist/routes/playback.d.ts.map +1 -0
- package/dist/routes/playback.js +83 -0
- package/dist/routes/playback.js.map +1 -0
- package/dist/routes/session.d.ts +3 -0
- package/dist/routes/session.d.ts.map +1 -0
- package/dist/routes/session.js +69 -0
- package/dist/routes/session.js.map +1 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/captions.d.ts +7 -0
- package/dist/utils/captions.d.ts.map +1 -0
- package/dist/utils/captions.js +66 -0
- package/dist/utils/captions.js.map +1 -0
- package/dist/utils/chapters.d.ts +7 -0
- package/dist/utils/chapters.d.ts.map +1 -0
- package/dist/utils/chapters.js +40 -0
- package/dist/utils/chapters.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/jsonld.d.ts +3 -0
- package/dist/utils/jsonld.d.ts.map +1 -0
- package/dist/utils/jsonld.js +42 -0
- package/dist/utils/jsonld.js.map +1 -0
- package/dist/utils/keys.d.ts +6 -0
- package/dist/utils/keys.d.ts.map +1 -0
- package/dist/utils/keys.js +16 -0
- package/dist/utils/keys.js.map +1 -0
- package/dist/utils/session.d.ts +10 -0
- package/dist/utils/session.d.ts.map +1 -0
- package/dist/utils/session.js +70 -0
- package/dist/utils/session.js.map +1 -0
- package/dist/utils/url.d.ts +7 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/url.js +25 -0
- package/dist/utils/url.js.map +1 -0
- package/dist/utils/youtube.d.ts +12 -0
- package/dist/utils/youtube.d.ts.map +1 -0
- package/dist/utils/youtube.js +21 -0
- package/dist/utils/youtube.js.map +1 -0
- package/dist/vite-plugin.d.ts +4 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +18 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +65 -0
- package/src/components/Video.astro +164 -0
- package/src/components/VideoEmbed.astro +109 -0
- package/src/components/VideoGallery.astro +36 -0
- package/src/components/VideoPreview.astro +70 -0
- package/src/components/index.ts +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chapters.d.ts","sourceRoot":"","sources":["../../src/utils/chapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,aAAa,CAAA;AAExD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAwBnE;AAED,MAAM,WAAW,mBAAmB;IACnC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CACtB;AAED,wBAAsB,YAAY,CACjC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,mBAAwB,GAC5B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAU9B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export function parseChaptersJson(raw) {
|
|
2
|
+
if (!raw || typeof raw !== 'object')
|
|
3
|
+
return null;
|
|
4
|
+
const obj = raw;
|
|
5
|
+
if (!Array.isArray(obj.chapters))
|
|
6
|
+
return null;
|
|
7
|
+
const out = [];
|
|
8
|
+
for (const item of obj.chapters) {
|
|
9
|
+
if (!item || typeof item !== 'object')
|
|
10
|
+
return null;
|
|
11
|
+
const ch = item;
|
|
12
|
+
if (typeof ch.index !== 'number' ||
|
|
13
|
+
typeof ch.title !== 'string' ||
|
|
14
|
+
typeof ch.startSeconds !== 'number' ||
|
|
15
|
+
typeof ch.endSeconds !== 'number') {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
out.push({
|
|
19
|
+
index: ch.index,
|
|
20
|
+
title: ch.title,
|
|
21
|
+
startSeconds: ch.startSeconds,
|
|
22
|
+
endSeconds: ch.endSeconds,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return { chapters: out };
|
|
26
|
+
}
|
|
27
|
+
export async function loadChapters(url, opts = {}) {
|
|
28
|
+
const fetcher = opts.fetcher ?? fetch;
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetcher(url);
|
|
31
|
+
if (!res.ok)
|
|
32
|
+
return null;
|
|
33
|
+
const raw = (await res.json());
|
|
34
|
+
return parseChaptersJson(raw);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=chapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chapters.js","sourceRoot":"","sources":["../../src/utils/chapters.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,iBAAiB,CAAC,GAAY;IAC7C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAChD,MAAM,GAAG,GAAG,GAA8B,CAAA;IAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAA;IAC7C,MAAM,GAAG,GAAc,EAAE,CAAA;IACzB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QAClD,MAAM,EAAE,GAAG,IAA+B,CAAA;QAC1C,IACC,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ;YAC5B,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ;YAC5B,OAAO,EAAE,CAAC,YAAY,KAAK,QAAQ;YACnC,OAAO,EAAE,CAAC,UAAU,KAAK,QAAQ,EAChC,CAAC;YACF,OAAO,IAAI,CAAA;QACZ,CAAC;QACD,GAAG,CAAC,IAAI,CAAC;YACR,KAAK,EAAE,EAAE,CAAC,KAAK;YACf,KAAK,EAAE,EAAE,CAAC,KAAK;YACf,YAAY,EAAE,EAAE,CAAC,YAAY;YAC7B,UAAU,EAAE,EAAE,CAAC,UAAU;SACzB,CAAC,CAAA;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAA;AACzB,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,GAAW,EACX,OAA4B,EAAE;IAE9B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAA;IACrC,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QACxB,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAY,CAAA;QACzC,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAA;IACZ,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonld.d.ts","sourceRoot":"","sources":["../../src/utils/jsonld.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAenD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CA0BzE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { posterUrl } from './url.js';
|
|
2
|
+
function toIsoDuration(seconds) {
|
|
3
|
+
const h = Math.floor(seconds / 3600);
|
|
4
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
5
|
+
const s = seconds % 60;
|
|
6
|
+
let out = 'PT';
|
|
7
|
+
if (h)
|
|
8
|
+
out += `${h}H`;
|
|
9
|
+
if (m)
|
|
10
|
+
out += `${m}M`;
|
|
11
|
+
if (s)
|
|
12
|
+
out += `${s}S`;
|
|
13
|
+
if (out === 'PT')
|
|
14
|
+
out = 'PT0S';
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
export function generateVideoObjectJsonLd(input) {
|
|
18
|
+
const thumbnailUrl = input.thumbnailUrls ?? [posterUrl(input.publicDomain, input.videoId)];
|
|
19
|
+
const ld = {
|
|
20
|
+
'@context': 'https://schema.org',
|
|
21
|
+
'@type': 'VideoObject',
|
|
22
|
+
name: input.title,
|
|
23
|
+
description: input.description,
|
|
24
|
+
uploadDate: input.uploadDate,
|
|
25
|
+
duration: toIsoDuration(input.durationSeconds),
|
|
26
|
+
thumbnailUrl,
|
|
27
|
+
embedUrl: input.embedUrl,
|
|
28
|
+
};
|
|
29
|
+
if (input.contentUrl)
|
|
30
|
+
ld.contentUrl = input.contentUrl;
|
|
31
|
+
if (input.transcript)
|
|
32
|
+
ld.transcript = input.transcript;
|
|
33
|
+
if (input.chapters && input.chapters.length > 0) {
|
|
34
|
+
ld.potentialAction = {
|
|
35
|
+
'@type': 'SeekToAction',
|
|
36
|
+
target: `${input.embedUrl}#t={seek_to_second_number}`,
|
|
37
|
+
'startOffset-input': 'required name=seek_to_second_number',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return ld;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=jsonld.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonld.js","sourceRoot":"","sources":["../../src/utils/jsonld.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,SAAS,aAAa,CAAC,OAAe;IACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACpC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC3C,MAAM,CAAC,GAAG,OAAO,GAAG,EAAE,CAAA;IACtB,IAAI,GAAG,GAAG,IAAI,CAAA;IACd,IAAI,CAAC;QAAE,GAAG,IAAI,GAAG,CAAC,GAAG,CAAA;IACrB,IAAI,CAAC;QAAE,GAAG,IAAI,GAAG,CAAC,GAAG,CAAA;IACrB,IAAI,CAAC;QAAE,GAAG,IAAI,GAAG,CAAC,GAAG,CAAA;IACrB,IAAI,GAAG,KAAK,IAAI;QAAE,GAAG,GAAG,MAAM,CAAA;IAC9B,OAAO,GAAG,CAAA;AACX,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAuB;IAChE,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA;IAE1F,MAAM,EAAE,GAA4B;QACnC,UAAU,EAAE,oBAAoB;QAChC,OAAO,EAAE,aAAa;QACtB,IAAI,EAAE,KAAK,CAAC,KAAK;QACjB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC;QAC9C,YAAY;QACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACxB,CAAA;IAED,IAAI,KAAK,CAAC,UAAU;QAAE,EAAE,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;IACtD,IAAI,KAAK,CAAC,UAAU;QAAE,EAAE,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAA;IAEtD,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,EAAE,CAAC,eAAe,GAAG;YACpB,OAAO,EAAE,cAAc;YACvB,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,4BAA4B;YACrD,mBAAmB,EAAE,qCAAqC;SAC1D,CAAA;IACF,CAAC;IAED,OAAO,EAAE,CAAA;AACV,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function videoManifestKey(videoId: string): string;
|
|
2
|
+
export declare function posterKey(videoId: string): string;
|
|
3
|
+
export declare function captionsKey(videoId: string, lang?: string): string;
|
|
4
|
+
export declare function chaptersKey(videoId: string): string;
|
|
5
|
+
export declare function hlsBundlePrefix(videoId: string): string;
|
|
6
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/utils/keys.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAa,GAAG,MAAM,CAExE;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function videoManifestKey(videoId) {
|
|
2
|
+
return `video/${videoId}/hls/master.m3u8`;
|
|
3
|
+
}
|
|
4
|
+
export function posterKey(videoId) {
|
|
5
|
+
return `video/${videoId}/poster.jpg`;
|
|
6
|
+
}
|
|
7
|
+
export function captionsKey(videoId, lang = 'en') {
|
|
8
|
+
return lang === 'en' ? `video/${videoId}/captions.vtt` : `video/${videoId}/captions-${lang}.vtt`;
|
|
9
|
+
}
|
|
10
|
+
export function chaptersKey(videoId) {
|
|
11
|
+
return `video/${videoId}/chapters.json`;
|
|
12
|
+
}
|
|
13
|
+
export function hlsBundlePrefix(videoId) {
|
|
14
|
+
return `video/${videoId}/hls/`;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/utils/keys.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,OAAO,SAAS,OAAO,kBAAkB,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe;IACxC,OAAO,SAAS,OAAO,aAAa,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,OAAe,IAAI;IAC/D,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,OAAO,eAAe,CAAC,CAAC,CAAC,SAAS,OAAO,aAAa,IAAI,MAAM,CAAA;AACjG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IAC1C,OAAO,SAAS,OAAO,gBAAgB,CAAA;AACxC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC9C,OAAO,SAAS,OAAO,OAAO,CAAA;AAC/B,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface SessionPayload {
|
|
2
|
+
videoIds: string[] | '*';
|
|
3
|
+
subjectId: string;
|
|
4
|
+
issuedAt: number;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
nonce: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function issueSession(payload: Omit<SessionPayload, 'nonce'>, secret: string): Promise<string>;
|
|
9
|
+
export declare function verifySession(cookie: string, secret: string, requestedVideoId: string): Promise<SessionPayload | null>;
|
|
10
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAG,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;CACb;AAiDD,wBAAsB,YAAY,CACjC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,EACtC,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAsB,aAAa,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GACtB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA0BhC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const encoder = new TextEncoder();
|
|
2
|
+
const decoder = new TextDecoder();
|
|
3
|
+
function toBase64Url(bytes) {
|
|
4
|
+
let bin = '';
|
|
5
|
+
for (let i = 0; i < bytes.length; i++)
|
|
6
|
+
bin += String.fromCharCode(bytes[i]);
|
|
7
|
+
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
8
|
+
}
|
|
9
|
+
function fromBase64Url(str) {
|
|
10
|
+
const padded = str.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (str.length % 4)) % 4);
|
|
11
|
+
const bin = atob(padded);
|
|
12
|
+
const bytes = new Uint8Array(bin.length);
|
|
13
|
+
for (let i = 0; i < bin.length; i++)
|
|
14
|
+
bytes[i] = bin.charCodeAt(i);
|
|
15
|
+
return bytes;
|
|
16
|
+
}
|
|
17
|
+
async function importKey(secret) {
|
|
18
|
+
return crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']);
|
|
19
|
+
}
|
|
20
|
+
async function sign(secret, data) {
|
|
21
|
+
const key = await importKey(secret);
|
|
22
|
+
const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(data));
|
|
23
|
+
return toBase64Url(new Uint8Array(sig));
|
|
24
|
+
}
|
|
25
|
+
async function verify(secret, data, signatureB64) {
|
|
26
|
+
const key = await importKey(secret);
|
|
27
|
+
const sig = fromBase64Url(signatureB64);
|
|
28
|
+
return crypto.subtle.verify('HMAC', key, sig.buffer, encoder.encode(data));
|
|
29
|
+
}
|
|
30
|
+
function randomNonce() {
|
|
31
|
+
const bytes = new Uint8Array(16);
|
|
32
|
+
crypto.getRandomValues(bytes);
|
|
33
|
+
let hex = '';
|
|
34
|
+
for (let i = 0; i < bytes.length; i++)
|
|
35
|
+
hex += bytes[i].toString(16).padStart(2, '0');
|
|
36
|
+
return hex;
|
|
37
|
+
}
|
|
38
|
+
export async function issueSession(payload, secret) {
|
|
39
|
+
const full = { ...payload, nonce: randomNonce() };
|
|
40
|
+
const payloadB64 = toBase64Url(encoder.encode(JSON.stringify(full)));
|
|
41
|
+
const sig = await sign(secret, payloadB64);
|
|
42
|
+
return `${payloadB64}.${sig}`;
|
|
43
|
+
}
|
|
44
|
+
export async function verifySession(cookie, secret, requestedVideoId) {
|
|
45
|
+
const dot = cookie.indexOf('.');
|
|
46
|
+
if (dot === -1)
|
|
47
|
+
return null;
|
|
48
|
+
const payloadB64 = cookie.slice(0, dot);
|
|
49
|
+
const sig = cookie.slice(dot + 1);
|
|
50
|
+
if (!payloadB64 || !sig)
|
|
51
|
+
return null;
|
|
52
|
+
if (!(await verify(secret, payloadB64, sig)))
|
|
53
|
+
return null;
|
|
54
|
+
let payload;
|
|
55
|
+
try {
|
|
56
|
+
payload = JSON.parse(decoder.decode(fromBase64Url(payloadB64)));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const now = Math.floor(Date.now() / 1000);
|
|
62
|
+
if (payload.expiresAt <= now)
|
|
63
|
+
return null;
|
|
64
|
+
if (payload.videoIds !== '*' &&
|
|
65
|
+
(!Array.isArray(payload.videoIds) || !payload.videoIds.includes(requestedVideoId))) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return payload;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/utils/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function manifestUrl(publicDomain: string, videoId: string): string;
|
|
2
|
+
export declare function posterUrl(publicDomain: string, videoId: string): string;
|
|
3
|
+
export declare function captionsUrl(publicDomain: string, videoId: string, lang?: string): string;
|
|
4
|
+
export declare function chaptersUrl(publicDomain: string, videoId: string): string;
|
|
5
|
+
export declare function premiumManifestUrl(playbackRoute: string, videoId: string): string;
|
|
6
|
+
export declare function premiumPlaybackUrl(playbackRoute: string, videoId: string, subpath: string): string;
|
|
7
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/utils/url.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAa,GAAG,MAAM,CAI9F;AAED,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzE;AAMD,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED,wBAAgB,kBAAkB,CACjC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACb,MAAM,CAGR"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function manifestUrl(publicDomain, videoId) {
|
|
2
|
+
return `https://${publicDomain}/video/${videoId}/hls/master.m3u8`;
|
|
3
|
+
}
|
|
4
|
+
export function posterUrl(publicDomain, videoId) {
|
|
5
|
+
return `https://${publicDomain}/cdn-cgi/image/width=1200,height=675,fit=cover,quality=80,format=auto/video/${videoId}/poster.jpg`;
|
|
6
|
+
}
|
|
7
|
+
export function captionsUrl(publicDomain, videoId, lang = 'en') {
|
|
8
|
+
return lang === 'en'
|
|
9
|
+
? `https://${publicDomain}/video/${videoId}/captions.vtt`
|
|
10
|
+
: `https://${publicDomain}/video/${videoId}/captions-${lang}.vtt`;
|
|
11
|
+
}
|
|
12
|
+
export function chaptersUrl(publicDomain, videoId) {
|
|
13
|
+
return `https://${publicDomain}/video/${videoId}/chapters.json`;
|
|
14
|
+
}
|
|
15
|
+
function normalizeRoute(route) {
|
|
16
|
+
return route.endsWith('/') ? route.slice(0, -1) : route;
|
|
17
|
+
}
|
|
18
|
+
export function premiumManifestUrl(playbackRoute, videoId) {
|
|
19
|
+
return `${normalizeRoute(playbackRoute)}/${videoId}/hls/master.m3u8`;
|
|
20
|
+
}
|
|
21
|
+
export function premiumPlaybackUrl(playbackRoute, videoId, subpath) {
|
|
22
|
+
const trimmed = subpath.startsWith('/') ? subpath.slice(1) : subpath;
|
|
23
|
+
return `${normalizeRoute(playbackRoute)}/${videoId}/${trimmed}`;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/utils/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,YAAoB,EAAE,OAAe;IAChE,OAAO,WAAW,YAAY,UAAU,OAAO,kBAAkB,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,YAAoB,EAAE,OAAe;IAC9D,OAAO,WAAW,YAAY,+EAA+E,OAAO,aAAa,CAAA;AAClI,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,YAAoB,EAAE,OAAe,EAAE,OAAe,IAAI;IACrF,OAAO,IAAI,KAAK,IAAI;QACnB,CAAC,CAAC,WAAW,YAAY,UAAU,OAAO,eAAe;QACzD,CAAC,CAAC,WAAW,YAAY,UAAU,OAAO,aAAa,IAAI,MAAM,CAAA;AACnE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,YAAoB,EAAE,OAAe;IAChE,OAAO,WAAW,YAAY,UAAU,OAAO,gBAAgB,CAAA;AAChE,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACpC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,aAAqB,EAAE,OAAe;IACxE,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,IAAI,OAAO,kBAAkB,CAAA;AACrE,CAAC;AAED,MAAM,UAAU,kBAAkB,CACjC,aAAqB,EACrB,OAAe,EACf,OAAe;IAEf,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IACpE,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,IAAI,OAAO,IAAI,OAAO,EAAE,CAAA;AAChE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface YouTubeEmbedOptions {
|
|
2
|
+
autoplay?: boolean;
|
|
3
|
+
start?: number;
|
|
4
|
+
end?: number;
|
|
5
|
+
muted?: boolean;
|
|
6
|
+
controls?: boolean;
|
|
7
|
+
privacyMode?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function youtubeEmbedUrl(videoId: string, opts?: YouTubeEmbedOptions): string;
|
|
10
|
+
export type YouTubeThumbnailQuality = 'default' | 'hq' | 'maxres';
|
|
11
|
+
export declare function youtubeThumbnailUrl(videoId: string, quality?: YouTubeThumbnailQuality): string;
|
|
12
|
+
//# sourceMappingURL=youtube.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"youtube.d.ts","sourceRoot":"","sources":["../../src/utils/youtube.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,mBAAwB,GAAG,MAAM,CAUvF;AAED,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,IAAI,GAAG,QAAQ,CAAA;AAEjE,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAAkC,GACzC,MAAM,CAGR"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function youtubeEmbedUrl(videoId, opts = {}) {
|
|
2
|
+
const host = opts.privacyMode === false ? 'www.youtube.com' : 'www.youtube-nocookie.com';
|
|
3
|
+
const params = new URLSearchParams();
|
|
4
|
+
if (opts.autoplay)
|
|
5
|
+
params.set('autoplay', '1');
|
|
6
|
+
if (opts.muted)
|
|
7
|
+
params.set('mute', '1');
|
|
8
|
+
if (opts.controls === false)
|
|
9
|
+
params.set('controls', '0');
|
|
10
|
+
if (opts.start != null)
|
|
11
|
+
params.set('start', String(opts.start));
|
|
12
|
+
if (opts.end != null)
|
|
13
|
+
params.set('end', String(opts.end));
|
|
14
|
+
const qs = params.toString();
|
|
15
|
+
return `https://${host}/embed/${videoId}${qs ? `?${qs}` : ''}`;
|
|
16
|
+
}
|
|
17
|
+
export function youtubeThumbnailUrl(videoId, quality = 'maxres') {
|
|
18
|
+
const suffix = quality === 'maxres' ? 'maxresdefault' : quality === 'hq' ? 'hqdefault' : 'default';
|
|
19
|
+
return `https://i.ytimg.com/vi/${videoId}/${suffix}.jpg`;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=youtube.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"youtube.js","sourceRoot":"","sources":["../../src/utils/youtube.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,OAA4B,EAAE;IAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,0BAA0B,CAAA;IACxF,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IACpC,IAAI,IAAI,CAAC,QAAQ;QAAE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;IAC9C,IAAI,IAAI,CAAC,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACvC,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;IACxD,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IAC/D,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI;QAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACzD,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;IAC5B,OAAO,WAAW,IAAI,UAAU,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;AAC/D,CAAC;AAID,MAAM,UAAU,mBAAmB,CAClC,OAAe,EACf,UAAmC,QAAQ;IAE3C,MAAM,MAAM,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;IAClG,OAAO,0BAA0B,OAAO,IAAI,MAAM,MAAM,CAAA;AACzD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAKxD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAc1E"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const VIRTUAL_MODULE_ID = 'virtual:growth-labs/video/config';
|
|
2
|
+
const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
3
|
+
export function growthLabsVideoPlugin(config) {
|
|
4
|
+
return {
|
|
5
|
+
name: 'growth-labs-video-config',
|
|
6
|
+
resolveId(id) {
|
|
7
|
+
if (id === VIRTUAL_MODULE_ID) {
|
|
8
|
+
return RESOLVED_VIRTUAL_MODULE_ID;
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
load(id) {
|
|
12
|
+
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
13
|
+
return `export const config = ${JSON.stringify(config)};`;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=vite-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAGA,MAAM,iBAAiB,GAAG,kCAAkC,CAAA;AAC5D,MAAM,0BAA0B,GAAG,KAAK,iBAAiB,EAAE,CAAA;AAE3D,MAAM,UAAU,qBAAqB,CAAC,MAA4B;IACjE,OAAO;QACN,IAAI,EAAE,0BAA0B;QAChC,SAAS,CAAC,EAAE;YACX,IAAI,EAAE,KAAK,iBAAiB,EAAE,CAAC;gBAC9B,OAAO,0BAA0B,CAAA;YAClC,CAAC;QACF,CAAC;QACD,IAAI,CAAC,EAAE;YACN,IAAI,EAAE,KAAK,0BAA0B,EAAE,CAAC;gBACvC,OAAO,yBAAyB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAA;YAC1D,CAAC;QACF,CAAC;KACD,CAAA;AACF,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@growth-labs/video",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./components": {
|
|
11
|
+
"types": "./src/components/index.ts",
|
|
12
|
+
"import": "./src/components/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"./components/*": {
|
|
15
|
+
"types": "./src/components/*.astro",
|
|
16
|
+
"import": "./src/components/*.astro"
|
|
17
|
+
},
|
|
18
|
+
"./utils": {
|
|
19
|
+
"types": "./dist/utils/index.d.ts",
|
|
20
|
+
"import": "./dist/utils/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./utils/*": {
|
|
23
|
+
"types": "./dist/utils/*.d.ts",
|
|
24
|
+
"import": "./dist/utils/*.js"
|
|
25
|
+
},
|
|
26
|
+
"./routes/*": {
|
|
27
|
+
"types": "./dist/routes/*.d.ts",
|
|
28
|
+
"import": "./dist/routes/*.js",
|
|
29
|
+
"default": "./dist/routes/*.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"src/components"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public",
|
|
38
|
+
"registry": "https://registry.npmjs.org/"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"astro": "^6.0.0",
|
|
42
|
+
"vidstack": "^1.12.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"vidstack": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"zod": "^3.23.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@cloudflare/workers-types": "^4.20260402.1",
|
|
54
|
+
"astro": "^6.0.0",
|
|
55
|
+
"typescript": "^5.7.0",
|
|
56
|
+
"vite": "^7.0.0",
|
|
57
|
+
"vitest": "^3.0.0",
|
|
58
|
+
"vidstack": "^1.12.0"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsc",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"test:watch": "vitest"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { config } from 'virtual:growth-labs/video/config'
|
|
3
|
+
import {
|
|
4
|
+
captionsUrl,
|
|
5
|
+
chaptersUrl,
|
|
6
|
+
manifestUrl,
|
|
7
|
+
posterUrl,
|
|
8
|
+
premiumManifestUrl,
|
|
9
|
+
} from '@growth-labs/video/utils'
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
videoId: string
|
|
13
|
+
title: string
|
|
14
|
+
premium?: boolean
|
|
15
|
+
poster?: string
|
|
16
|
+
captions?: boolean
|
|
17
|
+
chapters?: boolean
|
|
18
|
+
captionsLang?: string
|
|
19
|
+
autoplay?: boolean
|
|
20
|
+
muted?: boolean
|
|
21
|
+
class?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
videoId,
|
|
26
|
+
title,
|
|
27
|
+
premium = false,
|
|
28
|
+
captionsLang = 'en',
|
|
29
|
+
class: className,
|
|
30
|
+
} = Astro.props
|
|
31
|
+
|
|
32
|
+
const showCaptions = Astro.props.captions ?? config.player.showCaptions
|
|
33
|
+
const showChapters = Astro.props.chapters ?? config.player.showChapters
|
|
34
|
+
|
|
35
|
+
const src = premium
|
|
36
|
+
? premiumManifestUrl(config.premium!.playbackRoute, videoId)
|
|
37
|
+
: manifestUrl(config.publicDomain, videoId)
|
|
38
|
+
|
|
39
|
+
const poster = Astro.props.poster ?? posterUrl(config.publicDomain, videoId)
|
|
40
|
+
const chaptersSrc = showChapters ? chaptersUrl(config.publicDomain, videoId) : null
|
|
41
|
+
const captionsSrc = showCaptions
|
|
42
|
+
? captionsUrl(config.publicDomain, videoId, captionsLang)
|
|
43
|
+
: null
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<media-player
|
|
47
|
+
data-video-id={videoId}
|
|
48
|
+
data-premium={premium ? 'true' : 'false'}
|
|
49
|
+
data-session-route={premium ? config.premium?.sessionRoute : undefined}
|
|
50
|
+
data-chapters-src={chaptersSrc}
|
|
51
|
+
data-captions-src={captionsSrc}
|
|
52
|
+
data-captions-lang={captionsLang}
|
|
53
|
+
data-progress-marks={JSON.stringify(config.analytics.progressMarks)}
|
|
54
|
+
title={title}
|
|
55
|
+
src={src}
|
|
56
|
+
poster={poster}
|
|
57
|
+
preload={config.player.preload}
|
|
58
|
+
autoplay={Astro.props.autoplay ?? config.player.autoplay}
|
|
59
|
+
muted={Astro.props.muted ?? config.player.muted}
|
|
60
|
+
class={`gl-video ${className ?? ''}`.trim()}
|
|
61
|
+
>
|
|
62
|
+
<media-provider></media-provider>
|
|
63
|
+
<media-video-layout></media-video-layout>
|
|
64
|
+
</media-player>
|
|
65
|
+
|
|
66
|
+
<script>
|
|
67
|
+
import('vidstack/elements').then((mod: any) => {
|
|
68
|
+
if (typeof mod.defineCustomElements === 'function') {
|
|
69
|
+
mod.defineCustomElements()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const VIDEO_ACCESS_DENIED = 'gl:video-access-denied'
|
|
74
|
+
|
|
75
|
+
document.querySelectorAll<HTMLElement>('media-player[data-premium="true"]').forEach((player) => {
|
|
76
|
+
const videoId = player.dataset.videoId
|
|
77
|
+
const sessionRoute = player.dataset.sessionRoute
|
|
78
|
+
if (!videoId || !sessionRoute) return
|
|
79
|
+
|
|
80
|
+
let sessionChecked = false
|
|
81
|
+
const ensure = async () => {
|
|
82
|
+
if (sessionChecked) return
|
|
83
|
+
sessionChecked = true
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch(sessionRoute, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
credentials: 'same-origin',
|
|
88
|
+
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
body: JSON.stringify({ videoId }),
|
|
90
|
+
})
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
window.dispatchEvent(
|
|
93
|
+
new CustomEvent(VIDEO_ACCESS_DENIED, { detail: { videoId } }),
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
window.dispatchEvent(
|
|
98
|
+
new CustomEvent(VIDEO_ACCESS_DENIED, { detail: { videoId } }),
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
player.addEventListener('pointerenter', ensure, { once: true })
|
|
104
|
+
player.addEventListener('play', ensure)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
document.querySelectorAll<HTMLElement>('media-player[data-chapters-src]').forEach(async (player) => {
|
|
108
|
+
const src = player.dataset.chaptersSrc
|
|
109
|
+
if (!src) return
|
|
110
|
+
try {
|
|
111
|
+
const res = await fetch(src)
|
|
112
|
+
if (!res.ok) return
|
|
113
|
+
const data = (await res.json()) as { chapters: unknown }
|
|
114
|
+
;(player as unknown as { chapters?: unknown }).chapters = data.chapters
|
|
115
|
+
} catch {
|
|
116
|
+
// silent
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const emit = (name: string, detail: unknown) =>
|
|
121
|
+
window.dispatchEvent(new CustomEvent(name, { detail }))
|
|
122
|
+
|
|
123
|
+
document.querySelectorAll<HTMLElement>('media-player').forEach((player) => {
|
|
124
|
+
const videoId = player.dataset.videoId
|
|
125
|
+
if (!videoId) return
|
|
126
|
+
const marks: number[] = JSON.parse(player.dataset.progressMarks || '[]')
|
|
127
|
+
const fired = new Set<number>()
|
|
128
|
+
|
|
129
|
+
player.addEventListener('play', () => emit('gl:video-play', { videoId }))
|
|
130
|
+
player.addEventListener('ended', () =>
|
|
131
|
+
emit('gl:video-complete', {
|
|
132
|
+
videoId,
|
|
133
|
+
duration: (player as unknown as { duration?: number }).duration,
|
|
134
|
+
}),
|
|
135
|
+
)
|
|
136
|
+
player.addEventListener('error', (e: Event) =>
|
|
137
|
+
emit('gl:video-error', { videoId, error: String((e as ErrorEvent).message ?? '') }),
|
|
138
|
+
)
|
|
139
|
+
player.addEventListener('timeupdate', () => {
|
|
140
|
+
const p = player as unknown as { currentTime?: number; duration?: number }
|
|
141
|
+
if (!p.currentTime || !p.duration) return
|
|
142
|
+
const fraction = p.currentTime / p.duration
|
|
143
|
+
for (const mark of marks) {
|
|
144
|
+
if (fraction >= mark && !fired.has(mark)) {
|
|
145
|
+
fired.add(mark)
|
|
146
|
+
emit('gl:video-progress', {
|
|
147
|
+
videoId,
|
|
148
|
+
currentTime: p.currentTime,
|
|
149
|
+
duration: p.duration,
|
|
150
|
+
fraction: mark,
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<style>
|
|
159
|
+
.gl-video {
|
|
160
|
+
width: 100%;
|
|
161
|
+
aspect-ratio: 16 / 9;
|
|
162
|
+
display: block;
|
|
163
|
+
}
|
|
164
|
+
</style>
|