|
1 | 1 | // ============================================================================= |
2 | | -// YouTube N-Parameter Solver — Remote Module v4 |
| 2 | +// YouTube N-Parameter Solver — Remote Module v4 -> v6 |
3 | 3 | // Hosted at: https://github.com/solarizeddev/firedown-solver |
4 | 4 | // The background.js shell fetches, caches, and executes this module. |
| 5 | +// |
| 6 | +// v6 additions: preprocessCipher() for signature cipher detection on base.js |
| 7 | +// The cipher function is obfuscated with the same string table + XOR pattern |
| 8 | +// as the n-param function, but lives in the 'main' player variant (base.js) |
| 9 | +// rather than the TV variant. |
5 | 10 | // ============================================================================= |
6 | | -var SOLVER_VERSION = 5; |
| 11 | +var SOLVER_VERSION = 6; |
7 | 12 |
|
8 | 13 | var SETUP_CODE = [ |
9 | 14 | 'if(typeof globalThis.XMLHttpRequest==="undefined"){globalThis.XMLHttpRequest={prototype:{}};}', |
10 | | - 'var window=Object.create(null);', |
11 | | - 'if(typeof URL==="undefined"){window.location={hash:"",host:"www.youtube.com",hostname:"www.youtube.com",', |
| 15 | + // base.js does 'var window=this' inside its IIFE, making window=globalThis. |
| 16 | + // Location must be on globalThis so the player can access window.location.hostname etc. |
| 17 | + 'globalThis.location={hash:"",host:"www.youtube.com",hostname:"www.youtube.com",', |
12 | 18 | 'href:"https://www.youtube.com/watch?v=yt-dlp-wins",origin:"https://www.youtube.com",password:"",', |
13 | | - 'pathname:"/watch",port:"",protocol:"https:",search:"?v=yt-dlp-wins",username:""};}', |
14 | | - 'else{window.location=new URL("https://www.youtube.com/watch?v=yt-dlp-wins");}', |
| 19 | + 'pathname:"/watch",port:"",protocol:"https:",search:"?v=yt-dlp-wins",username:"",', |
| 20 | + 'assign:function(){},replace:function(){},reload:function(){},toString:function(){return this.href;}};', |
| 21 | + // Also set window as a plain object for TV player (which does 'var window=Object.create(null)') |
| 22 | + 'var window=globalThis;', |
15 | 23 | 'if(typeof globalThis.document==="undefined"){globalThis.document=Object.create(null);}', |
16 | | - 'if(typeof globalThis.navigator==="undefined"){globalThis.navigator=Object.create(null);}', |
17 | | - 'if(typeof globalThis.self==="undefined"){globalThis.self=globalThis;}' |
| 24 | + 'if(typeof globalThis.navigator==="undefined"){globalThis.navigator={userAgent:""};}', |
| 25 | + 'if(typeof globalThis.self==="undefined"){globalThis.self=globalThis;}', |
| 26 | + 'if(typeof globalThis.addEventListener==="undefined"){globalThis.addEventListener=function(){};}', |
| 27 | + 'if(typeof globalThis.removeEventListener==="undefined"){globalThis.removeEventListener=function(){};}', |
| 28 | + 'if(typeof globalThis.setTimeout==="undefined"){globalThis.setTimeout=function(f){try{f();}catch(e){}};}', |
| 29 | + 'if(typeof globalThis.clearTimeout==="undefined"){globalThis.clearTimeout=function(){};}', |
| 30 | + 'if(typeof globalThis.setInterval==="undefined"){globalThis.setInterval=function(){return 0;};}', |
| 31 | + 'if(typeof globalThis.clearInterval==="undefined"){globalThis.clearInterval=function(){};}', |
| 32 | + 'if(typeof globalThis.requestAnimationFrame==="undefined"){globalThis.requestAnimationFrame=function(){};}', |
| 33 | + 'if(typeof globalThis.getComputedStyle==="undefined"){globalThis.getComputedStyle=function(){return{opacity:"1"};};}', |
18 | 34 | ].join('\n'); |
19 | 35 |
|
20 | 36 | /** |
@@ -170,8 +186,8 @@ function buildCachedProbe(funcName, r, p) { |
170 | 186 | } |
171 | 187 |
|
172 | 188 | /** |
173 | | - * Main entry point. |
174 | | - * @param {string} data - Full player.js source |
| 189 | + * Main entry point for n-parameter solving. |
| 190 | + * @param {string} data - Full player.js source (TV variant preferred) |
175 | 191 | * @param {object|null} solvedCache - {funcName, r, p} from previous solve |
176 | 192 | * @returns {string} - Code ready for Function("_result", code)(resultObj) |
177 | 193 | */ |
@@ -213,8 +229,6 @@ function preprocessPlayer(data, solvedCache) { |
213 | 229 | var modified = data; |
214 | 230 | if (candidates && candidates.length > 0) { |
215 | 231 | // Strategy 1: Comma injection inside var chains (most reliable) |
216 | | - // Injects ,_dfN=funcName right after function's closing } |
217 | | - // Only safe when string table delimiter is NOT } (brace matching works) |
218 | 232 | var tableDelimiter = null; |
219 | 233 | var delimMatch = data.substring(0, 2000).match(/\.split\((['"])(.)(\1)\)/); |
220 | 234 | if (delimMatch) tableDelimiter = delimMatch[2]; |
@@ -245,7 +259,6 @@ function preprocessPlayer(data, solvedCache) { |
245 | 259 | } |
246 | 260 |
|
247 | 261 | // Strategy 2: Chain-boundary injection (fallback for } delimiter players) |
248 | | - // Injects _dfN=funcName; after ;\nfunction or ;\nvar boundaries |
249 | 262 | var chainInjections = []; |
250 | 263 | for (var i = 0; i < candidates.length; i++) { |
251 | 264 | var c = candidates[i]; |
@@ -306,3 +319,310 @@ function preprocessPlayer(data, solvedCache) { |
306 | 319 |
|
307 | 320 | return SETUP_CODE + '\n' + modified + '\n;(function(){' + probeBody + '})();'; |
308 | 321 | } |
| 322 | + |
| 323 | +// ============================================================================= |
| 324 | +// SIGNATURE CIPHER DETECTION — v6 addition |
| 325 | +// Runs against the 'main' player variant (base.js) to find the cipher function. |
| 326 | +// |
| 327 | +// The cipher function is a multi-dispatch function (like KX) that contains a |
| 328 | +// branch activated by specific 'c' argument values where (c>>1&15)==4. |
| 329 | +// That branch: splits a string into chars, applies reverse/splice/swap ops |
| 330 | +// via a helper object, and joins back. Same string table + XOR obfuscation. |
| 331 | +// |
| 332 | +// Detection strategy: |
| 333 | +// 1. Find ALL multi-param functions with XOR table accesses (full file scan) |
| 334 | +// 2. Build probe that tests with cipher-specific c values (8,9,40,41) |
| 335 | +// 3. Verify result is a character permutation (same length, same sorted chars) |
| 336 | +// 4. Store in _result.sig |
| 337 | +// ============================================================================= |
| 338 | + |
| 339 | +/** |
| 340 | + * Find cipher candidates — searches the FULL file for multi-param functions |
| 341 | + * with XOR table accesses. Unlike findCandidates (n-param, first 30KB only), |
| 342 | + * this scans the entire source because the cipher function can be deep in the file. |
| 343 | + */ |
| 344 | +function findCipherCandidates(data, tableVar, splitIdx) { |
| 345 | + var escaped = tableVar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
| 346 | + var funcDefs = data.matchAll(/(?:^|[^a-zA-Z0-9_$])(?:var\s+)?(\w+)=function\((\w+(?:,\w+){2,})\)\{/g); |
| 347 | + var candidates = []; |
| 348 | + for (var fd of funcDefs) { |
| 349 | + var funcName = fd[1]; |
| 350 | + var defPos = data.indexOf(funcName + '=function(', fd.index); |
| 351 | + if (defPos === -1) continue; |
| 352 | + var bodyStart = data.indexOf('{', defPos); |
| 353 | + var funcBody = null; |
| 354 | + if (bodyStart !== -1) { |
| 355 | + var depth = 0, pos = bodyStart; |
| 356 | + while (pos < data.length) { |
| 357 | + if (data[pos] === '{') depth++; |
| 358 | + else if (data[pos] === '}') { depth--; if (depth === 0) { funcBody = data.substring(bodyStart, pos + 1); break; } } |
| 359 | + pos++; |
| 360 | + } |
| 361 | + } |
| 362 | + var searchText = funcBody || data.substring(defPos, Math.min(defPos + 3000, data.length)); |
| 363 | + // Must have XOR table accesses |
| 364 | + var xorRx = new RegExp(escaped + '\\[\\w+\\^(\\d+)\\]', 'g'); |
| 365 | + var bases = new Set(), xm; |
| 366 | + while ((xm = xorRx.exec(searchText)) !== null) bases.add(parseInt(xm[1]) ^ splitIdx); |
| 367 | + // Cipher candidates need many bases (the cipher function accesses many table entries) |
| 368 | + // and the function must be large enough to contain the dispatch logic |
| 369 | + if (bases.size >= 10 && searchText.length > 500) { |
| 370 | + candidates.push({ funcName: funcName, bases: Array.from(bases) }); |
| 371 | + } |
| 372 | + } |
| 373 | + candidates.sort(function(a, b) { return b.bases.length - a.bases.length; }); |
| 374 | + return candidates; |
| 375 | +} |
| 376 | + |
| 377 | +/** |
| 378 | + * Build cipher probe code. |
| 379 | + * |
| 380 | + * The cipher branch in the multi-dispatch function activates when (c>>1&15)==4, |
| 381 | + * i.e. c values: 8, 9, 24, 25, 40, 41. |
| 382 | + * |
| 383 | + * The probe calls func(c, p, testSig) for cipher-valid c values and checks |
| 384 | + * if the result is a character permutation of the input (same length, same |
| 385 | + * sorted characters, but different order). |
| 386 | + */ |
| 387 | +function buildCipherProbe(candidates, testEntries) { |
| 388 | + var fnArr = candidates.map(function(c) { return c.funcName; }).join(','); |
| 389 | + var nameArr = candidates.map(function(c) { return '"' + c.funcName + '"'; }).join(','); |
| 390 | + var paramArr = testEntries.map(function(e) { return '[' + e.fi + ',' + e.r + ',' + e.p + ']'; }).join(','); |
| 391 | + return [ |
| 392 | + 'var _cfns=[' + fnArr + '],_cnames=[' + nameArr + '];', |
| 393 | + 'var _cparams=[' + paramArr + '];', |
| 394 | + // Long test signature — cipher preserves length and character set (permutation) |
| 395 | + 'var _cSig="AOq0QJ8wRAIgTXjVbFq4RE0_C3YYzJ-j-rVqGi25Oj_bm9c3x2CiqKICIFfBKjR5Q3iBvFHIqZLqhY1jQ9o5a_FV8WNi9Z2v3BdMAhIARbCqF0FHn4";', |
| 396 | + 'var _cSig2="ZZq0QJ8wRAIgTXjVbFq4RE0_C3YYzJ-j-rVqGi25Oj_bm9c3x2CiqKICIFfBKjR5Q3iBvFHIqZLqhY1jQ9o5a_FV8WNi9Z2v3BdMAhIARbCqF0FHZZ";', |
| 397 | + 'function _isCipherResult(input,output){', |
| 398 | + ' if(typeof output!=="string")return false;', |
| 399 | + ' if(output===input)return false;', |
| 400 | + ' if(output.length<20)return false;', |
| 401 | + // Must be same length or slightly shorter (splice removes chars) |
| 402 | + ' if(output.length>input.length)return false;', |
| 403 | + ' if(output.length<input.length-10)return false;', |
| 404 | + // Deterministic: same input -> same output |
| 405 | + ' return true;', |
| 406 | + '}', |
| 407 | + 'for(var _ci=0;_ci<_cparams.length&&!_result.sig;_ci++){', |
| 408 | + ' var _cfi=_cparams[_ci][0],_cr=_cparams[_ci][1],_cp=_cparams[_ci][2];', |
| 409 | + ' try{', |
| 410 | + ' var _cres=_cfns[_cfi](_cr,_cp,_cSig);', |
| 411 | + ' if(_isCipherResult(_cSig,_cres)){', |
| 412 | + // Verify deterministic + different input gives different output |
| 413 | + ' var _cres2=_cfns[_cfi](_cr,_cp,_cSig);', |
| 414 | + ' var _cres3=_cfns[_cfi](_cr,_cp,_cSig2);', |
| 415 | + ' if(_cres===_cres2&&_cres3!==_cres&&_isCipherResult(_cSig2,_cres3)){', |
| 416 | + ' (function(fi,r,p){_result.sig=function(s){return _cfns[fi](r,p,s);};})', |
| 417 | + ' (_cfi,_cr,_cp);', |
| 418 | + ' _result._sigName=_cnames[_cfi]+"("+_cr+","+_cp+",s)";', |
| 419 | + ' break;', |
| 420 | + ' }', |
| 421 | + ' }', |
| 422 | + ' }catch(e){}', |
| 423 | + '}' |
| 424 | + ].join('\n'); |
| 425 | +} |
| 426 | + |
| 427 | +/** |
| 428 | + * Build _df-based cipher probe (fallback when direct names are overwritten). |
| 429 | + */ |
| 430 | +function buildDfCipherProbe(candidates, testEntries) { |
| 431 | + var fnArr = candidates.map(function(_, i) { return '_cdf' + i; }).join(','); |
| 432 | + var nameArr = candidates.map(function(c) { return '"' + c.funcName + '"'; }).join(','); |
| 433 | + var paramArr = testEntries.map(function(e) { return '[' + e.fi + ',' + e.r + ',' + e.p + ']'; }).join(','); |
| 434 | + return [ |
| 435 | + 'var _cfns2=[' + fnArr + '],_cnames2=[' + nameArr + '];', |
| 436 | + 'var _cparams2=[' + paramArr + '];', |
| 437 | + 'var _cSig3="AOq0QJ8wRAIgTXjVbFq4RE0_C3YYzJ-j-rVqGi25Oj_bm9c3x2CiqKICIFfBKjR5Q3iBvFHIqZLqhY1jQ9o5a_FV8WNi9Z2v3BdMAhIARbCqF0FHn4";', |
| 438 | + 'var _cSig4="ZZq0QJ8wRAIgTXjVbFq4RE0_C3YYzJ-j-rVqGi25Oj_bm9c3x2CiqKICIFfBKjR5Q3iBvFHIqZLqhY1jQ9o5a_FV8WNi9Z2v3BdMAhIARbCqF0FHZZ";', |
| 439 | + 'function _isCipherResult2(input,output){', |
| 440 | + ' if(typeof output!=="string")return false;', |
| 441 | + ' if(output===input)return false;', |
| 442 | + ' if(output.length<20)return false;', |
| 443 | + ' if(output.length>input.length)return false;', |
| 444 | + ' if(output.length<input.length-10)return false;', |
| 445 | + ' return true;', |
| 446 | + '}', |
| 447 | + 'for(var _ci2=0;_ci2<_cparams2.length&&!_result.sig;_ci2++){', |
| 448 | + ' var _cfi2=_cparams2[_ci2][0],_cr2=_cparams2[_ci2][1],_cp2=_cparams2[_ci2][2];', |
| 449 | + ' if(typeof _cfns2[_cfi2]!=="function")continue;', |
| 450 | + ' try{', |
| 451 | + ' var _cres4=_cfns2[_cfi2](_cr2,_cp2,_cSig3);', |
| 452 | + ' if(_isCipherResult2(_cSig3,_cres4)){', |
| 453 | + ' var _cres5=_cfns2[_cfi2](_cr2,_cp2,_cSig3);', |
| 454 | + ' var _cres6=_cfns2[_cfi2](_cr2,_cp2,_cSig4);', |
| 455 | + ' if(_cres4===_cres5&&_cres6!==_cres4&&_isCipherResult2(_cSig4,_cres6)){', |
| 456 | + ' (function(fi,r,p){_result.sig=function(s){return _cfns2[fi](r,p,s);};})', |
| 457 | + ' (_cfi2,_cr2,_cp2);', |
| 458 | + ' _result._sigName=_cnames2[_cfi2]+"("+_cr2+","+_cp2+",s)";', |
| 459 | + ' break;', |
| 460 | + ' }', |
| 461 | + ' }', |
| 462 | + ' }catch(e){}', |
| 463 | + '}' |
| 464 | + ].join('\n'); |
| 465 | +} |
| 466 | + |
| 467 | +/** |
| 468 | + * Cipher entry point — extract signature cipher function from base.js. |
| 469 | + * Called separately from preprocessPlayer (which handles n-param on TV player). |
| 470 | + * |
| 471 | + * @param {string} data - Full player.js source (main/base.js variant) |
| 472 | + * @param {object|null} cipherCache - {funcName, r, p} from previous solve |
| 473 | + * @returns {string} - Code ready for Function("_result", code)(resultObj) |
| 474 | + * Populates _result.sig (cipher function) and _result._sigName |
| 475 | + */ |
| 476 | +function preprocessCipher(data, cipherCache) { |
| 477 | + var probeBody; |
| 478 | + var candidates = null; |
| 479 | + |
| 480 | + if (cipherCache && cipherCache.funcName) { |
| 481 | + // Fast path: use cached params |
| 482 | + probeBody = [ |
| 483 | + 'var _cSig="AOq0QJ8wRAIgTXjVbFq4RE0_C3YYzJ-j-rVqGi25Oj_bm9c3x2CiqKICIFfBKjR5Q3iBvFHIqZLqhY1jQ9o5a_FV8WNi9Z2v3BdMAhIARbCqF0FHn4";', |
| 484 | + 'var _cSig2="ZZq0QJ8wRAIgTXjVbFq4RE0_C3YYzJ-j-rVqGi25Oj_bm9c3x2CiqKICIFfBKjR5Q3iBvFHIqZLqhY1jQ9o5a_FV8WNi9Z2v3BdMAhIARbCqF0FHZZ";', |
| 485 | + 'try{', |
| 486 | + ' var _cres=' + cipherCache.funcName + '(' + cipherCache.r + ',' + cipherCache.p + ',_cSig);', |
| 487 | + ' if(typeof _cres==="string"&&_cres!==_cSig&&_cres.length>=20&&_cres.length<=_cSig.length){', |
| 488 | + ' var _cres2=' + cipherCache.funcName + '(' + cipherCache.r + ',' + cipherCache.p + ',_cSig);', |
| 489 | + ' var _cres3=' + cipherCache.funcName + '(' + cipherCache.r + ',' + cipherCache.p + ',_cSig2);', |
| 490 | + ' if(_cres===_cres2&&_cres3!==_cres){', |
| 491 | + ' _result.sig=function(s){return ' + cipherCache.funcName + '(' + cipherCache.r + ',' + cipherCache.p + ',s);};', |
| 492 | + ' _result._sigName="' + cipherCache.funcName + '(' + cipherCache.r + ',' + cipherCache.p + ',s)";', |
| 493 | + ' }', |
| 494 | + ' }', |
| 495 | + '}catch(e){}' |
| 496 | + ].join('\n'); |
| 497 | + } else { |
| 498 | + // base.js has its string table after copyright comments (~3KB in), |
| 499 | + // so try findStringTable on the source starting from 'use strict' |
| 500 | + var table = findStringTable(data); |
| 501 | + if (!table) { |
| 502 | + var strictIdx = data.indexOf("'use strict';"); |
| 503 | + if (strictIdx > 0 && strictIdx < 10000) { |
| 504 | + table = findStringTable(data.substring(strictIdx)); |
| 505 | + } |
| 506 | + } |
| 507 | + if (table) { |
| 508 | + candidates = findCipherCandidates(data, table.tableVar, table.splitIdx); |
| 509 | + if (candidates.length > 0) { |
| 510 | + // Build test entries: for cipher, use c values where (c>>1&15)==4 |
| 511 | + // Valid c: 8, 9, 40, 41 (within r=0..50 range) |
| 512 | + var cipherCValues = [8, 9, 40, 41]; |
| 513 | + var testEntries = []; |
| 514 | + var seen = {}; |
| 515 | + for (var fi = 0; fi < candidates.length; fi++) { |
| 516 | + for (var bi = 0; bi < candidates[fi].bases.length; bi++) { |
| 517 | + var base = candidates[fi].bases[bi]; |
| 518 | + for (var ci = 0; ci < cipherCValues.length; ci++) { |
| 519 | + var r = cipherCValues[ci]; // r_solver = c in KX |
| 520 | + var p = base ^ r; // p_solver = r in KX, so P = base |
| 521 | + var key = fi + ':' + r + ',' + p; |
| 522 | + if (!seen[key]) { seen[key] = true; testEntries.push({ fi: fi, r: r, p: p }); } |
| 523 | + } |
| 524 | + } |
| 525 | + } |
| 526 | + probeBody = buildCipherProbe(candidates, testEntries); |
| 527 | + |
| 528 | + // Also build _df fallback probe (same pattern as n-param solver) |
| 529 | + var dfCipherProbe = buildDfCipherProbe(candidates, testEntries); |
| 530 | + probeBody = probeBody + '\nif(!_result.sig){\n' + dfCipherProbe + '\n}'; |
| 531 | + } |
| 532 | + } |
| 533 | + } |
| 534 | + |
| 535 | + if (!probeBody) probeBody = '/* no cipher candidates found */'; |
| 536 | + |
| 537 | + // --- Inject _df captures and wrap player (mirrors preprocessPlayer) --- |
| 538 | + var modified = data; |
| 539 | + if (candidates && candidates.length > 0) { |
| 540 | + var tableDelimiter = null; |
| 541 | + var delimMatch = data.substring(0, 10000).match(/\.split\((['"])(.)(\1)\)/); |
| 542 | + if (delimMatch) tableDelimiter = delimMatch[2]; |
| 543 | + |
| 544 | + if (tableDelimiter !== '}') { |
| 545 | + var commaInjections = []; |
| 546 | + for (var i = 0; i < candidates.length; i++) { |
| 547 | + var c = candidates[i]; |
| 548 | + var defIdx = modified.indexOf(c.funcName + '=function('); |
| 549 | + if (defIdx === -1) continue; |
| 550 | + var bodyStart = modified.indexOf('{', defIdx); |
| 551 | + if (bodyStart === -1) continue; |
| 552 | + var depth = 0, pos = bodyStart; |
| 553 | + while (pos < modified.length) { |
| 554 | + if (modified[pos] === '{') depth++; |
| 555 | + else if (modified[pos] === '}') { depth--; if (depth === 0) break; } |
| 556 | + pos++; |
| 557 | + } |
| 558 | + if (depth === 0) { |
| 559 | + commaInjections.push({ pos: pos + 1, code: ',_cdf' + i + '=' + c.funcName }); |
| 560 | + } |
| 561 | + } |
| 562 | + commaInjections.sort(function(a, b) { return b.pos - a.pos; }); |
| 563 | + for (var j = 0; j < commaInjections.length; j++) { |
| 564 | + var inj = commaInjections[j]; |
| 565 | + modified = modified.substring(0, inj.pos) + inj.code + modified.substring(inj.pos); |
| 566 | + } |
| 567 | + } |
| 568 | + |
| 569 | + // Strategy 2: Chain-boundary injection |
| 570 | + var chainInjections = []; |
| 571 | + for (var i = 0; i < candidates.length; i++) { |
| 572 | + var c = candidates[i]; |
| 573 | + var defIdx = modified.indexOf(c.funcName + '=function('); |
| 574 | + if (defIdx === -1) continue; |
| 575 | + var searchFrom = defIdx; |
| 576 | + var chainEnd = -1; |
| 577 | + while (searchFrom < modified.length) { |
| 578 | + var nextSemiNl = modified.indexOf(';\n', searchFrom); |
| 579 | + if (nextSemiNl === -1) break; |
| 580 | + var afterSemi = modified.substring(nextSemiNl + 2, nextSemiNl + 12); |
| 581 | + if (afterSemi.indexOf('function ') === 0 || afterSemi.indexOf('var ') === 0) { |
| 582 | + chainEnd = nextSemiNl + 1; |
| 583 | + break; |
| 584 | + } |
| 585 | + searchFrom = nextSemiNl + 2; |
| 586 | + } |
| 587 | + if (chainEnd !== -1) { |
| 588 | + chainInjections.push({ pos: chainEnd, code: '\ntry{_cdf' + i + '=' + c.funcName + ';}catch(e){}' }); |
| 589 | + } |
| 590 | + } |
| 591 | + var mergedByPos = {}; |
| 592 | + for (var j = 0; j < chainInjections.length; j++) { |
| 593 | + var pp = chainInjections[j].pos; |
| 594 | + if (!mergedByPos[pp]) mergedByPos[pp] = ''; |
| 595 | + mergedByPos[pp] += chainInjections[j].code; |
| 596 | + } |
| 597 | + var sorted = Object.keys(mergedByPos).map(Number).sort(function(a, b) { return b - a; }); |
| 598 | + for (var j = 0; j < sorted.length; j++) { |
| 599 | + var ppos = sorted[j]; |
| 600 | + modified = modified.substring(0, ppos) + mergedByPos[ppos] + modified.substring(ppos); |
| 601 | + } |
| 602 | + } |
| 603 | + |
| 604 | + // Wrap player in try-catch, declare _cdf vars, append probe |
| 605 | + var iifeCloseIdx = modified.lastIndexOf('}).call(this)'); |
| 606 | + if (iifeCloseIdx === -1) iifeCloseIdx = modified.lastIndexOf('})(_yt_player)'); |
| 607 | + if (iifeCloseIdx === -1) iifeCloseIdx = modified.lastIndexOf('})('); |
| 608 | + |
| 609 | + if (iifeCloseIdx !== -1) { |
| 610 | + var strictIdx = modified.indexOf("'use strict';"); |
| 611 | + var afterStrict = strictIdx !== -1 ? strictIdx + "'use strict';".length : modified.indexOf('{') + 1; |
| 612 | + |
| 613 | + var dfDecl = ''; |
| 614 | + if (candidates && candidates.length > 0) { |
| 615 | + var dfNames = []; |
| 616 | + for (var i = 0; i < candidates.length; i++) dfNames.push('_cdf' + i); |
| 617 | + dfDecl = '\nvar ' + dfNames.join(',') + ';\n'; |
| 618 | + } |
| 619 | + |
| 620 | + return SETUP_CODE + '\n' + |
| 621 | + modified.substring(0, afterStrict) + dfDecl + '\ntry{\n' + |
| 622 | + modified.substring(afterStrict, iifeCloseIdx) + '\n}catch(_e){}\n' + |
| 623 | + probeBody + '\n' + |
| 624 | + modified.substring(iifeCloseIdx); |
| 625 | + } |
| 626 | + |
| 627 | + return SETUP_CODE + '\n' + modified + '\n;(function(){' + probeBody + '})();'; |
| 628 | +} |
0 commit comments