diff --git a/materialious/android/.idea/misc.xml b/materialious/android/.idea/misc.xml index dddfeda8..74dd639e 100644 --- a/materialious/android/.idea/misc.xml +++ b/materialious/android/.idea/misc.xml @@ -1,7 +1,7 @@ - + diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle index 9054238c..3742e523 100644 --- a/materialious/android/app/build.gradle +++ b/materialious/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "us.materialio.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 104 - versionName "1.8.0" + versionCode 105 + versionName "1.8.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/materialious/electron/materialious.metainfo.xml b/materialious/electron/materialious.metainfo.xml index 6a4627e3..35ada7cf 100644 --- a/materialious/electron/materialious.metainfo.xml +++ b/materialious/electron/materialious.metainfo.xml @@ -65,6 +65,10 @@ + + + https://github.com/Materialious/Materialious/releases/tag/1.8.1 + https://github.com/Materialious/Materialious/releases/tag/1.8.0 diff --git a/materialious/electron/package.json b/materialious/electron/package.json index 1fb139ba..34f74932 100644 --- a/materialious/electron/package.json +++ b/materialious/electron/package.json @@ -1,6 +1,6 @@ { "name": "Materialious", - "version": "1.8.0", + "version": "1.8.1", "description": "Modern material design for Invidious.", "author": { "name": "Ward Pearce", diff --git a/materialious/package-lock.json b/materialious/package-lock.json index 3b9febc2..a41472c8 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -1,12 +1,12 @@ { "name": "materialious", - "version": "1.8.0", + "version": "1.8.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "materialious", - "version": "1.8.0", + "version": "1.8.1", "hasInstallScript": true, "dependencies": { "@capacitor-community/electron": "^5.0.1", diff --git a/materialious/package.json b/materialious/package.json index bcbd065a..025560f0 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -1,6 +1,6 @@ { "name": "materialious", - "version": "1.8.0", + "version": "1.8.1", "private": true, "scripts": { "dev": "vite dev", diff --git a/materialious/src/lib/sabr/shakaHttpPlugin.ts b/materialious/src/lib/sabr/shakaHttpPlugin.ts index fbeb11dc..598a5b1e 100644 --- a/materialious/src/lib/sabr/shakaHttpPlugin.ts +++ b/materialious/src/lib/sabr/shakaHttpPlugin.ts @@ -101,13 +101,12 @@ export class HttpFetchPlugin { const timeoutMs = request.retryParameters.timeout; if (timeoutMs) { - const timer = new shaka.util.Timer(() => { - abortStatus.timedOut = true; - controller.abort(); - }); - - timer.tickAfter(timeoutMs / 1000); - op.finally(() => timer.stop()); + // const timer = new shaka.util.Timer(() => { + // abortStatus.timedOut = true; + // controller.abort(); + // }); + // timer.tickAfter(timeoutMs / 1000); + // op.finally(() => timer.stop()); } return op; diff --git a/materialious/static/nodejs-android/index.js b/materialious/static/nodejs-android/index.js index 42ef0331..a5f7d67a 100644 --- a/materialious/static/nodejs-android/index.js +++ b/materialious/static/nodejs-android/index.js @@ -25,6 +25,7 @@ const CORS_HEADERS = [ 'Referer', 'Cookie' ].join(', '); + const CORS_ORIGIN = 'https://www.youtube.com'; const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36(KHTML, like Gecko)'; @@ -37,122 +38,111 @@ function setCorsHeaders(res) { res.setHeader('Access-Control-Allow-Credentials', 'true'); } -function fetchWithRedirects(targetUrl, options, bodyChunks, redirectCount = 0) { - return new Promise((resolve, reject) => { - const httpClient = targetUrl.protocol.startsWith('https') ? https : http; +function proxyRequest(clientReq, clientRes, parsedUrl, bodyChunks = [], redirectCount = 0) { + if (redirectCount > MAX_REDIRECTS) { + clientRes.writeHead(508, { 'Content-Type': 'text/plain' }); + return clientRes.end('Too many redirects'); + } - const req = httpClient.request(targetUrl, options, (res) => { - if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { - if (redirectCount >= MAX_REDIRECTS) { - req.end(); - return reject(new Error('Too many redirects')); - } + const isHttps = parsedUrl.protocol === 'https:'; + const httpClient = isHttps ? https : http; - try { - // Attempt to create the redirect URL - const redirectUrl = new URL(res.headers.location, targetUrl); - return resolve(fetchWithRedirects(redirectUrl, options, bodyChunks, redirectCount + 1)); - } catch (error) { - req.end(); - return reject(new Error(`Invalid URL in redirect: ${error.message}`)); - } - } - resolve(res); // Resolve with the final response if not a redirect - }); + const proxyOptions = { + method: clientReq.method, + headers: { + ...clientReq.headers, + host: parsedUrl.host, + origin: parsedUrl.origin, + 'user-agent': USER_AGENT, + connection: 'close' + } + }; - // For POST and PUT methods, pass the body to the outgoing request - if (bodyChunks && (req.method === 'POST' || req.method === 'PUT')) { - const buffer = Buffer.concat(bodyChunks); - options.headers['Content-Length'] = buffer.length; + // Remove headers that may cause issues or be auto-managed + for (const key of [ + 'referer', + 'x-forwarded-for', + 'x-requested-with', + 'sec-ch-ua-mobile', + 'sec-ch-ua', + 'sec-ch-ua-platform' + ]) { + delete proxyOptions.headers[key]; + } - req.write(buffer); + const proxyReq = httpClient.request(parsedUrl, proxyOptions, (proxyRes) => { + // Handle redirects + if (proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers.location) { + proxyRes.resume(); // discard response data + const newUrl = new URL(proxyRes.headers.location, parsedUrl); + return proxyRequest(clientReq, clientRes, newUrl, bodyChunks, redirectCount + 1); } - req.setTimeout(10000, () => reject(new Error('Request timeout')), req.end()); - req.on('error', (error) => reject(error), req.end()); - req.end(); + clientRes.writeHead(proxyRes.statusCode, { + ...proxyRes.headers, + 'Access-Control-Allow-Origin': CORS_ORIGIN, + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': CORS_HEADERS, + 'Access-Control-Allow-Credentials': 'true' + }); + + proxyRes.pipe(clientRes); }); + + // Timeout handler + proxyReq.setTimeout(120000, () => { + proxyReq.destroy(new Error('Proxy request timed out')); + }); + + proxyReq.on('error', (err) => { + console.error('Proxy request error:', err.message); + if (!clientRes.headersSent) { + clientRes.writeHead(500, { 'Content-Type': 'text/plain' }); + } + clientRes.end(`Proxy error: ${err.message}`); + }); + + clientReq.on('aborted', () => proxyReq.destroy()); + + // Write buffered body if present + if (bodyChunks.length > 0) { + proxyReq.write(Buffer.concat(bodyChunks)); + } + + proxyReq.end(); } -const server = http.createServer(async (req, res) => { +const server = http.createServer((req, res) => { setCorsHeaders(res); if (req.method === 'OPTIONS') { - res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.writeHead(204); return res.end(); } if (!req.url || req.url === '/') { res.writeHead(400, { 'Content-Type': 'text/plain' }); - return res.end('No URL provided to fetch.'); + return res.end('No URL provided.'); + } + + let targetUrl = req.url.slice(1); + if (!targetUrl.startsWith('http')) { + targetUrl = 'http://' + targetUrl; } - let targetUrl = req.url.slice(1); // Remove leading '/' let parsedTarget; try { - // Ensure protocol (http) is added if missing - if (!targetUrl.startsWith('http')) { - targetUrl = 'http://' + targetUrl; - } parsedTarget = new URL(targetUrl); - } catch (error) { + } catch (err) { res.writeHead(400, { 'Content-Type': 'text/plain' }); - return res.end(`Invalid URL: ${error.message}`); + return res.end(`Invalid URL: ${err.message}`); } - let chunks = []; - req.on('data', (chunk) => { - chunks.push(chunk); - }); - - req.on('end', async () => { - const options = { - method: req.method, - headers: Object.fromEntries( - Object.entries(req.headers).filter( - ([key]) => - ![ - 'referer', - 'x-forwarded-for', - 'x-requested-with', - 'sec-ch-ua-mobile', - 'sec-ch-ua', - 'sec-ch-ua-platform' - ].includes(key.toLowerCase()) - ) - ) - }; - - options.headers.host = parsedTarget.host; - options.headers.origin = parsedTarget.origin; - options.headers['user-agent'] = USER_AGENT; - - try { - const proxyRes = await fetchWithRedirects(parsedTarget, options, chunks); - - req.on('close', () => { - console.log('Request canceled by the client.'); - proxyRes.destroy(); - }); - - res.writeHead(proxyRes.statusCode, { - ...proxyRes.headers, - 'Access-Control-Allow-Origin': CORS_ORIGIN, - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'Access-Control-Allow-Headers': CORS_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}`); - } - }); + const bodyChunks = []; + req.on('data', (chunk) => bodyChunks.push(chunk)); + req.on('end', () => proxyRequest(req, res, parsedTarget, bodyChunks)); }); server.listen(PORT, () => { - console.log(`Server is running on http://${HOST}:${PORT}`); + console.log(`Proxy server running at http://${HOST}:${PORT}`); }); diff --git a/materialious/static/nodejs-android/package-lock.json b/materialious/static/nodejs-android/package-lock.json index 140d2606..01f520ad 100644 --- a/materialious/static/nodejs-android/package-lock.json +++ b/materialious/static/nodejs-android/package-lock.json @@ -1,12 +1,12 @@ { - "name": "cors-patch", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cors-patch", - "version": "0.0.1" - } - } + "name": "cors-patch", + "version": "0.0.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cors-patch", + "version": "0.0.2" + } + } } diff --git a/materialious/static/nodejs-android/package.json b/materialious/static/nodejs-android/package.json index dfd11e64..ae94797e 100644 --- a/materialious/static/nodejs-android/package.json +++ b/materialious/static/nodejs-android/package.json @@ -1,5 +1,5 @@ { - "name": "cors-patch", - "version": "0.0.1", - "main": "./index.js" + "name": "cors-patch", + "version": "0.0.2", + "main": "./index.js" } diff --git a/update_versions.py b/update_versions.py index 599ca5ea..bc23c11e 100644 --- a/update_versions.py +++ b/update_versions.py @@ -3,7 +3,7 @@ import os import re from datetime import datetime -LATEST_VERSION = "1.8.0" +LATEST_VERSION = "1.8.1" RELEASE_DATE = datetime.now().strftime("%Y-%-m-%d") # Format: YYYY-M-D WORKING_DIR = os.path.join(