@jburnhams/tube-ts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/dist/browser/index.css +988 -0
- package/dist/browser/index.css.map +1 -0
- package/dist/browser/index.global.js +82151 -0
- package/dist/browser/index.global.js.map +1 -0
- package/dist/bundles/index.css +988 -0
- package/dist/bundles/index.css.map +1 -0
- package/dist/bundles/index.esm.js +82137 -0
- package/dist/bundles/index.esm.js.map +1 -0
- package/dist/index.cjs +854 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +841 -0
- package/dist/index.js.map +1 -0
- package/package.json +81 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
import shaka2 from 'shaka-player/dist/shaka-player.ui';
|
|
2
|
+
import { Platform, Innertube, UniversalCache, Utils, YT, Constants } from 'youtubei.js/web';
|
|
3
|
+
import { SabrUmpProcessor, SabrStreamingAdapter } from 'googlevideo/sabr-streaming-adapter';
|
|
4
|
+
import { FormatKeyUtils, isGoogleVideoURL, buildSabrFormat } from 'googlevideo/utils';
|
|
5
|
+
import { GOOG_API_KEY, buildURL, BG } from 'bgutils-js';
|
|
6
|
+
import 'shaka-player/dist/controls.css';
|
|
7
|
+
|
|
8
|
+
// src/TubePlayer.ts
|
|
9
|
+
function asMap(object) {
|
|
10
|
+
const map = /* @__PURE__ */ new Map();
|
|
11
|
+
for (const key of Object.keys(object)) {
|
|
12
|
+
map.set(key, object[key]);
|
|
13
|
+
}
|
|
14
|
+
return map;
|
|
15
|
+
}
|
|
16
|
+
function createRecoverableError(message, info) {
|
|
17
|
+
return new shaka2.util.Error(
|
|
18
|
+
shaka2.util.Error.Severity.RECOVERABLE,
|
|
19
|
+
shaka2.util.Error.Category.NETWORK,
|
|
20
|
+
shaka2.util.Error.Code.HTTP_ERROR,
|
|
21
|
+
message,
|
|
22
|
+
{ info }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
function headersToGenericObject(headers) {
|
|
26
|
+
const headersObj = {};
|
|
27
|
+
headers.forEach((value, key) => {
|
|
28
|
+
headersObj[key.trim()] = value;
|
|
29
|
+
});
|
|
30
|
+
return headersObj;
|
|
31
|
+
}
|
|
32
|
+
function makeResponse(headers, data, status, uri, responseURL, request, requestType) {
|
|
33
|
+
if (status >= 200 && status <= 299 && status !== 202) {
|
|
34
|
+
return {
|
|
35
|
+
uri: responseURL || uri,
|
|
36
|
+
originalUri: uri,
|
|
37
|
+
data,
|
|
38
|
+
status,
|
|
39
|
+
headers,
|
|
40
|
+
originalRequest: request,
|
|
41
|
+
fromCache: !!headers["x-shaka-from-cache"]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
let responseText = null;
|
|
45
|
+
try {
|
|
46
|
+
responseText = shaka2.util.StringUtils.fromBytesAutoDetect(data);
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
const severity = status === 401 || status === 403 ? shaka2.util.Error.Severity.CRITICAL : shaka2.util.Error.Severity.RECOVERABLE;
|
|
50
|
+
throw new shaka2.util.Error(
|
|
51
|
+
severity,
|
|
52
|
+
shaka2.util.Error.Category.NETWORK,
|
|
53
|
+
shaka2.util.Error.Code.BAD_HTTP_STATUS,
|
|
54
|
+
uri,
|
|
55
|
+
status,
|
|
56
|
+
responseText,
|
|
57
|
+
headers,
|
|
58
|
+
requestType,
|
|
59
|
+
responseURL || uri
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
async function fetchFunction(input, init) {
|
|
63
|
+
const url = input instanceof URL ? input : new URL(typeof input === "string" ? input : input.url);
|
|
64
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
65
|
+
if (url.pathname.includes("v1/player")) {
|
|
66
|
+
url.searchParams.set("$fields", "playerConfig,storyboards,captions,playabilityStatus,streamingData,responseContext.mainAppWebResponseContext.datasyncId,videoDetails.isLive,videoDetails.isLiveContent,videoDetails.title,videoDetails.author,videoDetails.thumbnail");
|
|
67
|
+
}
|
|
68
|
+
let skipProxy = false;
|
|
69
|
+
try {
|
|
70
|
+
if (typeof process !== "undefined" && process.env && process.env.SKIP_PROXY === "true") {
|
|
71
|
+
skipProxy = true;
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
}
|
|
75
|
+
if (skipProxy) {
|
|
76
|
+
const requestInit2 = {
|
|
77
|
+
...init,
|
|
78
|
+
headers
|
|
79
|
+
};
|
|
80
|
+
if (input instanceof Request && !requestInit2.method) {
|
|
81
|
+
requestInit2.method = input.method;
|
|
82
|
+
}
|
|
83
|
+
return fetch(url.toString(), requestInit2);
|
|
84
|
+
}
|
|
85
|
+
const proxyUrl = new URL(url.pathname + url.search, "https://vps.jonathanburnhams.com/");
|
|
86
|
+
if (url.pathname.includes("/s/player/") || url.pathname.includes("/yts/jsbin/")) {
|
|
87
|
+
proxyUrl.searchParams.set("__host", "www.youtube.com");
|
|
88
|
+
const scriptPathIndex = url.pathname.indexOf("/s/player/");
|
|
89
|
+
if (scriptPathIndex > -1) {
|
|
90
|
+
proxyUrl.pathname = url.pathname.substring(scriptPathIndex);
|
|
91
|
+
} else {
|
|
92
|
+
const jsbinIndex = url.pathname.indexOf("/yts/jsbin/");
|
|
93
|
+
if (jsbinIndex > -1) {
|
|
94
|
+
proxyUrl.pathname = url.pathname.substring(jsbinIndex);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
proxyUrl.searchParams.set("__host", url.host);
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
let sessionId = null;
|
|
102
|
+
if (typeof process !== "undefined" && process.env && process.env.PROXY_SESSION_ID) {
|
|
103
|
+
sessionId = process.env.PROXY_SESSION_ID;
|
|
104
|
+
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
105
|
+
sessionId = window.localStorage.getItem("tube-ts-session-id");
|
|
106
|
+
}
|
|
107
|
+
if (sessionId) {
|
|
108
|
+
proxyUrl.searchParams.set("session", sessionId);
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
const headersObj = {};
|
|
113
|
+
headers.forEach((value, key) => {
|
|
114
|
+
headersObj[key] = value;
|
|
115
|
+
});
|
|
116
|
+
proxyUrl.searchParams.set("__headers", JSON.stringify(headersObj));
|
|
117
|
+
const requestInit = {
|
|
118
|
+
...init,
|
|
119
|
+
headers
|
|
120
|
+
};
|
|
121
|
+
if (input instanceof Request) {
|
|
122
|
+
if (!requestInit.method) {
|
|
123
|
+
requestInit.method = input.method;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const response = await fetch(proxyUrl, requestInit);
|
|
127
|
+
const contentType = response.headers.get("content-type");
|
|
128
|
+
if (contentType && contentType.includes("text/html")) {
|
|
129
|
+
const text = await response.text();
|
|
130
|
+
throw new Error(`Proxy returned HTML (likely error page): ${response.status} ${response.statusText} - ${text.substring(0, 100)}`);
|
|
131
|
+
}
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/ShakaPlayerAdapter.ts
|
|
136
|
+
var ShakaPlayerAdapter = class {
|
|
137
|
+
player = null;
|
|
138
|
+
requestMetadataManager;
|
|
139
|
+
cacheManager;
|
|
140
|
+
abortController;
|
|
141
|
+
requestFilter;
|
|
142
|
+
responseFilter;
|
|
143
|
+
initialize(player, requestMetadataManager, cacheManager) {
|
|
144
|
+
this.player = player;
|
|
145
|
+
this.requestMetadataManager = requestMetadataManager;
|
|
146
|
+
this.cacheManager = cacheManager;
|
|
147
|
+
const networkingEngine = shaka2.net.NetworkingEngine;
|
|
148
|
+
const schemes = ["http", "https"];
|
|
149
|
+
if (!shaka2.net.HttpFetchPlugin.isSupported())
|
|
150
|
+
throw new Error("The Fetch API is not supported in this browser.");
|
|
151
|
+
schemes.forEach((scheme) => {
|
|
152
|
+
networkingEngine.registerScheme(
|
|
153
|
+
scheme,
|
|
154
|
+
this.parseRequest.bind(this),
|
|
155
|
+
networkingEngine.PluginPriority.PREFERRED
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
parseRequest(uri, request, requestType, progressUpdated, headersReceived, config) {
|
|
160
|
+
const headers = new Headers();
|
|
161
|
+
asMap(request.headers).forEach((value, key) => {
|
|
162
|
+
headers.append(key, value);
|
|
163
|
+
});
|
|
164
|
+
const controller = new AbortController();
|
|
165
|
+
this.abortController = controller;
|
|
166
|
+
const init = {
|
|
167
|
+
body: request.body || void 0,
|
|
168
|
+
headers,
|
|
169
|
+
method: request.method,
|
|
170
|
+
signal: this.abortController.signal,
|
|
171
|
+
credentials: request.allowCrossSiteCredentials ? "include" : void 0
|
|
172
|
+
};
|
|
173
|
+
const abortStatus = { canceled: false, timedOut: false };
|
|
174
|
+
const minBytes = config.minBytesForProgressEvents || 0;
|
|
175
|
+
const pendingRequest = this.request(uri, request, requestType, init, controller, abortStatus, progressUpdated, headersReceived, minBytes);
|
|
176
|
+
const operation = new shaka2.util.AbortableOperation(
|
|
177
|
+
pendingRequest,
|
|
178
|
+
() => {
|
|
179
|
+
abortStatus.canceled = true;
|
|
180
|
+
controller.abort();
|
|
181
|
+
return Promise.resolve();
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
const timeoutMs = request.retryParameters.timeout;
|
|
185
|
+
if (timeoutMs) {
|
|
186
|
+
const timer = new shaka2.util.Timer(() => {
|
|
187
|
+
abortStatus.timedOut = true;
|
|
188
|
+
controller.abort();
|
|
189
|
+
console.warn("[ShakaPlayerAdapter]", "Request aborted due to timeout:", uri, requestType);
|
|
190
|
+
});
|
|
191
|
+
timer.tickAfter(timeoutMs / 1e3);
|
|
192
|
+
operation.finally(() => timer.stop());
|
|
193
|
+
}
|
|
194
|
+
return operation;
|
|
195
|
+
}
|
|
196
|
+
async handleCachedRequest(requestMetadata, uri, request, progressUpdated, headersReceived, requestType) {
|
|
197
|
+
if (!requestMetadata.byteRange || !this.cacheManager) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const segmentKey = FormatKeyUtils.createSegmentCacheKeyFromMetadata(requestMetadata);
|
|
201
|
+
let arrayBuffer = (requestMetadata.isInit ? this.cacheManager.getInitSegment(segmentKey) : this.cacheManager.getSegment(segmentKey))?.buffer;
|
|
202
|
+
if (!arrayBuffer) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
if (requestMetadata.isInit) {
|
|
206
|
+
arrayBuffer = arrayBuffer.slice(
|
|
207
|
+
requestMetadata.byteRange.start,
|
|
208
|
+
requestMetadata.byteRange.end + 1
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const headers = {
|
|
212
|
+
"content-type": requestMetadata.format?.mimeType?.split(";")[0] || "",
|
|
213
|
+
"content-length": arrayBuffer.byteLength.toString(),
|
|
214
|
+
"x-shaka-from-cache": "true"
|
|
215
|
+
};
|
|
216
|
+
headersReceived(headers);
|
|
217
|
+
progressUpdated(0, arrayBuffer.byteLength, 0);
|
|
218
|
+
return makeResponse(headers, arrayBuffer, 200, uri, uri, request, requestType);
|
|
219
|
+
}
|
|
220
|
+
async handleUmpResponse(response, requestMetadata, uri, request, requestType, progressUpdated, abortController, minBytes) {
|
|
221
|
+
let lastTime = Date.now();
|
|
222
|
+
const sabrUmpReader = new SabrUmpProcessor(requestMetadata, this.cacheManager);
|
|
223
|
+
const checkResultIntegrity = (result) => {
|
|
224
|
+
if (!result.data && ((!!requestMetadata.error || requestMetadata.streamInfo?.streamProtectionStatus?.status === 3) && !requestMetadata.streamInfo?.sabrContextUpdate)) {
|
|
225
|
+
throw createRecoverableError("Server streaming error", requestMetadata);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const shouldReturnEmptyResponse = () => {
|
|
229
|
+
return requestMetadata.isSABR && (requestMetadata.streamInfo?.redirect || requestMetadata.streamInfo?.sabrContextUpdate);
|
|
230
|
+
};
|
|
231
|
+
if (!response.body) {
|
|
232
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
233
|
+
const currentTime = Date.now();
|
|
234
|
+
progressUpdated(currentTime - lastTime, arrayBuffer.byteLength, 0);
|
|
235
|
+
const result = await sabrUmpReader.processChunk(new Uint8Array(arrayBuffer));
|
|
236
|
+
if (result) {
|
|
237
|
+
checkResultIntegrity(result);
|
|
238
|
+
return this.createShakaResponse({ uri, request, requestType, response, arrayBuffer: result.data });
|
|
239
|
+
}
|
|
240
|
+
if (shouldReturnEmptyResponse()) {
|
|
241
|
+
return this.createShakaResponse({ uri, request, requestType, response, arrayBuffer: void 0 });
|
|
242
|
+
}
|
|
243
|
+
throw createRecoverableError("Empty response with no redirect information", requestMetadata);
|
|
244
|
+
} else {
|
|
245
|
+
const reader = response.body.getReader();
|
|
246
|
+
let loaded = 0;
|
|
247
|
+
let lastLoaded = 0;
|
|
248
|
+
let contentLength;
|
|
249
|
+
while (!abortController.signal.aborted) {
|
|
250
|
+
let readObj;
|
|
251
|
+
try {
|
|
252
|
+
readObj = await reader.read();
|
|
253
|
+
} catch {
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
const { value, done } = readObj;
|
|
257
|
+
if (done) {
|
|
258
|
+
if (shouldReturnEmptyResponse()) {
|
|
259
|
+
return this.createShakaResponse({ uri, request, requestType, response, arrayBuffer: void 0 });
|
|
260
|
+
}
|
|
261
|
+
throw createRecoverableError("Empty response with no redirect information", requestMetadata);
|
|
262
|
+
}
|
|
263
|
+
const result = await sabrUmpReader.processChunk(value);
|
|
264
|
+
const segmentInfo = sabrUmpReader.getSegmentInfo();
|
|
265
|
+
if (segmentInfo) {
|
|
266
|
+
if (!contentLength) {
|
|
267
|
+
contentLength = segmentInfo.mediaHeader.contentLength?.toString();
|
|
268
|
+
}
|
|
269
|
+
loaded += segmentInfo.lastChunkSize || 0;
|
|
270
|
+
segmentInfo.lastChunkSize = 0;
|
|
271
|
+
}
|
|
272
|
+
const currentTime = Date.now();
|
|
273
|
+
const chunkSize = loaded - lastLoaded;
|
|
274
|
+
if (currentTime - lastTime > 100 && chunkSize >= minBytes || result) {
|
|
275
|
+
if (result) checkResultIntegrity(result);
|
|
276
|
+
if (contentLength) {
|
|
277
|
+
const numBytesRemaining = result ? 0 : parseInt(contentLength) - loaded;
|
|
278
|
+
try {
|
|
279
|
+
progressUpdated(currentTime - lastTime, chunkSize, numBytesRemaining);
|
|
280
|
+
} catch {
|
|
281
|
+
} finally {
|
|
282
|
+
lastLoaded = loaded;
|
|
283
|
+
lastTime = currentTime;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (result) {
|
|
288
|
+
abortController.abort();
|
|
289
|
+
return this.createShakaResponse({ uri, request, requestType, response, arrayBuffer: result.data });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
throw createRecoverableError("UMP stream processing was aborted but did not produce a result.", requestMetadata);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async request(uri, request, requestType, init, abortController, abortStatus, progressUpdated, headersReceived, minBytes) {
|
|
296
|
+
try {
|
|
297
|
+
const requestMetadata = this.requestMetadataManager?.getRequestMetadata(uri);
|
|
298
|
+
if (requestMetadata) {
|
|
299
|
+
const cachedResponse = await this.handleCachedRequest(requestMetadata, uri, request, progressUpdated, headersReceived, requestType);
|
|
300
|
+
if (cachedResponse) {
|
|
301
|
+
return cachedResponse;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const fetchFn = fetchFunction;
|
|
305
|
+
const response = await fetchFn(uri, init);
|
|
306
|
+
headersReceived(headersToGenericObject(response.headers));
|
|
307
|
+
if (requestMetadata && init.method !== "HEAD" && response.headers.get("content-type") === "application/vnd.yt-ump") {
|
|
308
|
+
return this.handleUmpResponse(response, requestMetadata, uri, request, requestType, progressUpdated, abortController, minBytes);
|
|
309
|
+
}
|
|
310
|
+
const lastTime = Date.now();
|
|
311
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
312
|
+
const currentTime = Date.now();
|
|
313
|
+
progressUpdated(currentTime - lastTime, arrayBuffer.byteLength, 0);
|
|
314
|
+
return this.createShakaResponse({
|
|
315
|
+
uri,
|
|
316
|
+
request,
|
|
317
|
+
requestType,
|
|
318
|
+
response,
|
|
319
|
+
arrayBuffer
|
|
320
|
+
});
|
|
321
|
+
} catch (error) {
|
|
322
|
+
if (abortStatus.canceled) {
|
|
323
|
+
throw new shaka2.util.Error(
|
|
324
|
+
shaka2.util.Error.Severity.RECOVERABLE,
|
|
325
|
+
shaka2.util.Error.Category.NETWORK,
|
|
326
|
+
shaka2.util.Error.Code.OPERATION_ABORTED,
|
|
327
|
+
uri,
|
|
328
|
+
requestType
|
|
329
|
+
);
|
|
330
|
+
} else if (abortStatus.timedOut) {
|
|
331
|
+
throw new shaka2.util.Error(
|
|
332
|
+
shaka2.util.Error.Severity.RECOVERABLE,
|
|
333
|
+
shaka2.util.Error.Category.NETWORK,
|
|
334
|
+
shaka2.util.Error.Code.TIMEOUT,
|
|
335
|
+
uri,
|
|
336
|
+
requestType
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
throw new shaka2.util.Error(
|
|
340
|
+
shaka2.util.Error.Severity.RECOVERABLE,
|
|
341
|
+
shaka2.util.Error.Category.NETWORK,
|
|
342
|
+
shaka2.util.Error.Code.HTTP_ERROR,
|
|
343
|
+
uri,
|
|
344
|
+
error,
|
|
345
|
+
requestType
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
checkPlayerStatus() {
|
|
350
|
+
if (!this.player) {
|
|
351
|
+
throw new Error("Player not initialized");
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
getPlayerTime() {
|
|
355
|
+
this.checkPlayerStatus();
|
|
356
|
+
return this.player.getMediaElement()?.currentTime || 0;
|
|
357
|
+
}
|
|
358
|
+
getPlaybackRate() {
|
|
359
|
+
this.checkPlayerStatus();
|
|
360
|
+
return this.player.getPlaybackRate();
|
|
361
|
+
}
|
|
362
|
+
getBandwidthEstimate() {
|
|
363
|
+
this.checkPlayerStatus();
|
|
364
|
+
return this.player.getStats().estimatedBandwidth;
|
|
365
|
+
}
|
|
366
|
+
getActiveTrackFormats(activeFormat, sabrFormats) {
|
|
367
|
+
this.checkPlayerStatus();
|
|
368
|
+
const activeVariant = this.player.getVariantTracks().find(
|
|
369
|
+
(track) => FormatKeyUtils.getUniqueFormatId(activeFormat) === (activeFormat.width ? track.originalVideoId : track.originalAudioId)
|
|
370
|
+
);
|
|
371
|
+
if (!activeVariant) {
|
|
372
|
+
return { videoFormat: void 0, audioFormat: void 0 };
|
|
373
|
+
}
|
|
374
|
+
const formatMap = new Map(sabrFormats.map((format) => [FormatKeyUtils.getUniqueFormatId(format), format]));
|
|
375
|
+
return {
|
|
376
|
+
videoFormat: activeVariant.originalVideoId ? formatMap.get(activeVariant.originalVideoId) : void 0,
|
|
377
|
+
audioFormat: activeVariant.originalAudioId ? formatMap.get(activeVariant.originalAudioId) : void 0
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
registerRequestInterceptor(interceptor) {
|
|
381
|
+
this.checkPlayerStatus();
|
|
382
|
+
const networkingEngine = this.player.getNetworkingEngine();
|
|
383
|
+
if (!networkingEngine)
|
|
384
|
+
return;
|
|
385
|
+
this.requestFilter = async (type, request, context) => {
|
|
386
|
+
if (type !== shaka2.net.NetworkingEngine.RequestType.SEGMENT || !isGoogleVideoURL(request.uris[0])) return;
|
|
387
|
+
const modifiedRequest = await interceptor({
|
|
388
|
+
headers: request.headers,
|
|
389
|
+
url: request.uris[0],
|
|
390
|
+
method: request.method,
|
|
391
|
+
segment: {
|
|
392
|
+
getStartTime: () => context?.segment?.getStartTime() ?? null,
|
|
393
|
+
isInit: () => !context?.segment
|
|
394
|
+
},
|
|
395
|
+
body: request.body
|
|
396
|
+
});
|
|
397
|
+
if (modifiedRequest) {
|
|
398
|
+
request.uris = modifiedRequest.url ? [modifiedRequest.url] : request.uris;
|
|
399
|
+
request.method = modifiedRequest.method || request.method;
|
|
400
|
+
request.headers = modifiedRequest.headers || request.headers;
|
|
401
|
+
request.body = modifiedRequest.body || request.body;
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
networkingEngine.registerRequestFilter(this.requestFilter);
|
|
405
|
+
}
|
|
406
|
+
registerResponseInterceptor(interceptor) {
|
|
407
|
+
this.checkPlayerStatus();
|
|
408
|
+
const networkingEngine = this.player.getNetworkingEngine();
|
|
409
|
+
if (!networkingEngine) return;
|
|
410
|
+
this.responseFilter = async (type, response, context) => {
|
|
411
|
+
if (type !== shaka2.net.NetworkingEngine.RequestType.SEGMENT || !isGoogleVideoURL(response.uri)) return;
|
|
412
|
+
const modifiedResponse = await interceptor({
|
|
413
|
+
url: response.originalRequest.uris[0],
|
|
414
|
+
method: response.originalRequest.method,
|
|
415
|
+
headers: response.headers,
|
|
416
|
+
data: response.data,
|
|
417
|
+
makeRequest: async (url, headers) => {
|
|
418
|
+
const retryParameters = this.player.getConfiguration().streaming.retryParameters;
|
|
419
|
+
const redirectRequest = shaka2.net.NetworkingEngine.makeRequest([url], retryParameters);
|
|
420
|
+
Object.assign(redirectRequest.headers, headers);
|
|
421
|
+
const requestOperation = networkingEngine.request(type, redirectRequest, context);
|
|
422
|
+
const redirectResponse = await requestOperation.promise;
|
|
423
|
+
return {
|
|
424
|
+
url: redirectResponse.uri,
|
|
425
|
+
method: redirectResponse.originalRequest.method,
|
|
426
|
+
headers: redirectResponse.headers,
|
|
427
|
+
data: redirectResponse.data
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
if (modifiedResponse) {
|
|
432
|
+
response.data = modifiedResponse.data ?? response.data;
|
|
433
|
+
Object.assign(response.headers, modifiedResponse.headers);
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
networkingEngine.registerResponseFilter(this.responseFilter);
|
|
437
|
+
}
|
|
438
|
+
createShakaResponse(args) {
|
|
439
|
+
return makeResponse(
|
|
440
|
+
headersToGenericObject(args.response.headers),
|
|
441
|
+
args.arrayBuffer || new ArrayBuffer(0),
|
|
442
|
+
args.response.status,
|
|
443
|
+
args.uri,
|
|
444
|
+
args.response.url,
|
|
445
|
+
args.request,
|
|
446
|
+
args.requestType
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
dispose() {
|
|
450
|
+
if (this.abortController) {
|
|
451
|
+
this.abortController.abort();
|
|
452
|
+
this.abortController = void 0;
|
|
453
|
+
}
|
|
454
|
+
if (this.player) {
|
|
455
|
+
const networkingEngine = this.player.getNetworkingEngine();
|
|
456
|
+
if (networkingEngine && this.requestFilter && this.responseFilter) {
|
|
457
|
+
networkingEngine.unregisterRequestFilter(this.requestFilter);
|
|
458
|
+
networkingEngine.unregisterResponseFilter(this.responseFilter);
|
|
459
|
+
}
|
|
460
|
+
shaka2.net.NetworkingEngine.unregisterScheme("http");
|
|
461
|
+
shaka2.net.NetworkingEngine.unregisterScheme("https");
|
|
462
|
+
this.player = null;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
var BotguardService = class {
|
|
467
|
+
waaRequestKey = "O43z0dpjhgX20SCx4KAo";
|
|
468
|
+
botguardClient;
|
|
469
|
+
initializationPromise = null;
|
|
470
|
+
integrityTokenBasedMinter;
|
|
471
|
+
bgChallenge;
|
|
472
|
+
async init() {
|
|
473
|
+
if (this.initializationPromise) {
|
|
474
|
+
return await this.initializationPromise;
|
|
475
|
+
}
|
|
476
|
+
return this.setup();
|
|
477
|
+
}
|
|
478
|
+
async setup() {
|
|
479
|
+
if (this.initializationPromise)
|
|
480
|
+
return await this.initializationPromise;
|
|
481
|
+
this.initializationPromise = this._initBotguard();
|
|
482
|
+
try {
|
|
483
|
+
this.botguardClient = await this.initializationPromise;
|
|
484
|
+
return this.botguardClient;
|
|
485
|
+
} finally {
|
|
486
|
+
this.initializationPromise = null;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async _initBotguard() {
|
|
490
|
+
const challengeResponse = await fetchFunction(buildURL("Create", true), {
|
|
491
|
+
method: "POST",
|
|
492
|
+
headers: {
|
|
493
|
+
"content-type": "application/json+protobuf",
|
|
494
|
+
"x-goog-api-key": GOOG_API_KEY,
|
|
495
|
+
"x-user-agent": "grpc-web-javascript/0.1"
|
|
496
|
+
},
|
|
497
|
+
body: JSON.stringify([this.waaRequestKey])
|
|
498
|
+
});
|
|
499
|
+
const challengeResponseData = await challengeResponse.json();
|
|
500
|
+
this.bgChallenge = BG.Challenge.parseChallengeData(challengeResponseData);
|
|
501
|
+
if (!this.bgChallenge)
|
|
502
|
+
return;
|
|
503
|
+
const interpreterJavascript = this.bgChallenge.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue;
|
|
504
|
+
if (!interpreterJavascript) {
|
|
505
|
+
console.error("[BotguardService]", "Could not get interpreter javascript. Interpreter Hash:", this.bgChallenge.interpreterHash);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (!document.getElementById(this.bgChallenge.interpreterHash)) {
|
|
509
|
+
const script = document.createElement("script");
|
|
510
|
+
script.type = "text/javascript";
|
|
511
|
+
script.id = this.bgChallenge.interpreterHash;
|
|
512
|
+
script.textContent = interpreterJavascript;
|
|
513
|
+
document.head.appendChild(script);
|
|
514
|
+
}
|
|
515
|
+
this.botguardClient = await BG.BotGuardClient.create({
|
|
516
|
+
globalObj: globalThis,
|
|
517
|
+
globalName: this.bgChallenge.globalName,
|
|
518
|
+
program: this.bgChallenge.program
|
|
519
|
+
});
|
|
520
|
+
if (this.bgChallenge) {
|
|
521
|
+
const webPoSignalOutput = [];
|
|
522
|
+
const botguardResponse = await this.botguardClient.snapshot({ webPoSignalOutput });
|
|
523
|
+
const integrityTokenResponse = await fetchFunction(buildURL("GenerateIT", true), {
|
|
524
|
+
method: "POST",
|
|
525
|
+
headers: {
|
|
526
|
+
"content-type": "application/json+protobuf",
|
|
527
|
+
"x-goog-api-key": GOOG_API_KEY,
|
|
528
|
+
"x-user-agent": "grpc-web-javacript/0.1"
|
|
529
|
+
},
|
|
530
|
+
body: JSON.stringify([this.waaRequestKey, botguardResponse])
|
|
531
|
+
});
|
|
532
|
+
const integrityTokenResponseData = await integrityTokenResponse.json();
|
|
533
|
+
const integrityToken = integrityTokenResponseData[0];
|
|
534
|
+
if (!integrityToken) {
|
|
535
|
+
console.error("[BotguardService]", "Could not get integrity token. Interpreter Hash:", this.bgChallenge.interpreterHash);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
this.integrityTokenBasedMinter = await BG.WebPoMinter.create({ integrityToken }, webPoSignalOutput);
|
|
539
|
+
}
|
|
540
|
+
return this.botguardClient;
|
|
541
|
+
}
|
|
542
|
+
mintColdStartToken(contentBinding) {
|
|
543
|
+
return BG.PoToken.generateColdStartToken(contentBinding);
|
|
544
|
+
}
|
|
545
|
+
isInitialized() {
|
|
546
|
+
return !!this.botguardClient && !!this.integrityTokenBasedMinter;
|
|
547
|
+
}
|
|
548
|
+
dispose() {
|
|
549
|
+
if (this.botguardClient && this.bgChallenge) {
|
|
550
|
+
this.botguardClient.shutdown();
|
|
551
|
+
this.botguardClient = void 0;
|
|
552
|
+
this.integrityTokenBasedMinter = void 0;
|
|
553
|
+
const script = document.getElementById(this.bgChallenge.interpreterHash);
|
|
554
|
+
if (script) {
|
|
555
|
+
script.remove();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async reinit() {
|
|
560
|
+
if (this.initializationPromise)
|
|
561
|
+
return this.initializationPromise;
|
|
562
|
+
this.dispose();
|
|
563
|
+
return this.setup();
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
var botguardService = new BotguardService();
|
|
567
|
+
Platform.shim.eval = async (data, env) => {
|
|
568
|
+
const properties = [];
|
|
569
|
+
if (data.output && data.output.length < 1e3) {
|
|
570
|
+
console.log("[TubePlayer] Short code received:", data.output);
|
|
571
|
+
} else {
|
|
572
|
+
console.log(`[TubePlayer] Code received, length: ${data.output?.length}`);
|
|
573
|
+
}
|
|
574
|
+
if (env.n) {
|
|
575
|
+
if (data.exported?.includes("nFunction")) {
|
|
576
|
+
properties.push(`n: exportedVars.nFunction(${JSON.stringify(String(env.n))})`);
|
|
577
|
+
} else {
|
|
578
|
+
console.warn("[TubePlayer] nFunction not exported. Available exports:", data.exported);
|
|
579
|
+
throw new Error(`[TubePlayer] nFunction not exported. Available: ${data.exported?.join(", ")}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (env.sig) {
|
|
583
|
+
if (data.exported?.includes("sigFunction")) {
|
|
584
|
+
properties.push(`sig: exportedVars.sigFunction(${JSON.stringify(String(env.sig))})`);
|
|
585
|
+
} else {
|
|
586
|
+
console.warn("[TubePlayer] sigFunction not exported, skipping sig transformation");
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const code = `${data.output}
|
|
590
|
+
return { ${properties.join(", ")} }`;
|
|
591
|
+
try {
|
|
592
|
+
return new Function(code)();
|
|
593
|
+
} catch (e) {
|
|
594
|
+
console.error("[TubePlayer] Shim evaluation failed:", e);
|
|
595
|
+
console.error("[TubePlayer] Code preview:", code.substring(0, 200));
|
|
596
|
+
throw e;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
var TubePlayer = class {
|
|
600
|
+
player;
|
|
601
|
+
ui;
|
|
602
|
+
sabrAdapter;
|
|
603
|
+
innertube;
|
|
604
|
+
playbackWebPoTokenContentBinding;
|
|
605
|
+
playbackWebPoTokenCreationLock = false;
|
|
606
|
+
playbackWebPoToken;
|
|
607
|
+
coldStartToken;
|
|
608
|
+
container;
|
|
609
|
+
videoElement;
|
|
610
|
+
constructor(containerId) {
|
|
611
|
+
const container = document.getElementById(containerId);
|
|
612
|
+
if (!container) throw new Error(`Container element with ID ${containerId} not found.`);
|
|
613
|
+
this.container = container;
|
|
614
|
+
this.videoElement = document.createElement("video");
|
|
615
|
+
this.videoElement.style.width = "100%";
|
|
616
|
+
this.videoElement.style.height = "100%";
|
|
617
|
+
this.videoElement.controls = false;
|
|
618
|
+
this.container.appendChild(this.videoElement);
|
|
619
|
+
shaka2.polyfill.installAll();
|
|
620
|
+
if (!shaka2.Player.isBrowserSupported()) {
|
|
621
|
+
console.warn("Shaka Player is not supported on this browser.");
|
|
622
|
+
}
|
|
623
|
+
this.player = new shaka2.Player();
|
|
624
|
+
this.ui = new shaka2.ui.Overlay(this.player, this.container, this.videoElement);
|
|
625
|
+
}
|
|
626
|
+
// Store initialization options for retry logic
|
|
627
|
+
initOptions;
|
|
628
|
+
async initialize(options) {
|
|
629
|
+
this.initOptions = options;
|
|
630
|
+
let retryCount = 0;
|
|
631
|
+
const maxRetries = 3;
|
|
632
|
+
const useProxy = options?.useProxy ?? true;
|
|
633
|
+
const enableCache = options?.cache ?? true;
|
|
634
|
+
while (retryCount < maxRetries) {
|
|
635
|
+
try {
|
|
636
|
+
const fetchWrapper = async (input, init) => {
|
|
637
|
+
let urlStr = typeof input === "string" ? input : input instanceof Request ? input.url : input.toString();
|
|
638
|
+
const urlObj = new URL(urlStr);
|
|
639
|
+
if (urlStr.includes("player") || urlStr.includes("base.js")) {
|
|
640
|
+
urlObj.searchParams.set("t", String(Date.now()));
|
|
641
|
+
console.log("[TubePlayer] Fetching player script from:", urlObj.toString());
|
|
642
|
+
let modifiedInit = init;
|
|
643
|
+
if (input instanceof Request) {
|
|
644
|
+
modifiedInit = {
|
|
645
|
+
method: input.method,
|
|
646
|
+
...init
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
if (!useProxy) return fetch(urlObj.toString(), modifiedInit);
|
|
650
|
+
return fetchFunction(urlObj.toString(), modifiedInit);
|
|
651
|
+
}
|
|
652
|
+
if (!useProxy) {
|
|
653
|
+
return fetch(input, init);
|
|
654
|
+
}
|
|
655
|
+
return fetchFunction(input, init);
|
|
656
|
+
};
|
|
657
|
+
this.innertube = await Innertube.create({
|
|
658
|
+
// Create cache: persistent if enabled AND first try.
|
|
659
|
+
// If we are retrying internally here (retryCount > 0), we disable it.
|
|
660
|
+
// If enableCache is passed as false (from loadVideo retry), we disable it.
|
|
661
|
+
cache: new UniversalCache(enableCache && retryCount === 0),
|
|
662
|
+
fetch: fetchWrapper
|
|
663
|
+
});
|
|
664
|
+
break;
|
|
665
|
+
} catch (error) {
|
|
666
|
+
console.error("Innertube init failed", error);
|
|
667
|
+
retryCount++;
|
|
668
|
+
if (retryCount >= maxRetries) throw error;
|
|
669
|
+
console.log(`Retrying Innertube init (attempt ${retryCount + 1})...`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
await botguardService.init();
|
|
673
|
+
this.player.configure({
|
|
674
|
+
abr: { enabled: true },
|
|
675
|
+
streaming: {
|
|
676
|
+
bufferingGoal: 120,
|
|
677
|
+
rebufferingGoal: 2
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
await this.player.attach(this.videoElement);
|
|
681
|
+
this.ui.configure({
|
|
682
|
+
addBigPlayButton: false,
|
|
683
|
+
overflowMenuButtons: [
|
|
684
|
+
"captions",
|
|
685
|
+
"quality",
|
|
686
|
+
"language",
|
|
687
|
+
"chapter",
|
|
688
|
+
"picture_in_picture",
|
|
689
|
+
"playback_rate",
|
|
690
|
+
"loop",
|
|
691
|
+
"recenter_vr",
|
|
692
|
+
"toggle_stereoscopic",
|
|
693
|
+
"save_video_frame"
|
|
694
|
+
],
|
|
695
|
+
customContextMenu: true
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
async loadVideo(videoId) {
|
|
699
|
+
if (!this.innertube) {
|
|
700
|
+
throw new Error("TubePlayer not initialized. Call initialize() first.");
|
|
701
|
+
}
|
|
702
|
+
if (!videoId) {
|
|
703
|
+
throw new Error("Please enter a video ID.");
|
|
704
|
+
}
|
|
705
|
+
this.playbackWebPoToken = void 0;
|
|
706
|
+
this.playbackWebPoTokenContentBinding = videoId;
|
|
707
|
+
try {
|
|
708
|
+
await this.player.unload();
|
|
709
|
+
if (this.sabrAdapter) {
|
|
710
|
+
this.sabrAdapter.dispose();
|
|
711
|
+
}
|
|
712
|
+
const playerResponse = await this.innertube.actions.execute("/player", {
|
|
713
|
+
videoId,
|
|
714
|
+
contentCheckOk: true,
|
|
715
|
+
racyCheckOk: true,
|
|
716
|
+
playbackContext: {
|
|
717
|
+
adPlaybackContext: {
|
|
718
|
+
pyv: true
|
|
719
|
+
},
|
|
720
|
+
contentPlaybackContext: {
|
|
721
|
+
signatureTimestamp: this.innertube.session.player?.signature_timestamp
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
const cpn = Utils.generateRandomString(16);
|
|
726
|
+
const videoInfo = new YT.VideoInfo([playerResponse], this.innertube.actions, cpn);
|
|
727
|
+
if (videoInfo.playability_status?.status !== "OK") {
|
|
728
|
+
throw new Error(`Cannot play video: ${videoInfo.playability_status?.reason}`);
|
|
729
|
+
}
|
|
730
|
+
const isLive = videoInfo.basic_info.is_live;
|
|
731
|
+
const isPostLiveDVR = !!videoInfo.basic_info.is_post_live_dvr || videoInfo.basic_info.is_live_content && !!(videoInfo.streaming_data?.dash_manifest_url || videoInfo.streaming_data?.hls_manifest_url);
|
|
732
|
+
this.sabrAdapter = new SabrStreamingAdapter({
|
|
733
|
+
playerAdapter: new ShakaPlayerAdapter(),
|
|
734
|
+
clientInfo: {
|
|
735
|
+
osName: this.innertube.session.context.client.osName,
|
|
736
|
+
osVersion: this.innertube.session.context.client.osVersion,
|
|
737
|
+
clientName: parseInt(Constants.CLIENT_NAME_IDS[this.innertube.session.context.client.clientName]),
|
|
738
|
+
clientVersion: this.innertube.session.context.client.clientVersion
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
this.sabrAdapter.onMintPoToken(async () => {
|
|
742
|
+
if (!this.playbackWebPoToken) {
|
|
743
|
+
if (isLive) {
|
|
744
|
+
await this.mintContentWebPO();
|
|
745
|
+
} else {
|
|
746
|
+
this.mintContentWebPO().then();
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return this.playbackWebPoToken || this.coldStartToken || "";
|
|
750
|
+
});
|
|
751
|
+
this.sabrAdapter.onReloadPlayerResponse(async (reloadContext) => {
|
|
752
|
+
const reloadedInfo = await this.innertube.actions.execute("/player", {
|
|
753
|
+
videoId,
|
|
754
|
+
contentCheckOk: true,
|
|
755
|
+
racyCheckOk: true,
|
|
756
|
+
playbackContext: {
|
|
757
|
+
adPlaybackContext: {
|
|
758
|
+
pyv: true
|
|
759
|
+
},
|
|
760
|
+
contentPlaybackContext: {
|
|
761
|
+
signatureTimestamp: this.innertube.session.player?.signature_timestamp
|
|
762
|
+
},
|
|
763
|
+
reloadPlaybackContext: reloadContext
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
const parsedInfo = new YT.VideoInfo([reloadedInfo], this.innertube.actions, cpn);
|
|
767
|
+
this.sabrAdapter.setStreamingURL(await this.innertube.session.player.decipher(parsedInfo.streaming_data?.server_abr_streaming_url));
|
|
768
|
+
this.sabrAdapter.setUstreamerConfig(videoInfo.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config);
|
|
769
|
+
});
|
|
770
|
+
this.sabrAdapter.attach(this.player);
|
|
771
|
+
if (videoInfo.streaming_data && !isPostLiveDVR && !isLive) {
|
|
772
|
+
this.sabrAdapter.setStreamingURL(await this.innertube.session.player.decipher(videoInfo.streaming_data?.server_abr_streaming_url));
|
|
773
|
+
this.sabrAdapter.setUstreamerConfig(videoInfo.player_config?.media_common_config.media_ustreamer_request_config?.video_playback_ustreamer_config);
|
|
774
|
+
this.sabrAdapter.setServerAbrFormats(videoInfo.streaming_data.adaptive_formats.map(buildSabrFormat));
|
|
775
|
+
}
|
|
776
|
+
let manifestUri;
|
|
777
|
+
if (videoInfo.streaming_data) {
|
|
778
|
+
if (isLive) {
|
|
779
|
+
manifestUri = videoInfo.streaming_data.dash_manifest_url ? `${videoInfo.streaming_data.dash_manifest_url}/mpd_version/7` : videoInfo.streaming_data.hls_manifest_url;
|
|
780
|
+
} else if (isPostLiveDVR) {
|
|
781
|
+
manifestUri = videoInfo.streaming_data.hls_manifest_url || `${videoInfo.streaming_data.dash_manifest_url}/mpd_version/7`;
|
|
782
|
+
} else {
|
|
783
|
+
manifestUri = `data:application/dash+xml;base64,${btoa(await videoInfo.toDash({
|
|
784
|
+
manifest_options: {
|
|
785
|
+
is_sabr: true,
|
|
786
|
+
captions_format: "vtt",
|
|
787
|
+
include_thumbnails: false
|
|
788
|
+
}
|
|
789
|
+
}))}`;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (!manifestUri)
|
|
793
|
+
throw new Error("Could not find a valid manifest URI.");
|
|
794
|
+
await this.player.load(manifestUri);
|
|
795
|
+
return videoInfo.basic_info;
|
|
796
|
+
} catch (e) {
|
|
797
|
+
console.error("[TubePlayer]", "Error loading video:", e);
|
|
798
|
+
if (!this.isRetrying && this.initOptions) {
|
|
799
|
+
console.warn("[TubePlayer] Load failed. Retrying with cache disabled to fetch fresh player script...");
|
|
800
|
+
this.isRetrying = true;
|
|
801
|
+
try {
|
|
802
|
+
await this.initialize({ ...this.initOptions, cache: false });
|
|
803
|
+
return this.loadVideo(videoId);
|
|
804
|
+
} catch (retryError) {
|
|
805
|
+
console.error("[TubePlayer] Retry failed:", retryError);
|
|
806
|
+
} finally {
|
|
807
|
+
this.isRetrying = false;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
throw e;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
// Track retry state to prevent infinite loops
|
|
814
|
+
isRetrying = false;
|
|
815
|
+
async mintContentWebPO() {
|
|
816
|
+
if (!this.playbackWebPoTokenContentBinding || this.playbackWebPoTokenCreationLock) return;
|
|
817
|
+
this.playbackWebPoTokenCreationLock = true;
|
|
818
|
+
try {
|
|
819
|
+
this.coldStartToken = botguardService.mintColdStartToken(this.playbackWebPoTokenContentBinding);
|
|
820
|
+
if (!botguardService.isInitialized()) await botguardService.reinit();
|
|
821
|
+
if (botguardService.integrityTokenBasedMinter) {
|
|
822
|
+
this.playbackWebPoToken = await botguardService.integrityTokenBasedMinter.mintAsWebsafeString(decodeURIComponent(this.playbackWebPoTokenContentBinding));
|
|
823
|
+
}
|
|
824
|
+
} catch (err) {
|
|
825
|
+
console.error("[TubePlayer]", "Error minting WebPO token", err);
|
|
826
|
+
} finally {
|
|
827
|
+
this.playbackWebPoTokenCreationLock = false;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
destroy() {
|
|
831
|
+
this.player.destroy();
|
|
832
|
+
this.sabrAdapter?.dispose();
|
|
833
|
+
botguardService.dispose();
|
|
834
|
+
this.ui.destroy();
|
|
835
|
+
this.videoElement.remove();
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
export { ShakaPlayerAdapter, TubePlayer, asMap, botguardService, createRecoverableError, fetchFunction, headersToGenericObject, makeResponse };
|
|
840
|
+
//# sourceMappingURL=index.js.map
|
|
841
|
+
//# sourceMappingURL=index.js.map
|