annotate mrjunejune/src/tools/hls_player/hls-player.js @ 244:b8aa08503378

[tools] Add sandboxed online LaTeX editor Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Mon, 03 Aug 2026 16:56:25 -0700
parents 543df0fe7168
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
242
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
1 (function hlsPlayerModule(global) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
2 "use strict";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
3
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
4 const DEFAULT_CODECS = "vp09.00.10.08,opus";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
5 const HLS_MIME_TYPES = [
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
6 "application/vnd.apple.mpegurl",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
7 "application/x-mpegURL",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
8 ];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
9
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
10 function meaningfulLines(text) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
11 return text
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
12 .split(/\r?\n/)
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
13 .map(line => line.trim())
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
14 .filter(Boolean);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
15 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
16
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
17 function parseAttributeList(value) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
18 const attributes = {};
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
19 const expression = /([A-Z0-9-]+)=("[^"]*"|[^,]*)/g;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
20 let match;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
21 while ((match = expression.exec(value)) !== null) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
22 const rawValue = match[2];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
23 attributes[match[1]] = rawValue.startsWith('"')
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
24 ? rawValue.slice(1, -1)
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
25 : rawValue;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
26 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
27 return attributes;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
28 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
29
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
30 function resolveUri(value, baseUrl) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
31 return new URL(value, baseUrl).href;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
32 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
33
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
34 function normalizePlaylistUrl(value, baseUrl) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
35 const url = new URL(value, baseUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
36 if (url.protocol !== "http:" &&
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
37 url.protocol !== "https:" &&
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
38 url.protocol !== "blob:") {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
39 throw new Error("HLS playlist URL must use HTTP, HTTPS, or a local file.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
40 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
41 return url.href;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
42 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
43
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
44 function normalizeLocalPath(path) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
45 const output = [];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
46 for (const part of path.replace(/\\/g, "/").split("/")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
47 if (!part || part === ".") continue;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
48 if (part === "..") {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
49 if (!output.length) throw new Error("Local playlist path escapes its selected folder.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
50 output.pop();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
51 } else {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
52 output.push(part);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
53 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
54 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
55 return output.join("/");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
56 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
57
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
58 function localRelativePath(currentPath, reference) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
59 const cleanReference = reference.split(/[?#]/)[0];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
60 if (/^https?:\/\//i.test(cleanReference) ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
61 cleanReference.startsWith("blob:")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
62 return null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
63 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
64 const slash = currentPath.lastIndexOf("/");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
65 const rootRelative = cleanReference.startsWith("/");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
66 const directory = !rootRelative && slash >= 0
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
67 ? currentPath.slice(0, slash + 1)
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
68 : "";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
69 return normalizeLocalPath(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
70 `${directory}${decodeURIComponent(cleanReference).replace(/^\/+/, "")}`,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
71 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
72 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
73
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
74 async function rewritePlaylistUris(text, resolveReference) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
75 const output = [];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
76 for (const originalLine of text.split(/\r?\n/)) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
77 const line = originalLine.trim();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
78 if (line.startsWith("#")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
79 let rewrittenLine = originalLine;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
80 const references = [
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
81 ...originalLine.matchAll(/URI="([^"]+)"/g),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
82 ].map(match => match[1]);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
83 for (const reference of references) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
84 rewrittenLine = rewrittenLine.replace(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
85 `URI="${reference}"`,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
86 `URI="${await resolveReference(reference)}"`,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
87 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
88 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
89 output.push(rewrittenLine);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
90 } else if (line && !line.startsWith("#")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
91 output.push(await resolveReference(line));
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
92 } else {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
93 output.push(originalLine);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
94 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
95 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
96 return output.join("\n");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
97 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
98
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
99 async function createLocalHlsUrl(fileList) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
100 const files = Array.from(fileList || []);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
101 if (!files.length) throw new Error("Choose an HLS playlist and its media files.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
102
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
103 const byPath = new Map();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
104 const byName = new Map();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
105 for (const file of files) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
106 const relativePath = normalizeLocalPath(file.webkitRelativePath || file.name);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
107 byPath.set(relativePath, file);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
108 const basename = relativePath.split("/").pop();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
109 if (!byName.has(basename)) byName.set(basename, file);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
110 else byName.set(basename, null);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
111 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
112
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
113 const playlists = [...byPath.entries()].filter(([filePath]) =>
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
114 filePath.toLowerCase().endsWith(".m3u8")
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
115 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
116 if (!playlists.length) throw new Error("No .m3u8 playlist was selected.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
117 const master =
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
118 playlists.find(([filePath]) => /(^|\/)master\.m3u8$/i.test(filePath)) ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
119 playlists.find(([filePath]) => /stream\.m3u8$/i.test(filePath)) ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
120 playlists[0];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
121
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
122 const objectUrls = [];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
123 const assetUrls = new Map();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
124 const playlistUrls = new Map();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
125 const resolving = new Set();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
126
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
127 const rememberUrl = blob => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
128 const url = URL.createObjectURL(blob);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
129 objectUrls.push(url);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
130 return url;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
131 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
132
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
133 const findFile = (reference, currentPath) => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
134 const relativePath = localRelativePath(currentPath, reference);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
135 if (relativePath === null) return null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
136 const direct = byPath.get(relativePath);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
137 if (direct) return [relativePath, direct];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
138 const basename = relativePath.split("/").pop();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
139 const unique = byName.get(basename);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
140 if (unique) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
141 const uniquePath = [...byPath.entries()].find(([, file]) => file === unique)[0];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
142 return [uniquePath, unique];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
143 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
144 throw new Error(`Local HLS file is missing: ${reference}`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
145 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
146
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
147 const materializePlaylist = async (filePath, file) => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
148 if (playlistUrls.has(filePath)) return playlistUrls.get(filePath);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
149 if (resolving.has(filePath)) throw new Error("Local HLS playlists contain a cycle.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
150 resolving.add(filePath);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
151 try {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
152 const rewritten = await rewritePlaylistUris(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
153 await file.text(),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
154 async reference => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
155 if (/^https?:\/\//i.test(reference) ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
156 reference.startsWith("blob:")) return reference;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
157 const [resolvedPath, resolvedFile] = findFile(reference, filePath);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
158 if (resolvedPath.toLowerCase().endsWith(".m3u8")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
159 return materializePlaylist(resolvedPath, resolvedFile);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
160 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
161 if (!assetUrls.has(resolvedPath)) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
162 assetUrls.set(resolvedPath, rememberUrl(resolvedFile));
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
163 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
164 return assetUrls.get(resolvedPath);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
165 },
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
166 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
167 const url = rememberUrl(new Blob(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
168 [rewritten],
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
169 { type: "application/vnd.apple.mpegurl" },
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
170 ));
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 playlistUrls.set(filePath, url);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 return url;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173 } finally {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 resolving.delete(filePath);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178 try {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
179 const url = await materializePlaylist(master[0], master[1]);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
180 return {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
181 url,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
182 playlistName: master[0],
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
183 revoke() {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
184 for (const objectUrl of objectUrls) URL.revokeObjectURL(objectUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185 objectUrls.length = 0;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 },
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188 } catch (error) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189 for (const objectUrl of objectUrls) URL.revokeObjectURL(objectUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190 throw error;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
193
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
194 function parseMasterPlaylist(text, baseUrl) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195 const lines = meaningfulLines(text);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
196 const variants = [];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197 for (let index = 0; index < lines.length; index++) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 if (!lines[index].startsWith("#EXT-X-STREAM-INF:")) continue;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 const attributes = parseAttributeList(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200 lines[index].slice("#EXT-X-STREAM-INF:".length),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
201 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
202 const uri = lines[index + 1];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 if (!uri || uri.startsWith("#")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 throw new Error("HLS variant is missing its playlist URL.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
206 variants.push({
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
207 url: resolveUri(uri, baseUrl),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208 bandwidth: Number(attributes.BANDWIDTH || 0),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 codecs: attributes.CODECS || "",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
210 resolution: attributes.RESOLUTION || "",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
211 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212 index++;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214 return variants;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 function parseMediaPlaylist(text, baseUrl) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 const lines = meaningfulLines(text);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219 const segments = [];
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 let initSegment = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
221 let duration = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
222 let totalDuration = 0;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
223 let endList = false;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
224
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
225 for (const line of lines) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
226 if (line.startsWith("#EXT-X-KEY:")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
227 const attributes = parseAttributeList(line.slice("#EXT-X-KEY:".length));
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
228 if ((attributes.METHOD || "NONE") !== "NONE") {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
229 throw new Error("Encrypted HLS playlists are not supported by the JavaScript fallback.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
230 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
231 } else if (line.startsWith("#EXT-X-BYTERANGE")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
232 throw new Error("Byte-range HLS playlists are not supported by the JavaScript fallback.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
233 } else if (line.startsWith("#EXT-X-MAP:")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
234 const attributes = parseAttributeList(line.slice("#EXT-X-MAP:".length));
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
235 if (!attributes.URI) throw new Error("HLS initialization segment URL is missing.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
236 initSegment = resolveUri(attributes.URI, baseUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
237 } else if (line.startsWith("#EXTINF:")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
238 duration = Number(line.slice("#EXTINF:".length).split(",")[0]);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
239 if (!Number.isFinite(duration)) throw new Error("Invalid HLS segment duration.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
240 } else if (line === "#EXT-X-ENDLIST") {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
241 endList = true;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
242 } else if (!line.startsWith("#")) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
243 if (duration === null) continue;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
244 segments.push({
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
245 url: resolveUri(line, baseUrl),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
246 duration,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
247 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
248 totalDuration += duration;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
249 duration = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
250 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
251 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
252
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
253 return { initSegment, segments, totalDuration, endList };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
254 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
255
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
256 function chooseVariant(variants, isSupported = () => true) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
257 const supported = variants.filter(isSupported);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
258 if (!supported.length) return null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
259 return supported.reduce((best, current) =>
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
260 current.bandwidth > best.bandwidth ? current : best
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
261 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
262 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
263
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
264 function once(target, eventName, errorName) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
265 return new Promise((resolve, reject) => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
266 const cleanup = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
267 target.removeEventListener(eventName, onEvent);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
268 if (errorName) target.removeEventListener(errorName, onError);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
269 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
270 const onEvent = event => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
271 cleanup();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
272 resolve(event);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
273 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
274 const onError = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
275 cleanup();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
276 reject(new Error(`Media event failed: ${errorName}`));
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
277 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
278 target.addEventListener(eventName, onEvent, { once: true });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279 if (errorName) target.addEventListener(errorName, onError, { once: true });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
280 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
282
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
283 async function appendBuffer(sourceBuffer, bytes) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
284 sourceBuffer.appendBuffer(bytes);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
285 await once(sourceBuffer, "updateend", "error");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
286 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
287
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
288 class HlsPlayer {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
289 constructor(video, options = {}) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
290 if (!video) throw new Error("A video element is required.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
291 this.video = video;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
292 this.statusElement = options.statusElement || null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
293 this.detailsElement = options.detailsElement || null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
294 this.abortController = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
295 this.mediaSource = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
296 this.objectUrl = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
297 this.hls = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
298 this.generation = 0;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
299 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
300
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
301 setStatus(message, state = "loading") {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
302 if (!this.statusElement) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
303 this.statusElement.textContent = message;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
304 this.statusElement.dataset.state = state;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
305 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
306
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
307 setDetails(details) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
308 if (!this.detailsElement) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
309 for (const [key, value] of Object.entries(details)) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
310 const target = this.detailsElement.querySelector(`[data-detail="${key}"]`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
311 if (target) target.textContent = String(value);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
312 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
313 this.detailsElement.hidden = false;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
314 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
315
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
316 destroy() {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
317 this.generation++;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
318 if (this.abortController) this.abortController.abort();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
319 this.abortController = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
320 if (this.hls) this.hls.destroy();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
321 this.hls = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
322 this.video.pause();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
323 this.video.removeAttribute("src");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
324 this.video.load();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
325 if (this.objectUrl) URL.revokeObjectURL(this.objectUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
326 this.objectUrl = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
327 this.mediaSource = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
328 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
329
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
330 async fetchText(url, signal) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
331 const response = await fetch(url, { signal, cache: "no-store" });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
332 if (!response.ok) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
333 throw new Error(`Playlist request failed (${response.status}).`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
334 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
335 return response.text();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
336 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
337
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
338 async fetchBytes(url, signal) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
339 const response = await fetch(url, { signal, cache: "no-store" });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
340 if (!response.ok) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
341 throw new Error(`Media request failed (${response.status}): ${url}`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
342 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
343 return response.arrayBuffer();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
344 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
345
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
346 nativeHlsSupported() {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
347 return HLS_MIME_TYPES.some(type => this.video.canPlayType(type) !== "");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
348 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
349
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
350 hlsJsSupported() {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
351 return Boolean(global.Hls && global.Hls.isSupported());
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
352 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
353
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
354 async loadWithHlsJs(playlistUrl, generation, signal) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
355 const Hls = global.Hls;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
356 const hls = new Hls({ enableWorker: false });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
357 this.hls = hls;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
358 this.setStatus("Loading HLS manifest...");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
359
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
360 const manifest = await new Promise((resolve, reject) => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
361 let manifestData = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
362 let mediaReady = this.video.readyState >= 2;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
363 let settled = false;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
364
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
365 const cleanup = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
366 hls.off(Hls.Events.MEDIA_ATTACHED, onMediaAttached);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
367 hls.off(Hls.Events.MANIFEST_PARSED, onManifestParsed);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
368 this.video.removeEventListener("loadeddata", onLoadedData);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
369 this.video.removeEventListener("error", onMediaError);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
370 signal.removeEventListener("abort", onAbort);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
371 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
372 const finish = (callback, value) => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
373 if (settled) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
374 settled = true;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
375 cleanup();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
376 callback(value);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
377 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
378 const maybeResolve = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
379 if (manifestData && mediaReady) finish(resolve, manifestData);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
380 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
381 const onMediaAttached = () => hls.loadSource(playlistUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
382 const onManifestParsed = (_event, data) => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
383 manifestData = data;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
384 maybeResolve();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
385 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
386 const onLoadedData = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
387 mediaReady = true;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
388 maybeResolve();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
389 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
390 const onMediaError = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
391 finish(reject, new Error("The browser could not decode this HLS stream."));
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
392 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
393 const onHlsError = (_event, data) => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
394 if (!data.fatal) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
395 const reason = data.error?.message ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
396 data.reason ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
397 data.details ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
398 data.type ||
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
399 "unknown error";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
400 const error = new Error(`HLS playback failed: ${reason}`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
401 if (!settled) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
402 finish(reject, error);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
403 return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
404 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
405 if (generation === this.generation && this.hls === hls) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
406 hls.destroy();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
407 this.hls = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
408 this.setStatus(error.message, "error");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
409 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
410 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
411 const onAbort = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
412 const error = new Error("HLS loading was cancelled.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
413 error.name = "AbortError";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
414 finish(reject, error);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
415 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
416
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
417 hls.on(Hls.Events.MEDIA_ATTACHED, onMediaAttached);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
418 hls.on(Hls.Events.MANIFEST_PARSED, onManifestParsed);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
419 hls.on(Hls.Events.ERROR, onHlsError);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
420 this.video.addEventListener("loadeddata", onLoadedData);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
421 this.video.addEventListener("error", onMediaError);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
422 signal.addEventListener("abort", onAbort, { once: true });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
423 hls.attachMedia(this.video);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
424 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
425
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
426 if (generation !== this.generation || signal.aborted) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
427 const selectedLevelIndex = hls.currentLevel >= 0
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
428 ? hls.currentLevel
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
429 : hls.loadLevel;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
430 const selectedLevel = hls.levels[selectedLevelIndex] || hls.levels[0] || {};
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
431 const codecs = [
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
432 selectedLevel.videoCodec,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
433 selectedLevel.audioCodec,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
434 ].filter(Boolean).join(", ") || "Detected from stream";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
435 const live = !Number.isFinite(this.video.duration);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
436
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
437 if (!live && this.video.duration > 0 && this.video.seekable.length) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
438 this.video.currentTime = Math.min(0.05, this.video.duration);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
439 await once(this.video, "seeked", "error");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
440 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
441 if (generation !== this.generation || signal.aborted) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
442
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
443 this.setDetails({
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
444 mode: "hls.js",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
445 segments: manifest.levels?.length > 1
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
446 ? `${manifest.levels.length} adaptive levels`
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
447 : "Managed by hls.js",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
448 duration: live ? "Live" : `${this.video.duration.toFixed(1)} seconds`,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
449 codecs,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
450 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
451 this.setStatus(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
452 live ? "Live stream ready. Press play." : "Stream ready. Press play.",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
453 "ready",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
454 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
455 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
456
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
457 async load(inputUrl, options = {}) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
458 this.destroy();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
459 const generation = this.generation;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
460 this.abortController = new AbortController();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
461 const signal = this.abortController.signal;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
462 try {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
463 const playlistUrl = normalizePlaylistUrl(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
464 inputUrl,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
465 global.location?.href || "http://localhost/",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
466 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
467 this.setStatus("Loading playlist...");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
468 if (this.nativeHlsSupported() && !options.forceMediaSource) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
469 this.video.src = playlistUrl;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
470 await once(this.video, "loadedmetadata", "error");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
471 if (generation !== this.generation || signal.aborted) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
472 this.setDetails({
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
473 mode: "Native HLS",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
474 segments: "Managed by browser",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
475 duration: Number.isFinite(this.video.duration)
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
476 ? `${this.video.duration.toFixed(1)} seconds`
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
477 : "Live",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
478 codecs: "Managed by browser",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
479 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
480 this.setStatus("Stream ready.", "ready");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
481 return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
482 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
483
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
484 if (this.hlsJsSupported()) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
485 await this.loadWithHlsJs(playlistUrl, generation, signal);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
486 return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
487 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
488
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
489 if (!global.MediaSource) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
490 throw new Error("This browser does not support native HLS or MediaSource playback.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
491 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
492
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
493 let mediaPlaylistUrl = playlistUrl;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
494 let codecs = "";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
495 let playlistText = await this.fetchText(mediaPlaylistUrl, signal);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
496 const variants = parseMasterPlaylist(playlistText, mediaPlaylistUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
497 if (variants.length) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
498 const variant = chooseVariant(variants, candidate => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
499 const candidateCodecs = candidate.codecs || DEFAULT_CODECS;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
500 return global.MediaSource.isTypeSupported(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
501 `video/mp4; codecs="${candidateCodecs}"`,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
502 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
503 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
504 if (!variant) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
505 throw new Error("No HLS variant uses a codec supported by this browser.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
506 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
507 mediaPlaylistUrl = variant.url;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
508 codecs = variant.codecs;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
509 this.setStatus(`Loading ${variant.bandwidth || "selected"} bps variant...`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
510 playlistText = await this.fetchText(mediaPlaylistUrl, signal);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
511 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
512
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
513 const playlist = parseMediaPlaylist(playlistText, mediaPlaylistUrl);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
514 if (!playlist.endList) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
515 throw new Error("The JavaScript fallback currently supports VOD playlists only.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
516 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
517 if (!playlist.initSegment || !playlist.segments.length) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
518 throw new Error("The JavaScript fallback requires an fMP4 playlist with EXT-X-MAP.");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
519 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
520
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
521 codecs = codecs || DEFAULT_CODECS;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
522 const mimeType = `video/mp4; codecs="${codecs}"`;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
523 if (!global.MediaSource.isTypeSupported(mimeType)) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
524 throw new Error(`Browser does not support ${mimeType}.`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
525 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
526
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
527 this.mediaSource = new global.MediaSource();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
528 this.objectUrl = URL.createObjectURL(this.mediaSource);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
529 this.video.src = this.objectUrl;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
530 await once(this.mediaSource, "sourceopen");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
531 if (generation !== this.generation) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
532
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
533 const sourceBuffer = this.mediaSource.addSourceBuffer(mimeType);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
534 sourceBuffer.mode = "segments";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
535 const initBytes = await this.fetchBytes(playlist.initSegment, signal);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
536 if (generation !== this.generation || signal.aborted) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
537 await appendBuffer(sourceBuffer, initBytes);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
538
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
539 for (let index = 0; index < playlist.segments.length; index++) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
540 this.setStatus(`Loading segment ${index + 1} of ${playlist.segments.length}...`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
541 const segmentBytes = await this.fetchBytes(
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
542 playlist.segments[index].url,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
543 signal,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
544 );
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
545 if (generation !== this.generation || signal.aborted) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
546 await appendBuffer(sourceBuffer, segmentBytes);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
547 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
548
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
549 if (generation !== this.generation) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
550 this.mediaSource.endOfStream();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
551 this.video.currentTime = Math.min(0.05, playlist.totalDuration);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
552 await once(this.video, "seeked", "error");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
553 if (generation !== this.generation || signal.aborted) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
554 this.setDetails({
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
555 mode: "JavaScript MediaSource",
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
556 segments: playlist.segments.length,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
557 duration: `${playlist.totalDuration.toFixed(1)} seconds`,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
558 codecs,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
559 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
560 this.setStatus("Stream ready. Press play.", "ready");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
561 } catch (error) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
562 if (error.name === "AbortError" || generation !== this.generation) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
563 if (this.hls) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
564 this.hls.destroy();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
565 this.hls = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
566 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
567 this.setStatus(error.message || "Unable to load HLS stream.", "error");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
568 throw error;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
569 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
570 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
571 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
572
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
573 const exportsObject = {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
574 HlsPlayer,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
575 chooseVariant,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
576 parseAttributeList,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
577 parseMasterPlaylist,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
578 parseMediaPlaylist,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
579 normalizePlaylistUrl,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
580 createLocalHlsUrl,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
581 localRelativePath,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
582 rewritePlaylistUris,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
583 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
584
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
585 if (typeof module !== "undefined" && module.exports) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
586 module.exports = exportsObject;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
587 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
588 global.HlsPlayerModule = exportsObject;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
589
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
590 if (global.document) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
591 global.addEventListener("DOMContentLoaded", () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
592 const form = document.querySelector("#hlsForm");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
593 const input = document.querySelector("#hlsUrl");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
594 const video = document.querySelector("#hlsVideo");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
595 const status = document.querySelector("#hlsStatus");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
596 const details = document.querySelector("#hlsDetails");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
597 const sampleButton = document.querySelector("#sampleButton");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
598 const fileInputs = [
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
599 document.querySelector("#hlsFiles"),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
600 document.querySelector("#hlsFolder"),
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
601 ].filter(Boolean);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
602 if (!form || !input || !video) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
603
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
604 const player = new HlsPlayer(video, {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
605 statusElement: status,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
606 detailsElement: details,
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
607 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
608 global.hlsPlayer = player;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
609 let localSelection = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
610 let localLoadGeneration = 0;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
611
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
612 const load = () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
613 localLoadGeneration++;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
614 if (localSelection) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
615 localSelection.revoke();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
616 localSelection = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
617 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
618 const value = input.value.trim();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
619 if (!value) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
620 const url = new URL(global.location.href);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
621 url.searchParams.set("url", value);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
622 global.history.replaceState({}, "", url);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
623 player.load(value).catch(() => {});
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
624 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
625
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
626 const loadLocal = async files => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
627 const generation = ++localLoadGeneration;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
628 if (localSelection) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
629 localSelection.revoke();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
630 localSelection = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
631 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
632 let selection = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
633 try {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
634 selection = await createLocalHlsUrl(files);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
635 if (generation !== localLoadGeneration) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
636 selection.revoke();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
637 return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
638 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
639 localSelection = selection;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
640 input.value = `Local: ${selection.playlistName}`;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
641 player.setStatus(`Loading local playlist ${selection.playlistName}...`);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
642 await player.load(selection.url, { forceMediaSource: true });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
643 if (generation !== localLoadGeneration) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
644 } catch (error) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
645 if (selection) selection.revoke();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
646 if (localSelection === selection) localSelection = null;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
647 if (generation !== localLoadGeneration) return;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
648 player.setStatus(error.message || "Unable to load local HLS files.", "error");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
649 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
650 };
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
651
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
652 form.addEventListener("submit", event => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
653 event.preventDefault();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
654 load();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
655 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
656 sampleButton.addEventListener("click", () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
657 input.value = "/public/hls-sample/h264-ts-stream.m3u8";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
658 load();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
659 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
660 for (const fileInput of fileInputs) {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
661 fileInput.addEventListener("change", () => {
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
662 if (fileInput.files?.length) loadLocal(fileInput.files);
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
663 fileInput.value = "";
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
664 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
665 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
666
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
667 const requestedUrl = new URL(global.location.href).searchParams.get("url");
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
668 if (requestedUrl) input.value = requestedUrl;
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
669 load();
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
670 });
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
671 }
543df0fe7168 [tools] Add full HLS player support
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
672 })(typeof window !== "undefined" ? window : globalThis);