/*
 * harvest-sabr-bundle.js — paste into the youtube.com DevTools console while
 * signed in, then PLAY the (age-restricted) video and let it run a few seconds.
 *
 * It hooks fetch, finds the WEB player's SABR POST (videoplayback?...&sabr=1),
 * decodes the request body, and prints a self-consistent creds bundle as JSON:
 *
 *   { serverAbrStreamingUrl, cver, ustreamerConfig(b64), poToken(b64url),
 *     video:{itag,lastModified,xtags}, audio:{itag,lastModified,xtags} }
 *
 * Everything comes from ONE request, so cpn / session / poToken all match (the
 * consistency the SABR server enforces). Copy the printed JSON to a file and
 * feed it to: node tools/sabr-live-test.mjs bundle.json
 *
 * Field map (VideoPlaybackAbrRequest): f2 selectedFormatIds (itag f1, lmt f2,
 * xtags f3 — the correct lmt/xtags source, see project_sabr_reload_xtags),
 * f5 videoPlaybackUstreamerConfig, f19 streamerContext{ f2 poToken }.
 */
(() => {
  const AUDIO_ITAGS = new Set([139, 140, 141, 171, 249, 250, 251, 256, 258, 327, 338, 774]);
  const rv = (b, p) => { let r = 0, s = 0, x; do { x = b[p++]; r += (x & 0x7f) * 2 ** s; s += 7; } while (x & 0x80); return [r, p]; };
  const fldAll = (buf, t) => {
    let p = 0; const out = [];
    while (p < buf.length) {
      let tag; [tag, p] = rv(buf, p); const f = tag >> 3, w = tag & 7;
      if (w === 2) { let l; [l, p] = rv(buf, p); if (f === t) out.push(buf.subarray(p, p + l)); p += l; }
      else if (w === 0) { [, p] = rv(buf, p); } else if (w === 5) p += 4; else if (w === 1) p += 8; else break;
    }
    return out;
  };
  const fld = (buf, t) => fldAll(buf, t)[0] || null;
  const b64 = u => { let s = ''; for (const c of u) s += String.fromCharCode(c); return btoa(s); };
  const b64url = u => b64(u).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
  const decFmt = u => {
    let p = 0, itag, lmt, xtags = '';
    while (p < u.length) {
      let tag; [tag, p] = rv(u, p); const f = tag >> 3, w = tag & 7;
      if (w === 0) { let v; [v, p] = rv(u, p); if (f === 1) itag = v; else if (f === 2) lmt = v; }
      else if (w === 2) { let l; [l, p] = rv(u, p); if (f === 3) xtags = new TextDecoder().decode(u.subarray(p, p + l)); p += l; }
      else break;
    }
    return { itag, lastModified: String(lmt), xtags };
  };
  const orig = window.fetch;
  window.fetch = async function (input, init) {
    try {
      const url = typeof input === 'string' ? input : (input && input.url) || '';
      let body = init && init.body;
      if (!body && input && input.clone) { try { body = await input.clone().arrayBuffer(); } catch {} }
      if (url.includes('videoplayback') && url.includes('sabr=1') && body) {
        const buf = new Uint8Array(body instanceof ArrayBuffer ? body : await new Response(body).arrayBuffer());
        const ust = fld(buf, 5);
        const sc = fld(buf, 19);
        const pot = sc && fld(sc, 2);
        const fmts = fldAll(buf, 2).map(decFmt).filter(f => f.itag !== undefined);
        const audio = fmts.find(f => AUDIO_ITAGS.has(f.itag));
        const video = fmts.find(f => !AUDIO_ITAGS.has(f.itag));
        if (ust && pot && audio && video) {
          const bundle = {
            serverAbrStreamingUrl: url,
            cver: (url.match(/[?&]cver=([^&]+)/) || [])[1] || '',
            ustreamerConfig: b64(ust),
            poToken: b64url(pot),
            video, audio
          };
          window.__sabrBundle = bundle;
          console.log('%cSABR BUNDLE — copy the line below to bundle.json:', 'color:lime;font-size:14px');
          console.log(JSON.stringify(bundle));
        }
      }
    } catch (e) { console.warn('[harvest]', e); }
    return orig.apply(this, arguments);
  };
  console.log('%c[harvest] hook installed — play/seek the video, then copy the SABR BUNDLE JSON', 'color:cyan;font-size:13px');
})();
