YT: do not check player ID if actions are cached

This commit is contained in:
Rafał Dzięgiel
2021-03-15 13:07:12 +01:00
parent 06f8e5d259
commit 5b6141ee8c

View File

@@ -52,7 +52,8 @@ var YouTubeClient = GObject.registerClass({
debug(`obtaining YouTube video info: ${videoId}`); debug(`obtaining YouTube video info: ${videoId}`);
this.downloadingVideoId = videoId; this.downloadingVideoId = videoId;
const [info, isAborted] = await this._getInfoPromise(videoId).catch(debug); let [info, isAborted] = await this._getInfoPromise(videoId).catch(debug);
if(!info) { if(!info) {
if(isAborted) if(isAborted)
return reject(new Error('download aborted')); return reject(new Error('download aborted'));
@@ -84,68 +85,63 @@ var YouTubeClient = GObject.registerClass({
if(!info.streamingData.adaptiveFormats) if(!info.streamingData.adaptiveFormats)
info.streamingData.adaptiveFormats = []; info.streamingData.adaptiveFormats = [];
const isCipher = this._getIsCipher(info.streamingData); if(this._getIsCipher(info.streamingData)) {
if(isCipher) {
debug('video requires deciphering'); debug('video requires deciphering');
const embedUri = `https://www.youtube.com/embed/${videoId}`; /* Decipher actions do not change too often, so try
const [body, isAbortedBody] = * to reuse without triggering too many requests ban */
await this._downloadDataPromise(embedUri).catch(debug); let actions = this.cachedSig.actions;
if(isAbortedBody)
break;
/* We need matching info, so start from beginning */
if(!body)
continue;
const ytPath = body.match(/(?<=jsUrl\":\").*?(?=\")/gs)[0];
if(!ytPath) {
debug(new Error('could not find YouTube player URI'));
break;
}
const ytUri = `https://www.youtube.com${ytPath}`;
if(
/* check if site has "/" after ".com" */
ytUri[23] !== '/'
|| !Gst.Uri.is_valid(ytUri)
) {
debug(`misformed player URI: ${ytUri}`);
break;
}
debug(`found player URI: ${ytUri}`);
const ytId = ytPath.split('/').find(el => Misc.isHex(el));
let actions;
if(this.cachedSig.id === ytId) {
debug('reusing cached cipher actions');
actions = this.cachedSig.actions;
}
/* TODO: load cache from file */
if(!actions) { if(!actions) {
const [pBody, isAbortedPlayer] = const embedUri = `https://www.youtube.com/embed/${videoId}`;
await this._downloadDataPromise(ytUri).catch(debug); const [body, isAbortedBody] =
if(!pBody || isAbortedPlayer) { await this._downloadDataPromise(embedUri).catch(debug);
debug(new Error('could not download player body'));
if(isAbortedBody)
break;
if(!body)
continue;
const ytPath = body.match(/(?<=jsUrl\":\").*?(?=\")/gs)[0];
if(!ytPath) {
debug(new Error('could not find YouTube player URI'));
break; break;
} }
actions = YTDL.sig.extractActions(pBody); const ytUri = `https://www.youtube.com${ytPath}`;
this._createCacheFileAsync(ytId, actions); if(
} /* check if site has "/" after ".com" */
ytUri[23] !== '/'
|| !Gst.Uri.is_valid(ytUri)
) {
debug(`misformed player URI: ${ytUri}`);
break;
}
debug(`found player URI: ${ytUri}`);
if(!actions || !actions.length) { const ytId = ytPath.split('/').find(el => Misc.isHex(el));
debug(new Error('could not extract decipher actions'));
break;
}
debug('successfully obtained decipher actions');
if(this.cachedSig.id !== ytId) { /* TODO: load cache from file */
this.cachedSig.id = ytId;
this.cachedSig.actions = actions; if(!actions) {
debug('set current decipher actions for reuse'); const [pBody, isAbortedPlayer] =
await this._downloadDataPromise(ytUri).catch(debug);
if(!pBody || isAbortedPlayer) {
debug(new Error('could not download player body'));
break;
}
actions = YTDL.sig.extractActions(pBody);
this._createCacheFileAsync(ytId, actions);
}
if(!actions || !actions.length) {
debug(new Error('could not extract decipher actions'));
break;
}
debug('successfully obtained decipher actions');
if(this.cachedSig.id !== ytId) {
this.cachedSig.id = ytId;
this.cachedSig.actions = actions;
debug('remembered current decipher actions for reuse');
}
} }
const isDeciphered = this._decipherStreamingData( const isDeciphered = this._decipherStreamingData(
@@ -214,6 +210,8 @@ var YouTubeClient = GObject.registerClass({
debug(`got chunk of data, length: ${chunk.length}`); debug(`got chunk of data, length: ${chunk.length}`);
const chunkData = chunk.get_data(); const chunkData = chunk.get_data();
if(!chunkData) return;
data += (chunkData instanceof Uint8Array) data += (chunkData instanceof Uint8Array)
? ByteArray.toString(chunkData) ? ByteArray.toString(chunkData)
: chunkData; : chunkData;