Skip to content

Commit 3713bd5

Browse files
committed
optimized
1 parent fc1f2b4 commit 3713bd5

File tree

5 files changed

+106
-100
lines changed

5 files changed

+106
-100
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,4 @@ dist
170170

171171
.dev.vars
172172
.wrangler/
173+
**/test.*

src/index.js

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1-
/**
2-
* Welcome to Cloudflare Workers! This is your first worker.
3-
*
4-
* - Run `npm run dev` in your terminal to start a development server
5-
* - Open a browser tab at http://localhost:8787/ to see your worker in action
6-
* - Run `npm run deploy` to publish your worker
7-
*
8-
* Learn more at https://developers.cloudflare.com/workers/
9-
*/
10-
11-
// index.js
12-
131
import handleCorsRequest from './cors.js';
142
import proxy from './proxy.js';
3+
import reverseProxy from './reverseProxy.js';
154
import { thumbnailHandler } from './thumbnails.js';
16-
import { handleRequest } from './utils/handler.js';
5+
import { handleRequest } from '../utils/handler.js';
6+
7+
const handlePath = {
8+
'/proxy': proxy,
9+
'/reverse-proxy': reverseProxy,
10+
'/cors': handleCorsRequest,
11+
'/image': handleCorsRequest,
12+
'/thumbnail': thumbnailHandler,
13+
};
1714

