20 Commits

Author SHA1 Message Date
e864bc6100 Update dependencies and version 2025-04-04 00:45:03 +02:00
3ac73ac3b4 Update vite config 2025-04-04 00:44:51 +02:00
6aaf960c28 Holy fuck why does vite have a problem with this 2025-04-04 00:43:57 +02:00
8a13dea681 Format 2025-04-04 00:01:21 +02:00
8d575241fe Fix lastPathSegment util 2025-04-04 00:00:38 +02:00
50f400b8b9 Use enum to specify media type 2025-04-03 23:56:18 +02:00
f6fcfd354a Add more utils 2025-04-03 23:48:53 +02:00
c081127d35 Add loadx.ws 2025-04-03 23:43:49 +02:00
416fceba88 Update match function to manually specify media type 2025-04-03 23:43:39 +02:00
a9cf03c176 Fix luluvdo.com 2025-04-03 23:14:31 +02:00
8852318483 Update voe.sx to catch every redirect url 2025-04-03 22:55:24 +02:00
7067aaf4a0 Add luluvdo.com to supported websites table 2025-03-14 18:52:08 +01:00
bf723e2ed6 Update dependencies and version 2025-03-14 18:41:05 +01:00
9c80362a1c Update ci actions 2025-03-14 18:31:29 +01:00
ae738ff32f Add luluvdo.com site 2025-03-14 18:20:08 +01:00
16d4f2956a Add dood.work domain (#23) 2025-03-14 17:43:50 +01:00
f6c6102436 Merge pull request #24 from muleyo/main
Added Voe.sx Domain
2025-03-13 16:08:39 +01:00
f1525817b1 Added Voe.sx Domain
A domain for Voe.sx was missing
2025-03-10 01:42:23 +01:00
1e1d8cdfa6 Fix tick character 2024-12-19 22:50:59 +01:00
47eed2d12b Update supported websites 2024-12-15 19:08:50 +01:00
11 changed files with 700 additions and 965 deletions

View File

@ -9,12 +9,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Install nodejs - name: Install nodejs
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 22
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
@ -32,12 +32,12 @@ jobs:
- manifest_version: 3 - manifest_version: 3
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Install nodejs - name: Install nodejs
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 22
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
@ -49,7 +49,7 @@ jobs:
run: npm run build run: npm run build
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: stream-bypass-mv${{ matrix.manifest_version }} name: stream-bypass-mv${{ matrix.manifest_version }}
path: ./dist path: ./dist

View File

@ -80,11 +80,12 @@ The best way to install the extension are the official browser extension stores:
## 📜 Supported websites ## 📜 Supported websites
| Site | Firefox & Firefox for Android | Chrome & Chromium based | | Site | Firefox & Firefox for Android | Chrome & Chromium based |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| [dropload.io](https://dropload.io) | ✔ | ✔ | | [dropload.io](https://dropload.io) | ✔ | ✔ |
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔ | ⚠ (redirect probably required) | | [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔ | ⚠ (redirect probably required) |
| [filemoon.to](https://filemoon.to) | ✔ | ✔ | | [filemoon.to](https://filemoon.to) | ✔ | ✔ |
| [goodstream.uno](https://goodstream.uno) | ✔ | ✔ | | [goodstream.uno](https://goodstream.uno) | ✔ | ✔ |
| [luluvdo.com](https://luluvdo.com) | ✔ | ❌ (background request always required) |
| [mixdrop.co](https://mixdrop.co) | ✔ | ✔ | | [mixdrop.co](https://mixdrop.co) | ✔ | ✔ |
| [mp4upload.com](https://mp4upload.com) | ✔ | ✔ | | [mp4upload.com](https://mp4upload.com) | ✔ | ✔ |
| [newgrounds.com](https://newgrounds.com) | ✔ | ✔ | | [newgrounds.com](https://newgrounds.com) | ✔ | ✔ |
@ -94,7 +95,7 @@ The best way to install the extension are the official browser extension stores:
| [supervideo.tv](https://supervideo.tv) | ✔ | ✔ | | [supervideo.tv](https://supervideo.tv) | ✔ | ✔ |
| [upstream.to](https://upstream.to) | ✔ | ✔ | | [upstream.to](https://upstream.to) | ✔ | ✔ |
| [vidmoly.to](https://vidmoly.me) | ✔ | ✔ | | [vidmoly.to](https://vidmoly.me) | ✔ | ✔ |
| [vidoza.net](https://vidoza.net) | ⚠ (doesn't always work the first time, retrying/reloading the page one or two times fixes it) | ⚠ (doesn't always work the first time, retrying/reloading the page one or two times fixes it) | | [vidoza.net](https://vidoza.net) | ✔ | ✔ |
| [voe.sx](https://voe.sx) | ✔ | ❌ (redirect always required) | | [voe.sx](https://voe.sx) | ✔ | ❌ (redirect always required) |
| [vupload.com](https://vupload.com) | ✔ | ✔ | | [vupload.com](https://vupload.com) | ✔ | ✔ |
| [kwik.cx](https://kwik.cx) | ✔ | ✔ | | [kwik.cx](https://kwik.cx) | ✔ | ✔ |

1436
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "stream-bypass", "name": "stream-bypass",
"version": "3.1.1", "version": "3.1.3",
"displayName": "Stream Bypass", "displayName": "Stream Bypass",
"author": "bytedream", "author": "bytedream",
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video", "description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
@ -26,25 +26,25 @@
}, },
"devDependencies": { "devDependencies": {
"@samrum/vite-plugin-web-extension": "^5.1.1", "@samrum/vite-plugin-web-extension": "^5.1.1",
"@sveltejs/vite-plugin-svelte": "^5.0.2", "@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tsconfig/svelte": "^5.0.4", "@tsconfig/svelte": "^5.0.4",
"@types/chrome": "^0.0.287", "@types/chrome": "^0.0.313",
"@types/firefox-webext-browser": "^120.0.4", "@types/firefox-webext-browser": "^120.0.4",
"eslint": "^9.17.0", "eslint": "^9.23.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^10.1.1",
"eslint-plugin-svelte": "^2.46.1", "eslint-plugin-svelte": "^3.5.0",
"hls.js": "^1.5.17", "hls.js": "^1.6.0",
"prettier": "^3.4.2", "prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.3.2", "prettier-plugin-svelte": "^3.3.3",
"sass": "^1.83.0", "sass": "^1.86.2",
"svelte": "^5.14.0", "svelte": "^5.25.6",
"svelte-check": "^4.1.1", "svelte-check": "^4.1.5",
"svelte-preprocess": "^6.0.3", "svelte-preprocess": "^6.0.3",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.7.2", "typescript": "^5.8.2",
"typescript-eslint": "^8.18.0", "typescript-eslint": "^8.29.0",
"vite": "^6.0.3", "vite": "^6.2.5",
"web-ext": "^8.3.0" "web-ext": "^8.5.0"
}, },
"type": "module" "type": "module"
} }

View File

@ -1,5 +1,4 @@
import type { Match } from '~/lib/match'; import { getMatch, type Match, MatchMediaType } from '~/lib/match';
import { getMatch } from '~/lib/match';
import { Other, Redirect } from '~/lib/settings'; import { Other, Redirect } from '~/lib/settings';
async function main() { async function main() {
@ -35,13 +34,28 @@ async function main() {
} }
let url: string | null; let url: string | null;
let urlType: MatchMediaType | null;
try { try {
url = await match.match(re); const matchResult = await match.match(re);
if (matchResult && typeof matchResult === 'string') {
url = matchResult;
urlType = url.includes('.m3u8') ? MatchMediaType.Hls : MatchMediaType.Native;
} else if (matchResult && typeof matchResult === 'object') {
if (MatchMediaType.Hls in matchResult) {
url = matchResult[MatchMediaType.Hls];
urlType = MatchMediaType.Hls;
} else if (MatchMediaType.Native in matchResult) {
url = matchResult[MatchMediaType.Native];
urlType = MatchMediaType.Native;
}
}
} catch { } catch {
return; return;
} }
if (!url) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (!url || !urlType) {
return; return;
} }
@ -50,7 +64,7 @@ async function main() {
await chrome.runtime.sendMessage({ action: 'ff2mpv', url: url }); await chrome.runtime.sendMessage({ action: 'ff2mpv', url: url });
} }
if (match.replace && !url.includes('.m3u8')) { if (match.replace && urlType != MatchMediaType.Hls) {
// this destroys all intervals that may spawn popups or events // this destroys all intervals that may spawn popups or events
let intervalId = window.setInterval(() => {}, 0); let intervalId = window.setInterval(() => {}, 0);
while (intervalId--) { while (intervalId--) {
@ -81,7 +95,7 @@ async function main() {
chrome.runtime.getURL( chrome.runtime.getURL(
`src/entries/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}&domain=${ `src/entries/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}&domain=${
window.location.hostname window.location.hostname
}` }&type=${urlType}`
) )
); );
} }

View File

@ -1,4 +1,4 @@
import { matches } from '~/lib/match'; import { matches, MatchMediaType } from '~/lib/match';
import Hls from 'hls.js'; import Hls from 'hls.js';
import { UrlReferer } from '~/lib/settings'; import { UrlReferer } from '~/lib/settings';
@ -31,6 +31,7 @@ export async function play(videoElem: HTMLVideoElement) {
const id = urlQuery.get('id') as string; const id = urlQuery.get('id') as string;
const url = decodeURIComponent(urlQuery.get('url') as string); const url = decodeURIComponent(urlQuery.get('url') as string);
const domain = urlQuery.get('domain') as string; const domain = urlQuery.get('domain') as string;
const urlType = urlQuery.get('urlType') as MatchMediaType;
const match = matches[id]; const match = matches[id];
if (match === undefined) { if (match === undefined) {
@ -38,9 +39,9 @@ export async function play(videoElem: HTMLVideoElement) {
} }
document.title = `Stream Bypass (${domain})`; document.title = `Stream Bypass (${domain})`;
if (new URL(url).pathname.endsWith('.m3u8')) { if (urlType === MatchMediaType.Hls) {
await playHls(url, domain, videoElem); await playHls(url, domain, videoElem);
} else { } else if (urlType === MatchMediaType.Native) {
await playNative(url, domain, videoElem); await playNative(url, domain, videoElem);
} }
} }

View File

@ -43,7 +43,7 @@
/> />
</div> </div>
<hr /> <hr />
{#each hosters as hoster, i} {#each hosters as hoster, i (hoster.id)}
<label for="hoster-{i}" style="cursor: {hostersEnabled ? 'pointer' : 'default'}" <label for="hoster-{i}" style="cursor: {hostersEnabled ? 'pointer' : 'default'}"
>{hoster.name}</label >{hoster.name}</label
> >

View File

@ -1,5 +1,6 @@
import { unpack } from './utils'; import { unpack } from './util/userspace';
import { Hosters, Redirect, TmpHost } from './settings'; import { Hosters, Redirect, TmpHost } from './settings';
import { lastPathSegment } from './util/extract';
export interface Match { export interface Match {
name: string; name: string;
@ -9,7 +10,16 @@ export interface Match {
regex: RegExp[]; regex: RegExp[];
notice?: string; notice?: string;
match(match: RegExpMatchArray): Promise<string | null>; match(
match: RegExpMatchArray
): Promise<
string | { [MatchMediaType.Hls]: string } | { [MatchMediaType.Native]: string } | null
>;
}
export enum MatchMediaType {
Hls = 'hls',
Native = 'native'
} }
export const Doodstream: Match = { export const Doodstream: Match = {
@ -23,6 +33,7 @@ export const Doodstream: Match = {
'dood.cx', 'dood.cx',
'dood.sh', 'dood.sh',
'dood.watch', 'dood.watch',
'dood.work',
'dood.to', 'dood.to',
'dood.so', 'dood.so',
'dood.la', 'dood.la',
@ -105,6 +116,65 @@ export const Kwik: Match = {
} }
}; };
export const LoadX: Match = {
name: 'LoadX',
id: 'loadx',
domains: ['loadx.ws'],
regex: [/./gm],
match: async () => {
const hash = encodeURIComponent(lastPathSegment(window.location.href));
const response = await fetch(
`https://${window.location.host}/player/index.php?data=${hash}&do=getVideo`,
{
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
}
);
const responseJson = await response.json();
const videoSource: string = responseJson['videoSource'];
// extension of extracted url is '.txt', so we have to manually specify that it's a hls
return { [MatchMediaType.Hls]: videoSource.replace('\\/', '/') };
}
};
export const Luluvdo: Match = {
name: 'Luluvdo',
id: 'luluvdo',
domains: ['luluvdo.com'],
regex: [/./gm],
match: async () => {
const requestBody = new FormData();
requestBody.set('op', 'embed');
requestBody.set('file_code', lastPathSegment(window.location.href));
const response = await fetch(`https://${window.location.host}/dl`, {
method: 'POST',
body: requestBody,
referrer: window.location.href
});
let unpacked;
const responseText = await response.text();
const evalMatch = responseText.match(/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms)!;
// sometimes is packed, sometimes it's not. looks like someone forgets to obfuscate the code when pushing to
// production
if (evalMatch) {
unpacked = await unpack(evalMatch[0]);
return unpacked.match(/(?<=file:").*(?=")/)![0];
} else {
unpacked = responseText;
}
return unpacked.match(/(?<=file:").*(?=")/)![0];
}
};
export const Mixdrop: Match = { export const Mixdrop: Match = {
name: 'Mixdrop', name: 'Mixdrop',
id: 'mixdrop', id: 'mixdrop',
@ -248,11 +318,22 @@ export const Voe: Match = {
name: 'Voe', name: 'Voe',
id: 'voe', id: 'voe',
domains: ['voe.sx'], domains: ['voe.sx'],
regex: [/(?<='hls':\s*')\S*(?=')/gm], regex: [
// voe.sx
/(?<=window\.location\.href\s=\s')\S*(?=')/gm,
// whatever site voe.sx redirects to
/(?<='hls':\s*')\S*(?=')/gm
],
match: async (match: RegExpMatchArray) => { match: async (match: RegExpMatchArray) => {
if (window.location.host === 'voe.sx') {
const redirectUrl = new URL(match[0]);
await TmpHost.set(redirectUrl.host, Voe);
return null;
} else {
return atob(match[0]); return atob(match[0]);
} }
}
}; };
export const Vupload: Match = { export const Vupload: Match = {
@ -272,6 +353,8 @@ export const matches = {
[Filemoon.id]: Filemoon, [Filemoon.id]: Filemoon,
[GoodStream.id]: GoodStream, [GoodStream.id]: GoodStream,
[Kwik.id]: Kwik, [Kwik.id]: Kwik,
[LoadX.id]: LoadX,
[Luluvdo.id]: Luluvdo,
[Mixdrop.id]: Mixdrop, [Mixdrop.id]: Mixdrop,
[Mp4Upload.id]: Mp4Upload, [Mp4Upload.id]: Mp4Upload,
[Newgrounds.id]: Newgrounds, [Newgrounds.id]: Newgrounds,

6
src/lib/util/extract.ts Normal file
View File

@ -0,0 +1,6 @@
export function lastPathSegment(path: string): string {
while (path.endsWith('/')) {
path = path.slice(0, -1);
}
return path.substring(path.lastIndexOf('/') + 1);
}

View File

@ -1,9 +1,9 @@
import { defineConfig, loadEnv } from 'vite'; import { defineConfig, loadEnv, type PluginOption } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte'; import { svelte } from '@sveltejs/vite-plugin-svelte';
import webExtension from '@samrum/vite-plugin-web-extension'; import webExtension from '@samrum/vite-plugin-web-extension';
import path from 'path';
import { getManifest } from './src/manifest'; import { getManifest } from './src/manifest';
import { matches } from './src/lib/match'; import { matches } from './src/lib/match';
import { fileURLToPath } from 'url';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
@ -11,7 +11,7 @@ export default defineConfig(({ mode }) => {
return { return {
plugins: [ plugins: [
svelte(), svelte() as PluginOption,
webExtension({ webExtension({
manifest: getManifest(Number(env.MANIFEST_VERSION)), manifest: getManifest(Number(env.MANIFEST_VERSION)),
additionalInputs: { additionalInputs: {
@ -27,12 +27,10 @@ export default defineConfig(({ mode }) => {
} }
] ]
} }
}) }) as unknown as PluginOption
], ],
resolve: { resolve: {
alias: { alias: [{ find: '~', replacement: fileURLToPath(new URL('./src', import.meta.url)) }]
'~': path.resolve(__dirname, './src')
}
} }
}; };
}); });