diff --git a/materialious/src/lib/android/http/androidRequests.ts b/materialious/src/lib/android/http/androidRequests.ts index 0df0dcde..d9bf4542 100644 --- a/materialious/src/lib/android/http/androidRequests.ts +++ b/materialious/src/lib/android/http/androidRequests.ts @@ -4,10 +4,8 @@ import { Capacitor } from "@capacitor/core"; if (Capacitor.getPlatform() === 'android') { const originalFetch = window.fetch; - // CORS-Anywhere proxy URL - const corsAnywhereProxyUrl: string = 'http://localhost:3000/'; + const corsProxyUrl: string = 'http://localhost:3000/'; - // Overwrite fetch to use CORS-Anywhere proxy window.fetch = async (requestInput: string | URL | Request, requestOptions?: RequestInit): Promise => { let requestUrl: string; @@ -26,15 +24,18 @@ if (Capacitor.getPlatform() === 'android') { referrer: requestInput.referrer, signal: requestInput.signal, }; + + if (requestOptions.body && requestOptions.body instanceof ReadableStream) { + (requestOptions as RequestInit & { duplex?: 'half' | 'full'; }).duplex = 'half'; + } } else { requestUrl = requestInput.toString(); } - if (!requestUrl.startsWith(corsAnywhereProxyUrl) && !requestUrl.startsWith('/') && !requestUrl.startsWith('blob:')) { - requestInput = corsAnywhereProxyUrl + requestUrl; + if (!requestUrl.startsWith(corsProxyUrl) && !requestUrl.startsWith('/') && !requestUrl.startsWith('blob:')) { + requestInput = corsProxyUrl + requestUrl; } - // Use the original fetch with the proxied URL and options return originalFetch(requestInput, requestOptions); }; @@ -45,7 +46,7 @@ if (Capacitor.getPlatform() === 'android') { XMLHttpRequest.prototype.open = function (...args: any[]): void { const targetOriginMatch = /^https?:\/\/([^\/]+)/i.exec(args[1]); if (targetOriginMatch && targetOriginMatch[0].toLowerCase() !== currentOrigin) { - args[1] = corsAnywhereProxyUrl + args[1]; + args[1] = corsProxyUrl + args[1]; } /* @ts-ignore */ return originalXhrOpen.apply(this, args); diff --git a/materialious/src/lib/components/Player.svelte b/materialious/src/lib/components/Player.svelte index cb09dacd..16ff3344 100644 --- a/materialious/src/lib/components/Player.svelte +++ b/materialious/src/lib/components/Player.svelte @@ -346,6 +346,8 @@ } player.addEventListener('dash-can-play', async () => { + await loadPlayerPos(); + const defaultLanguage = get(playerDefaultLanguage); if (defaultLanguage) { let trackIndex = 0; @@ -363,7 +365,6 @@ player.play(); player.exitFullscreen(); } - await loadPlayerPos(); }); if ( @@ -544,7 +545,7 @@ function savePlayerPos() { if (data.video.hlsUrl) return; - if (get(playerSavePlaybackPositionStore) && player.currentTime) { + if (get(playerSavePlaybackPositionStore) && player && player.currentTime) { if (player.currentTime < player.duration - 10 && player.currentTime > 10) { try { localStorage.setItem(`v_${data.video.videoId}`, player.currentTime.toString()); diff --git a/materialious/static/nodejs-android/index.js b/materialious/static/nodejs-android/index.js index d4e20363..bef64195 100644 --- a/materialious/static/nodejs-android/index.js +++ b/materialious/static/nodejs-android/index.js @@ -34,7 +34,12 @@ function fetchWithRedirects(targetUrl, options, redirectCount = 0) { resolve(res); // Resolve with the final response if not a redirect }); - req.on('error', (error) => reject(error)); // Better error handling for request + if (options.body && (req.method === 'POST' || req.method === 'PUT')) { + req.write(options.body); + } + + req.setTimeout(10000, () => reject(new Error('Request timeout')), req.end()); + req.on('error', (error) => reject(error), req.end()); req.end(); }); } @@ -65,30 +70,51 @@ const server = http.createServer(async (req, res) => { return res.end(`Invalid URL: ${error.message}`); } - const options = { - method: req.method, - headers: { - ...req.headers, - host: parsedTarget.hostname, // Ensure correct host header - }, - }; + let body = ''; + req.on('data', chunk => { + body += chunk; + }); - try { - const proxyRes = await fetchWithRedirects(parsedTarget, options); - res.writeHead(proxyRes.statusCode, { - ...proxyRes.headers, - 'Access-Control-Allow-Origin': ORIGIN, - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'Access-Control-Allow-Headers': '*', - 'Access-Control-Allow-Credentials': 'true', - }); + req.on('end', async () => { + const options = { + method: req.method, + headers: { + ...req.headers, + host: parsedTarget.hostname, // Ensure correct host header + } + }; - proxyRes.pipe(res); // Pipe response data back to the client - } catch (error) { - console.error('Proxy error:', error); - res.writeHead(500, { 'Content-Type': 'text/plain' }); - res.end(`Error: ${error.message}`); - } + // For POST and PUT methods, pass the body to the outgoing request + if (req.method === 'POST' || req.method === 'PUT') { + options.headers['Content-Length'] = Buffer.byteLength(body); + options.body = body; + } + + try { + const proxyRes = await fetchWithRedirects(parsedTarget, options); + + req.on('close', () => { + console.log('Request canceled by the client.'); + proxyRes.destroy(); + }); + + res.writeHead(proxyRes.statusCode, { + ...proxyRes.headers, + 'Access-Control-Allow-Origin': ORIGIN, + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': '*', + 'Access-Control-Allow-Credentials': 'true', + }); + + // Pipe response data back to the client + proxyRes.pipe(res); + + } catch (error) { + console.error('Proxy error:', error); + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.end(`Error: ${error.message}`); + } + }); }); server.listen(PORT, () => {