Spaces:
Runtime error
Runtime error
| /** | |
| * Injected into MAIN world on labs.google β has access to window.grecaptcha | |
| * Also intercepts TRPC fetch responses to capture fresh signed media URLs. | |
| */ | |
| const SITE_KEY = '6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV'; | |
| // βββ XHR Interceptor (for file uploads) βββββββββββββββββββββ | |
| const _xhrOpen = XMLHttpRequest.prototype.open; | |
| const _xhrSend = XMLHttpRequest.prototype.send; | |
| XMLHttpRequest.prototype.open = function (method, url, ...rest) { | |
| this.__sniffUrl = url; | |
| this.__sniffMethod = method; | |
| return _xhrOpen.call(this, method, url, ...rest); | |
| }; | |
| XMLHttpRequest.prototype.send = function (body) { | |
| try { | |
| const url = this.__sniffUrl || ''; | |
| if (url.includes('googleapis.com') || url.includes('labs.google') || url.includes('storage.google')) { | |
| window.postMessage({ | |
| type: '__FLOWKIT_SNIFF__', | |
| url, | |
| body: typeof body === 'string' ? body : `(binary ${body?.size || body?.byteLength || '?'} bytes)`, | |
| method: this.__sniffMethod || 'POST', | |
| }, '*'); | |
| } | |
| } catch {} | |
| return _xhrSend.call(this, body); | |
| }; | |
| // βββ TRPC Response Monitor βββββββββββββββββββββββββββββββββ | |
| // Monkey-patch fetch to intercept TRPC responses containing media URLs. | |
| // Fresh signed GCS URLs are extracted and forwarded to the agent. | |
| const _originalFetch = window.fetch; | |
| window.fetch = async function (...args) { | |
| try { | |
| const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || ''; | |
| // βββ SNIFF ALL outgoing requests (catch upload) βββββββββ | |
| { | |
| let bodyText = ''; | |
| if (args[1]?.body) { | |
| const b = args[1].body; | |
| if (typeof b === 'string') bodyText = b.length > 5000 ? b.slice(0, 200) + `...(${b.length} chars)` : b; | |
| else if (b instanceof FormData) bodyText = `(FormData: ${[...b.keys()].join(', ')})`; | |
| else if (b instanceof Blob) bodyText = `(Blob ${b.size} bytes, type=${b.type})`; | |
| else if (b instanceof ArrayBuffer) bodyText = `(ArrayBuffer ${b.byteLength} bytes)`; | |
| else if (b instanceof ReadableStream) bodyText = '(ReadableStream)'; | |
| else bodyText = JSON.stringify(b)?.slice(0, 2000) || '(unknown)'; | |
| } | |
| window.postMessage({ | |
| type: '__FLOWKIT_SNIFF__', | |
| url, body: bodyText, method: args[1]?.method || 'GET', | |
| }, '*'); | |
| } | |
| } catch {} | |
| const response = await _originalFetch.apply(this, args); | |
| try { | |
| const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || ''; | |
| // Only intercept TRPC calls on labs.google that return project/flow data | |
| if (url.includes('/fx/api/trpc/') && response.ok) { | |
| const clone = response.clone(); | |
| clone.text().then(text => { | |
| if (text.includes('storage.googleapis.com/ai-sandbox-videofx/')) { | |
| window.dispatchEvent(new CustomEvent('TRPC_MEDIA_URLS', { | |
| detail: { url, body: text }, | |
| })); | |
| } | |
| }).catch(() => {}); | |
| } | |
| } catch {} | |
| return response; | |
| }; | |
| window.addEventListener('GET_CAPTCHA', async ({ detail }) => { | |
| const { requestId, pageAction } = detail; | |
| try { | |
| await waitForGrecaptcha(); | |
| const token = await window.grecaptcha.enterprise.execute(SITE_KEY, { | |
| action: pageAction, | |
| }); | |
| window.dispatchEvent(new CustomEvent('CAPTCHA_RESULT', { | |
| detail: { requestId, token }, | |
| })); | |
| } catch (e) { | |
| window.dispatchEvent(new CustomEvent('CAPTCHA_RESULT', { | |
| detail: { requestId, error: e.message }, | |
| })); | |
| } | |
| }); | |
| function waitForGrecaptcha(timeout = 10000) { | |
| return new Promise((resolve, reject) => { | |
| const start = Date.now(); | |
| const check = () => { | |
| if (window.grecaptcha?.enterprise?.execute) return resolve(); | |
| if (Date.now() - start > timeout) return reject(new Error('grecaptcha not available')); | |
| setTimeout(check, 200); | |
| }; | |
| check(); | |
| }); | |
| } | |
| // βββ Video Upload Handler βββββββββββββββββββββββββββββββββββ | |
| window.addEventListener('UPLOAD_VIDEO', async ({ detail }) => { | |
| const { requestId, videoBase64, projectId } = detail; | |
| try { | |
| // Convert base64 to Blob | |
| const byteChars = atob(videoBase64); | |
| const byteArray = new Uint8Array(byteChars.length); | |
| for (let i = 0; i < byteChars.length; i++) { | |
| byteArray[i] = byteChars.charCodeAt(i); | |
| } | |
| const blob = new Blob([byteArray], { type: 'video/mp4' }); | |
| // Step 1: POST start β get session URL | |
| const startResp = await _originalFetch('/fx/api/upload-video?action=start', { | |
| method: 'POST', | |
| credentials: 'include', | |
| headers: { | |
| 'X-Upload-Project-Id': projectId || '', | |
| 'X-Upload-Content-Type': 'video/mp4', | |
| 'X-Upload-Content-Length': blob.size.toString(), | |
| }, | |
| }); | |
| const sessionUrl = startResp.headers.get('X-Upload-Session-Url') || ''; | |
| const startData = await startResp.json().catch(() => ({})); | |
| // sessionUrl may be in header OR in response body | |
| const finalSessionUrl = sessionUrl || startData.sessionUrl || ''; | |
| startData._sessionUrl = finalSessionUrl; | |
| startData._status = startResp.status; | |
| if (!finalSessionUrl) { | |
| window.dispatchEvent(new CustomEvent('UPLOAD_VIDEO_RESULT', { | |
| detail: { requestId, error: 'NO_SESSION_URL', startData }, | |
| })); | |
| return; | |
| } | |
| // Step 2: PUT directly to GCS session URL with resumable upload headers | |
| const uploadResp = await _originalFetch(finalSessionUrl, { | |
| method: 'PUT', | |
| body: blob, | |
| headers: { | |
| 'Content-Type': 'video/mp4', | |
| 'X-Goog-Upload-Command': 'upload, finalize', | |
| 'X-Goog-Upload-Offset': '0', | |
| }, | |
| }); | |
| const uploadData = await uploadResp.json().catch(() => ({})); | |
| uploadData._status = uploadResp.status; | |
| window.dispatchEvent(new CustomEvent('UPLOAD_VIDEO_RESULT', { | |
| detail: { requestId, startData, uploadData, status: uploadResp.status }, | |
| })); | |
| } catch (e) { | |
| window.dispatchEvent(new CustomEvent('UPLOAD_VIDEO_RESULT', { | |
| detail: { requestId, error: e.message }, | |
| })); | |
| } | |
| }); | |