1815
export default {
19-
async fetch(request) {
16+
async fetch(request, env, context) {
2017
const url = new URL(request.url);
18+
const path = url.pathname;
19+
20+
if (handlePath[path]) {
21+
if (path === '/proxy') {
22+
return handlePath[path](request, env, context);
23+
}
24+
return handlePath[path](...handleRequest(request));
25+
}
26+
27+
if (path === '/favicon.ico') {
28+
return new Response(null, { status: 200 });
29+
}
2130

22-
if (url.pathname === '/proxy') {
23-
return proxy(request);
24-
} else if (url.pathname === '/cors') {
25-
const [url, headers, origin] = handleRequest(request);
26-
return handleCorsRequest(url, headers);
27-
} else if (url.pathname === '/image') {
28-
const [url, headers, origin] = handleRequest(request);
29-
return handleCorsRequest(url, headers, origin);
30-
} else if (url.pathname === '/thumbnail') {
31-
const [url, headers, origin] = handleRequest(request);
32-
return thumbnailHandler(url, headers, origin);
33-
} else if (url.pathname === '/') {
31+
if (path === '/') {
3432
return new Response(
3533
JSON.stringify({
3634
message: 'Welcome to Roxy',
@@ -40,18 +38,14 @@ export default {
4038
{ '/image': 'For Manga Images' },
4139
{ '/thumbnail': 'For Thumbnails' },
4240
],
43-
params: '?url=<Base64-encoded-m3u8-url>&headers=<Base64-encoded-headers>',
44-
tip: 'Base64Encoding is optional for /cors, /thumbnail, /image. Encode the url if it gives error. For /proxy, encoding is required.',
4541
}),
4642
{
4743
status: 200,
48-
headers: {
49-
'Content-Type': 'application/json',
50-
},
44+
headers: { 'Content-Type': 'application/json' },
5145
}
5246
);
5347
}
5448

55-
return new Response('Not Found', { status: 404 });
49+
return new Response('Not Found', { status: 403 });
5650
},
5751
};

src/proxy.js

Lines changed: 29 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { decodeHeaders } from './utils/handler';
1+
import { handleRequest } from '../utils/handler';
22

33
const m3u8ContentTypes = [
44
'application/vnd.apple.mpegurl',
@@ -11,17 +11,30 @@ const m3u8ContentTypes = [
1111
'application/x-apple-hls',
1212
];
1313

14-
const videoContentTypes = [
15-
'video/mp4',
16-
'video/webm',
17-
'video/ogg',
18-
'video/quicktime',
19-
'video/MP2T',
20-
'application/mp4',
21-
'video/x-m4v',
22-
...m3u8ContentTypes,
23-
];
24-
14+
function processM3U8Content(content, mediaUrl, origin) {
15+
return content
16+
.split('\n')
17+
.map((line) => {
18+
// Process URI attributes in tags
19+
const uriMatch = line.match(/(URI=)(["'])(?<uri>.*?)\2/);
20+
if (uriMatch) {
21+
const [fullMatch, prefix, quote] = uriMatch;
22+
const resolvedUrl = new URL(uriMatch.groups.uri, mediaUrl).toString();
23+
const proxyUrl = `${origin}/proxy?url=${encodeURIComponent(resolvedUrl)}`;
24+
return line.replace(fullMatch, `${prefix}${quote}${proxyUrl}${quote}`);
25+
}
26+
27+
// Process segment URLs
28+
if (!line.startsWith('#') && line.trim()) {
29+
const resolvedUrl = new URL(line.trim(), mediaUrl).toString();
30+
const proxyUrl = `${origin}/proxy?url=${encodeURIComponent(resolvedUrl)}`;
31+
return line.replace(line.trim(), proxyUrl);
32+
}
33+
34+
return line;
35+
})
36+
.join('\n');
37+
}
2538
async function proxy(request) {
2639
if (request.method === 'OPTIONS') {
2740
return new Response(null, {
@@ -36,36 +49,8 @@ async function proxy(request) {
3649
}
3750

3851
try {
39-
const url = new URL(request.url);
40-
const urlParams = url.searchParams;
41-
const encodedUrl = urlParams.get('url');
42-
const headersBase64 = urlParams.get('headers');
52+
let [mediaUrl, decodedHeaders, origin] = handleRequest(request);
4353

44-
if (!encodedUrl) {
45-
return new Response('"url" query parameters are required', {
46-
status: 400,
47-
headers: {
48-
'Access-Control-Allow-Origin': '*',
49-
},
50-
});
51-
}
52-
53-
const mediaUrl = atob(decodeURIComponent(encodedUrl));
54-
const decodedHeaders = decodeHeaders(headersBase64);
55-
56-
if (!decodedHeaders) {
57-
return new Response('Invalid headers format. Must be valid base64-encoded JSON.', {
58-
status: 400,
59-
headers: {
60-
'Access-Control-Allow-Origin': '*',
61-
},
62-
});
63-
}
64-
65-
const baseUrl = new URL(mediaUrl);
66-
const basePath = `${baseUrl.protocol}//${baseUrl.host}${baseUrl.pathname.substring(0, baseUrl.pathname.lastIndexOf('/') + 1)}`;
67-
68-
// Pass through any Range header from the original request
6954
const rangeHeader = request.headers.get('Range');
7055
const fetchHeaders = {
7156
...decodedHeaders,
@@ -85,7 +70,7 @@ async function proxy(request) {
8570
throw new Error(`HTTP error! status: ${response.status}`);
8671
}
8772

88-
const contentType = response.headers.get('content-type') || 'application/octet-stream';
73+
const contentType = response.headers.get('content-type');
8974
const isM3U8 = m3u8ContentTypes.some((type) => contentType.includes(type));
9075

9176
if (!isM3U8) {
@@ -102,27 +87,8 @@ async function proxy(request) {
10287
}
10388

10489
let responseContent = await response.text();
105-
responseContent = responseContent.replace(/URI=['"](.*?)['"]/, (_, url) => {
106-
const fullUrl = url.startsWith('http')
107-
? url
108-
: url.startsWith('/')
109-
? `${baseUrl.protocol}//${baseUrl.host}${url}`
110-
: `${basePath}${url}`;
111-
return `URI="${new URL(request.url).origin}/proxy?url=${encodeURIComponent(btoa(fullUrl))}&headers=${encodeURIComponent(
112-
headersBase64
113-
)}"`;
114-
});
115-
116-
const modifiedBody = responseContent.replace(/^(?!#)([^\s]+)$/gm, (match) => {
117-
const fullUrl = match.startsWith('http')
118-
? match
119-
: match.startsWith('/')
120-
? `${baseUrl.protocol}//${baseUrl.host}${match}`
121-
: `${basePath}${match}`;
122-
return `${new URL(request.url).origin}/proxy?url=${encodeURIComponent(btoa(fullUrl))}&headers=${encodeURIComponent(headersBase64)}`;
123-
});
124-
125-
return new Response(modifiedBody, {
90+
responseContent = processM3U8Content(responseContent, mediaUrl, origin);
91+
return new Response(responseContent, {
12692
headers: {
12793
'Content-Type': 'application/vnd.apple.mpegurl',
12894
'Access-Control-Allow-Origin': '*',

src/reverseProxy.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//! oops sorry
2+
async function reverseProxy(url, reqheaders) {
3+
try {
4+
const headers = {
5+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0',
6+
Accept: '*/*',
7+
'Accept-Language': 'en-US,en;q=0.5',
8+
'x-pie-req-header-host': url.split('//')[1].split('/')[0],
9+
'x-pie-req-header-user-agent': 'HTTPie',
10+
'x-pie-req-meta-follow-redirects': 'true',
11+
'x-pie-req-meta-method': 'GET',
12+
'x-pie-req-meta-ssl-verify': 'true',
13+
'x-pie-req-meta-url': url,
14+
'Content-Type': 'text/plain;charset=UTF-8',
15+
'Sec-GPC': '1',
16+
'Sec-Fetch-Dest': 'empty',
17+
'Sec-Fetch-Mode': 'cors',
18+
'Sec-Fetch-Site': 'same-origin',
19+
Priority: 'u=0',
20+
Pragma: 'no-cache',
21+
'Cache-Control': 'no-cache',
22+
referer: 'https://httpie.io/app',
23+
};
24+
const ref = reqheaders['Referer'] || reqheaders['referer'] || reqheaders['Referrer'] || reqheaders['referrer'];
25+
const reverseProxyUrl = 'https://httpie.io/app/api/proxy';
26+
const response = await fetch(reverseProxyUrl, {
27+
method: 'POST',
28+
headers: {
29+
...headers,
30+
'x-pie-req-header-referer': ref || url,
31+
},
32+
});
33+
34+
return new Response(response.body, {
35+
status: response.status,
36+
headers: response.headers,
37+
});
38+
} catch (error) {
39+
console.error('Error fetching the webpage:', error.message);
40+
return new Response('An error occurred while fetching the webpage.', {
41+
status: 500,
42+
headers: { 'Access-Control-Allow-Origin': '*' },
43+
});
44+
}
45+
}
46+
47+
export default reverseProxy;

src/utils/handler.js renamed to utils/handler.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const decodeHeaders = (base64Headers) => {
2-
const headers = {}; // Use a plain object instead of `Headers`
2+
const headers = {};
33
if (!base64Headers) {
44
return headers;
55
}
@@ -37,12 +37,10 @@ export const handleRequest = (request) => {
3737
status: 400,
3838
});
3939
}
40-
let targetUrl;
40+
let targetUrl = decodeURIComponent(encodedUrl);
4141
try {
4242
targetUrl = atob(encodedUrl);
43-
} catch (error) {
44-
targetUrl = encodedUrl;
45-
}
43+
} catch (error) {}
4644

4745
const headers = decodeHeaders(headersBase64);
4846

0 commit comments

Comments
 (0)