1/** Talk to the Cuts Point Chrome extension for YouTube cookie handoff. */ 2;(function () { 3 const cfg = window.VIRALMOMENT || {} 4 5 function handoffOrigin() { 6 return (cfg.extensionHandoffOrigin || "https://streamaifrontend.netlify.app").replace(/\/$/, "") 7 } 8 9 /** Origins where the published extension can talk to the page directly. */ 10 function isExtensionTrustedSite() { 11 const host = location.hostname 12 return ( 13 host === "streamaifrontend.netlify.app" || 14 host === "localhost" || 15 host === "127.0.0.1" 16 ) 17 } 18 19 function fail(code, detail) { 20 const base = { ok: false, code, reason: detail } 21 return window.VMExtUi?.formatResult ? window.VMExtUi.formatResult(base) : base 22 } 23 24 function finalize(result) { 25 if (!result || result.ok) return result 26 return window.VMExtUi?.formatResult ? window.VMExtUi.formatResult(result) : result 27 } 28 29 function extensionIds() { 30 const ids = Array.isArray(cfg.chromeExtensionIds) ? cfg.chromeExtensionIds.slice() : [] 31 try { 32 const saved = localStorage.getItem("cutspoint_ext_id") 33 if (saved && !ids.includes(saved)) ids.unshift(saved) 34 } catch { 35 /* ignore */ 36 } 37 return ids 38 } 39 40 function sendViaContentBridge(message) { 41 return new Promise((resolve) => { 42 const bridgeId = `${Date.now()}-${Math.random().toString(36).slice(2)}` 43 let settled = false 44 45 const done = (result) => { 46 if (settled) return 47 settled = true 48 window.removeEventListener("message", onResult) 49 clearTimeout(timer) 50 resolve(result) 51 } 52 53 const onResult = (event) => { 54 if (event.source !== window) return 55 const data = event.data 56 if (!data || data.type !== "CUTSPOINT_EXT_BRIDGE_RESULT" || data.bridgeId !== bridgeId) return 57 if (data.error) { 58 done(fail("extension_missing", data.error)) 59 return 60 } 61 done(data.response ? finalize(data.response) : fail("extension_missing")) 62 } 63 64 window.addEventListener("message", onResult) 65 const timer = setTimeout(() => { 66 done(fail("extension_missing")) 67 }, 15000) 68 69 try { 70 window.postMessage({ type: "CUTSPOINT_EXT_BRIDGE", bridgeId, message }, "*") 71 } catch { 72 done(fail("extension_missing")) 73 } 74 }) 75 } 76 77 function sendViaExternalRuntime(message) { 78 return new Promise((resolve) => { 79 if (!window.chrome?.runtime?.sendMessage) { 80 resolve(null) 81 return 82 } 83 const ids = extensionIds() 84 if (!ids.length) { 85 resolve(fail("extension_missing")) 86 return 87 } 88 89 let remaining = ids.length 90 let settled = false 91 92 ids.forEach((id) => { 93 try { 94 window.chrome.runtime.sendMessage(id, message, (response) => { 95 const err = window.chrome.runtime.lastError 96 remaining -= 1 97 if (settled) return 98 if (!err && response) { 99 settled = true 100 try { 101 localStorage.setItem("cutspoint_ext_id", id) 102 } catch { 103 /* ignore */ 104 } 105 resolve(response) 106 return 107 } 108 if (remaining <= 0) { 109 resolve(null) 110 } 111 }) 112 } catch { 113 remaining -= 1 114 if (remaining <= 0 && !settled) { 115 resolve(null) 116 } 117 } 118 }) 119 }) 120 } 121 122 async function sendToExtension(message) { 123 // cutspoint.com is not in the published extension's externally_connectable list, 124 // and Netlify blocks iframes (X-Frame-Options: DENY). Cookie flows use a popup 125 // to the trusted Netlify origin instead â do not attempt iframe RPC here. 126 if (!isExtensionTrustedSite()) { 127 return fail("extension_missing") 128 } 129 const external = await sendViaExternalRuntime(message) 130 if (external) return external 131 return sendViaContentBridge(message) 132 } 133 134 async function ping() { 135 if (!isExtensionTrustedSite()) { 136 // Can't probe from cutspoint.com without framing; popup handoff verifies later. 137 return { ok: true, deferred: true } 138 } 139 const res = await sendToExtension({ type: "PING_EXTENSION" }) 140 if (res?.ok) return res 141 return finalize(res || fail("extension_missing")) 142 } 143 144 async function attachCookies(jobId, accessToken) { 145 return sendToExtension({ 146 type: "ATTACH_YOUTUBE_COOKIES", 147 jobId, 148 accessToken 149 }) 150 } 151 152 async function getBridgeUrl(jobId) { 153 return sendToExtension({ type: "GET_COOKIE_BRIDGE_URL", jobId: jobId || "" }) 154 } 155 156 function listenForCookieResult(jobId, resolve) { 157 const onMessage = (event) => { 158 const data = event.data 159 if (!data || data.type !== "CUTSPOINT_COOKIES_RESULT") return 160 if (jobId && data.jobId && data.jobId !== jobId) return 161 window.removeEventListener("message", onMessage) 162 clearTimeout(timer) 163 resolve( 164 data.ok 165 ? { 166 ok: true, 167 reason: data.message || "ok", 168 cookiesB64: data.cookiesB64 || null 169 } 170 : finalize({ ok: false, reason: data.message }) 171 ) 172 } 173 window.addEventListener("message", onMessage) 174 const timer = setTimeout(() => { 175 window.removeEventListener("message", onMessage) 176 resolve(fail("timeout")) 177 }, 120000) 178 return timer 179 } 180 181 function openCookieBridgePopup(jobId, accessToken, authUrl) { 182 return new Promise((resolve) => { 183 const hash = new URLSearchParams({ 184 access_token: accessToken || "", 185 token_type: "bearer", 186 type: "website" 187 }) 188 const url = `${authUrl}#${hash.toString()}` 189 const popup = window.open(url, "cutspoint_cookie_bridge", "width=440,height=560") 190 191 listenForCookieResult(jobId, resolve) 192 193 if (!popup) { 194 resolve(fail("popup_blocked")) 195 } 196 }) 197 } 198 199 /** Full cookie flow on Netlify (extension-trusted) â used from cutspoint.com. */ 200 function openNetlifyCookieHandoff(jobId, accessToken) { 201 return new Promise((resolve) => { 202 const url = new URL(`${handoffOrigin()}/ext-cookie-handoff.html`) 203 url.searchParams.set("mode", "cookies") 204 url.searchParams.set("origin", location.origin) 205 if (jobId) url.searchParams.set("jobId", jobId) 206 url.hash = new URLSearchParams({ 207 access_token: accessToken || "", 208 token_type: "bearer", 209 type: "website" 210 }).toString() 211 212 const popup = window.open(url.toString(), "cutspoint_handoff", "width=480,height=620") 213 listenForCookieResult(jobId, resolve) 214 215 if (!popup) { 216 resolve(fail("popup_blocked")) 217 } 218 }) 219 } 220 221 async function getCookiesB64IfReady() { 222 if (!isExtensionTrustedSite()) { 223 return { ok: false, cookiesB64: null } 224 } 225 return sendToExtension({ type: "GET_YOUTUBE_COOKIES_B64" }) 226 } 227 228 async function obtainCookiesForScan() { 229 const token = (await window.VMAuth.getAccessToken()) || "" 230 231 if (!isExtensionTrustedSite()) { 232 // Popup to Netlify (trusted by extension) â no iframe. 233 return finalize(await openNetlifyCookieHandoff("", token)) 234 } 235 236 const ready = await getCookiesB64IfReady() 237 if (ready?.ok && ready.cookiesB64) { 238 return { ok: true, cookiesB64: ready.cookiesB64 } 239 } 240 241 const bridge = await getBridgeUrl("") 242 const authUrl = bridge?.authUrl 243 if (!authUrl) { 244 return finalize({ 245 ok: false, 246 code: "extension_missing", 247 reason: bridge?.reason 248 }) 249 } 250 251 return finalize(await openCookieBridgePopup("", token, authUrl)) 252 } 253 254 async function ensureCookiesForJob(jobId, opts) { 255 const force = Boolean(opts && opts.force) 256 if (!force) { 257 const status = await window.VMApi.getCookiesStatus(jobId) 258 if (status?.has_cookies) return { ok: true, alreadyHad: true } 259 } 260 261 const token = await window.VMAuth.getAccessToken() 262 if (!token) { 263 return fail("sign_in") 264 } 265 266 if (!isExtensionTrustedSite()) { 267 const result = finalize(await openNetlifyCookieHandoff(jobId, token)) 268 if (result.ok && result.cookiesB64) { 269 try { 270 await window.VMApi.attachCookies(jobId, result.cookiesB64) 271 } catch { 272 /* attach may already have succeeded inside the bridge */ 273 } 274 } 275 return result 276 } 277 278 // Silent path first (Netlify / localhost) â refresh without a popup when possible. 279 const ready = await getCookiesB64IfReady() 280 if (ready?.ok && ready.cookiesB64) {
281 try { 282 await window.VMApi.attachCookies(jobId, ready.cookiesB64) 283 return { ok: true, cookiesB64: ready.cookiesB64 } 284 } catch { 285 /* fall through to extension attach / popup */ 286 } 287 } 288 289 const attached = await attachCookies(jobId, token) 290 if (attached?.ok) { 291 return { ok: true, cookiesB64: attached.cookiesB64 || ready?.cookiesB64 || null } 292 } 293 294 let authUrl = attached?.authUrl 295 if (!authUrl) { 296 const bridge = await getBridgeUrl(jobId) 297 authUrl = bridge?.authUrl 298 } 299 300 if (!authUrl) { 301 return finalize({ 302 ok: false, 303 code: "extension_missing", 304 reason: attached?.reason 305 }) 306 } 307 308 const result = finalize(await openCookieBridgePopup(jobId, token, authUrl)) 309 if (result.ok && result.cookiesB64) { 310 try { 311 await window.VMApi.attachCookies(jobId, result.cookiesB64) 312 } catch { 313 /* attach may already have succeeded inside the bridge */ 314 } 315 } 316 return result 317 } 318 319 window.addEventListener("cutspoint-extension-ready", (event) => { 320 const id = event?.detail?.id 321 if (!id) return 322 try { 323 localStorage.setItem("cutspoint_ext_id", id) 324 } catch { 325 /* ignore */ 326 } 327 }) 328 329 window.VMExt = { 330 ping, 331 attachCookies, 332 getBridgeUrl, 333 openCookieBridgePopup, 334 getCookiesB64IfReady, 335 obtainCookiesForScan, 336 ensureCookiesForJob 337 } 338})()
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.