mirror of
https://github.com/bytedream/stream-bypass.git
synced 2025-05-09 20:25:14 +02:00
Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
607326e6d6 | |||
47f6de2de8 | |||
d159fb0aa4 | |||
290733a85a | |||
84eb32ab61 | |||
927a4e3ae3 | |||
5926177920 | |||
9a29316641 | |||
c300125f64 | |||
59f2ffec57 | |||
6dff691c25 | |||
c4085f9ac8 | |||
422702b16f | |||
c3dad3681c | |||
6eb3259ec5 | |||
68e445a738 | |||
e864bc6100 | |||
3ac73ac3b4 | |||
6aaf960c28 | |||
8a13dea681 | |||
8d575241fe | |||
50f400b8b9 | |||
f6fcfd354a | |||
c081127d35 | |||
416fceba88 | |||
a9cf03c176 | |||
8852318483 | |||
7067aaf4a0 | |||
bf723e2ed6 | |||
9c80362a1c | |||
ae738ff32f | |||
16d4f2956a | |||
|
f6c6102436 | ||
|
f1525817b1 | ||
1e1d8cdfa6 | |||
47eed2d12b | |||
67e03eafaa | |||
db0ccd9b56 | |||
fd3eda9c61 | |||
e522916585 | |||
b9514284da | |||
06beaa6ebe | |||
6a56c37dd2 | |||
018b381732 |
@ -1,9 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
/release
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
package-lock.json
|
@ -1,34 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'no-undef': 'off'
|
||||
}
|
||||
};
|
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@ -9,12 +9,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install nodejs
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
@ -32,12 +32,12 @@ jobs:
|
||||
- manifest_version: 3
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install nodejs
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
@ -49,7 +49,7 @@ jobs:
|
||||
run: npm run build
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: stream-bypass-mv${{ matrix.manifest_version }}
|
||||
path: ./dist
|
||||
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
17
.prettierrc.cjs
Normal file
17
.prettierrc.cjs
Normal file
@ -0,0 +1,17 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import("prettier").Config} */
|
||||
module.exports = {
|
||||
useTabs: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'none',
|
||||
printWidth: 100,
|
||||
plugins: ['prettier-plugin-svelte', '@ianvs/prettier-plugin-sort-imports'],
|
||||
/* prettier-plugin-svelte */
|
||||
overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }],
|
||||
/* @ianvs/prettier-plugin-sort-imports */
|
||||
importOrder: ['^~/(.*)$', '^./(.*)$', ''],
|
||||
importOrderParserPlugins: ['typescript'],
|
||||
importOrderTypeScriptVersion: '5.0.0',
|
||||
importOrderCaseSensitive: false
|
||||
};
|
84
README.md
84
README.md
@ -12,9 +12,6 @@ A multi-browser addon / extension for multiple streaming providers which redirec
|
||||
<a href="https://chromewebstore.google.com/detail/ddfpfjomnakfckhmilacnbokdaknamdb">
|
||||
<img src="https://img.shields.io/chrome-web-store/users/ddfpfjomnakfckhmilacnbokdaknamdb?style=flat-square&label=Chrome%20Users" alt="Chrome Store">
|
||||
</a>
|
||||
<a href="https://addons.mozilla.org/de/firefox/addon/stream-bypass/">
|
||||
<img src="https://img.shields.io/amo/stars/stream-bypass?label=Firefox%20Store%20Stars&style=flat-square" alt="Firefox Addon Stars">
|
||||
</a>
|
||||
<a href="https://github.com/bytedream/stream-bypass/releases/latest">
|
||||
<img src="https://img.shields.io/github/downloads/ByteDream/stream-bypass/total?label=GitHub%20Downloads&style=flat-square" alt="GitHub Downloads">
|
||||
</a>
|
||||
@ -25,7 +22,9 @@ A multi-browser addon / extension for multiple streaming providers which redirec
|
||||
•
|
||||
<a href="#-installation">Installation 📥</a>
|
||||
•
|
||||
<a href="#-supported-sites">Supported Sites 📜</a>
|
||||
<a href="#-features">Features ✨</a>
|
||||
•
|
||||
<a href="#-supported-websites">Supported Websites 📜</a>
|
||||
•
|
||||
<a href="#%EF%B8%8F-building">Building 🛠️</a>
|
||||
•
|
||||
@ -51,7 +50,7 @@ Additionally, this enables you to download the video by right-clicking it and ju
|
||||
|
||||
The best way to install the extension are the official browser extension stores:
|
||||
|
||||
- [Firefox Addon Store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/)
|
||||
- [Firefox Addon Store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/) (Firefox for Android is supported too!)
|
||||
- [Chrome Web Store](https://chromewebstore.google.com/detail/ddfpfjomnakfckhmilacnbokdaknamdb)
|
||||
|
||||
### Manual installation
|
||||
@ -67,46 +66,49 @@ The best way to install the extension are the official browser extension stores:
|
||||
- Turn on the developer mode by checking the switch in the top right corner
|
||||
- Click `Load unpacked` and choose the unzipped directory
|
||||
|
||||
## 📜 Supported sites
|
||||
## ✨ Features
|
||||
|
||||
| Site | Supported | Note |
|
||||
| --------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| [dropload.io](https://dropload.io) | ✔ | |
|
||||
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔️ | |
|
||||
| [filemoon.sx](https://filemoon.sx) | ✔ | |
|
||||
| [goodstream.uno](https://goodstream.uno) | ✔ | |
|
||||
| [mcloud.to](https://mcloud.to/) | ❌ | Reverse engineering the site costs too much time ([#5](https://github.com/ByteDream/stream-bypass/issues/5)) |
|
||||
| [mixdrop.co](https://mixdrop.co) | ✔ ️ | |
|
||||
| [mp4upload.com](https://mp4upload.com) | ✔ | |
|
||||
| [newgrounds.com](https://newgrounds.com) | ✔ | |
|
||||
| [streama2s.com](https://streama2z.com) | ✔ |
|
||||
| [streamtape.com](https://streamtape.com) | ✔ | |
|
||||
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | |
|
||||
| [supervideo.tv](https://supervideo.tv) | ✔ | |
|
||||
| [upstream.to](https://upstream.to) | ✔ | |
|
||||
| [videovard.sx](https://videovard.sx) | ❌ | Reverse engineering the site costs too much time |
|
||||
| [vidmoly.me](https://vidmoly.me) | ✔ | |
|
||||
| [vidoza.net](https://vidoza.net) | ✔ | |
|
||||
| [vidstream.pro](https://vidstream.pro) | ❌ | Reverse engineering the site costs too much time ([#5](https://github.com/ByteDream/stream-bypass/issues/5)) |
|
||||
| [voe.sx](https://voe.sx) | ✔ | |
|
||||
| [vupload.com](https://vupload.com) | ✔ | |
|
||||
| [kwik.cx](https://kwik.cx) | ✔ | |
|
||||
| Feature | Firefox | Chrome | Firefox for Android |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | ------------------- |
|
||||
| Replace site-speicifc video player with browser native video player | ✔ | ✔ | ✔ |
|
||||
| Support websites that are accessed via a redirect | ✔ | ❌ | ✔ |
|
||||
| Open video in mpv (with [ff2mpv](https://github.com/ByteDream/stream-bypass/tree/master#ff2mpv-use-mpv-to-directly-play-streams)) | ✔ | ✔ | ❌ |
|
||||
|
||||
- ✔️: Supported.
|
||||
- ❌: Not supported.
|
||||
|
||||
## 📜 Supported websites
|
||||
|
||||
| Site | Firefox & Firefox for Android | Chrome & Chromium based |
|
||||
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| [dropload.io](https://dropload.io) | ✔ | ✔ |
|
||||
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔ | ⚠ (redirect probably required) |
|
||||
| [filemoon.to](https://filemoon.to) | ✔ | ✔ |
|
||||
| [goodstream.uno](https://goodstream.uno) | ✔ | ✔ |
|
||||
| [kwik.cx](https://kwik.cx) | ✔ | ✔ |
|
||||
| [loadx.ws](https://loadx.ws) | ✔ | ❌ (background request always required) |
|
||||
| [luluvdo.com](https://luluvdo.com) | ✔ | ❌ (background request always required) |
|
||||
| [mixdrop.co](https://mixdrop.co) | ✔ ️ | ✔ |
|
||||
| [mp4upload.com](https://mp4upload.com) | ✔ | ✔ |
|
||||
| [newgrounds.com](https://newgrounds.com) | ✔ | ✔ |
|
||||
| [streama2z.com](https://streama2z.com) | ✔ | ❌ (redirect always required) |
|
||||
| [streamtape.com](https://streamtape.com) | ⚠ (correct video url can't always be extract, retrying/reloading the page might fix it) | ⚠ (correct video url can't always be extract, retrying/reloading the page might fix it) |
|
||||
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | ✔ |
|
||||
| [supervideo.tv](https://supervideo.tv) | ✔ | ✔ |
|
||||
| [upstream.to](https://upstream.to) | ✔ | ✔ |
|
||||
| [vidmoly.to](https://vidmoly.me) | ✔ | ✔ |
|
||||
| [vidoza.net](https://vidoza.net) | ✔ | ✔ |
|
||||
| [voe.sx](https://voe.sx) | ✔ | ❌ (redirect always required) |
|
||||
| [vupload.com](https://vupload.com) | ✔ | ✔ |
|
||||
|
||||
- ✔️: Everything ok.
|
||||
- ⚠: Included in the addon but will probably not work. See `Note` in this case, an explanation why will stand there in the most cases.
|
||||
- ❌: Not included / supported by the addon. This can have various reasons. See `Note` for an explanation.
|
||||
- ⚠: Works with limitations.
|
||||
- ❌: Not supported.
|
||||
|
||||
_This table might not be 100% accurate, it isn't actively monitored if the addon works for every website!_
|
||||
|
||||
Some sites put much effort in obfuscating their code / how they receive the video stream so that it simply cost too much time for me to reverse engineer it and find out how to bypass the native video player of the site.
|
||||
|
||||
<details>
|
||||
<summary>Hall of dead sites</summary>
|
||||
<ul>
|
||||
<li><a href="https://evoload.io">evoload.io</a> - Down</li>
|
||||
<li><a href="https://vidlox.me">vidlox.me</a> - Reachable but empty</li>
|
||||
<li><a href="https://vivo.sx">vivo.sx</a> - Down</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
## 🛠️ Building
|
||||
|
||||
If you want to build the addon from source and not using the [installation](#installation) way, follow the instructions.
|
||||
@ -161,11 +163,11 @@ Steps to get it set up:
|
||||
- Chrome/Chromium:
|
||||
- Go To: Settings -> Extensions
|
||||
- Click on `Details` of the Stream Bypass extension and copy the ID
|
||||
- Add `chrome-extension://your-id-here/` to `allowed_origins` ->
|
||||
- Add `chrome-extension://ddfpfjomnakfckhmilacnbokdaknamdb/` to `allowed_origins` ->
|
||||
```
|
||||
"allowed_origins": [
|
||||
"chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/",
|
||||
"chrome-extension://your-id-her/"
|
||||
"chrome-extension://ddfpfjomnakfckhmilacnbokdaknamdb/"
|
||||
]
|
||||
```
|
||||
|
||||
|
40
eslint.config.mjs
Normal file
40
eslint.config.mjs
Normal file
@ -0,0 +1,40 @@
|
||||
import js from '@eslint/js';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
import svelte from 'eslint-plugin-svelte';
|
||||
import ts from 'typescript-eslint';
|
||||
|
||||
export default ts.config(
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
...svelte.configs['flat/recommended'],
|
||||
prettier,
|
||||
...svelte.configs['flat/prettier'],
|
||||
{
|
||||
files: ['**/*.svelte'],
|
||||
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: ts.parser
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'no-undef': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'.DS_Store',
|
||||
'node_modules',
|
||||
'dist',
|
||||
'release',
|
||||
'.idea',
|
||||
'.env',
|
||||
'.env.*',
|
||||
'!.env.example',
|
||||
'package-lock.json'
|
||||
]
|
||||
}
|
||||
);
|
7128
package-lock.json
generated
7128
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
44
package.json
44
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stream-bypass",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.6",
|
||||
"displayName": "Stream Bypass",
|
||||
"author": "bytedream",
|
||||
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
|
||||
@ -11,8 +11,8 @@
|
||||
"serve:firefox": "web-ext run --start-url \"about:debugging#/runtime/this-firefox\" --source-dir ./dist/",
|
||||
"serve:chrome": "web-ext run -t chromium --start-url \"https://example.com\" --source-dir ./dist/",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"lint": "prettier --check --plugin prettier-plugin-svelte . && eslint .",
|
||||
"format": "prettier --write --plugin prettier-plugin-svelte .",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write .",
|
||||
"release:firefox": "MANIFEST_VERSION=2 vite build --outDir release/firefox",
|
||||
"release:chrome": "MANIFEST_VERSION=3 vite build --outDir release/chrome"
|
||||
},
|
||||
@ -25,27 +25,27 @@
|
||||
"url": "https://github.com/bytedream/stream-bypass/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@samrum/vite-plugin-web-extension": "^5.1.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.5.3",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
|
||||
"@samrum/vite-plugin-web-extension": "^5.1.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@tsconfig/svelte": "^5.0.4",
|
||||
"@types/chrome": "^0.0.269",
|
||||
"@types/chrome": "^0.0.320",
|
||||
"@types/firefox-webext-browser": "^120.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^7.17.0",
|
||||
"@typescript-eslint/parser": "^7.17.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.43.0",
|
||||
"hls.js": "^1.5.13",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"sass": "^1.77.8",
|
||||
"svelte": "^4.2.18",
|
||||
"svelte-check": "^3.8.4",
|
||||
"svelte-preprocess": "^6.0.2",
|
||||
"tslib": "^2.6.3",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^4.5.3",
|
||||
"web-ext": "^8.2.0"
|
||||
"eslint": "^9.26.0",
|
||||
"eslint-config-prettier": "^10.1.3",
|
||||
"eslint-plugin-svelte": "^3.5.1",
|
||||
"hls.js": "^1.6.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"sass": "^1.87.0",
|
||||
"svelte": "^5.28.2",
|
||||
"svelte-check": "^4.1.7",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.0",
|
||||
"vite": "^6.3.5",
|
||||
"web-ext": "^8.6.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
@ -1,23 +1,22 @@
|
||||
import './shared';
|
||||
|
||||
import type { Match } from '~/lib/match';
|
||||
import { Redirect, storageDelete, storageGet } from '~/lib/settings';
|
||||
import { getMatch } from '~/lib/match';
|
||||
import { getMatch, type Match } from '~/lib/match';
|
||||
import { Redirect, UrlReferer } from '~/lib/settings';
|
||||
|
||||
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
async (details) => {
|
||||
const referer: { domain: string } | undefined = await storageGet('referer');
|
||||
if (referer === undefined) return;
|
||||
const referer = await UrlReferer.get(new URL(details.url).hostname);
|
||||
if (!referer) return;
|
||||
|
||||
await UrlReferer.delete(new URL(details.url).hostname);
|
||||
|
||||
details.requestHeaders!.push({
|
||||
name: 'Referer',
|
||||
value: `https://${referer.domain}/`
|
||||
value: `https://${referer}/`
|
||||
});
|
||||
|
||||
await storageDelete('referer');
|
||||
|
||||
return { requestHeaders: details.requestHeaders };
|
||||
},
|
||||
{ urls: ['<all_urls>'], types: ['xmlhttprequest'] },
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { Match } from '~/lib/match';
|
||||
import { getMatch } from '~/lib/match';
|
||||
import { getMatch, MatchMediaType, type Match } from '~/lib/match';
|
||||
import { Other, Redirect } from '~/lib/settings';
|
||||
|
||||
async function main() {
|
||||
@ -20,22 +19,43 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
const re = document.body.innerHTML.match(match.regex);
|
||||
let re = null;
|
||||
for (const regex of match.regex) {
|
||||
if ((re = document.body.innerHTML.match(regex)) !== null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (re === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (redirect) {
|
||||
await Redirect.delete();
|
||||
}
|
||||
|
||||
let url: string | null;
|
||||
let urlType: MatchMediaType | null;
|
||||
try {
|
||||
url = await match.match(re);
|
||||
} catch (e) {
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (!url || !urlType) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -44,7 +64,7 @@ async function main() {
|
||||
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
|
||||
let intervalId = window.setInterval(() => {}, 0);
|
||||
while (intervalId--) {
|
||||
@ -58,6 +78,8 @@ async function main() {
|
||||
// clear completed document
|
||||
document.documentElement.innerHTML = '';
|
||||
|
||||
document.body.style.backgroundColor = '#131313';
|
||||
|
||||
// video player
|
||||
const player = document.createElement('video');
|
||||
player.style.width = '100%';
|
||||
@ -73,7 +95,7 @@ async function main() {
|
||||
chrome.runtime.getURL(
|
||||
`src/entries/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}&domain=${
|
||||
window.location.hostname
|
||||
}`
|
||||
}&type=${urlType}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { play } from '~/entries/player/player';
|
||||
import { onMount } from 'svelte';
|
||||
import { play } from '~/entries/player/player';
|
||||
|
||||
let errorMessage: string | null = $state(null);
|
||||
|
||||
let errorMessage: string | null = null;
|
||||
let videoElem: HTMLVideoElement;
|
||||
|
||||
onMount(async () => {
|
||||
@ -10,13 +11,13 @@
|
||||
await play(videoElem);
|
||||
videoElem.controls = true;
|
||||
} catch (e) {
|
||||
errorMessage = e;
|
||||
errorMessage = e as string;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video id="video" bind:this={videoElem} />
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video id="video" bind:this={videoElem}></video>
|
||||
{#if errorMessage}
|
||||
<div id="message-container">
|
||||
<p>
|
||||
|
@ -6,9 +6,10 @@
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
import { mount } from 'svelte';
|
||||
import Player from '~/entries/player/Player.svelte';
|
||||
|
||||
new Player({
|
||||
mount(Player, {
|
||||
target: document.body
|
||||
});
|
||||
</script>
|
||||
|
@ -1,17 +1,23 @@
|
||||
import { matches } from '~/lib/match';
|
||||
import Hls from 'hls.js';
|
||||
import { storageSet } from '~/lib/settings';
|
||||
import { matches, MatchMediaType } from '~/lib/match';
|
||||
import { UrlReferer } from '~/lib/settings';
|
||||
|
||||
async function playNative(url: string, domain: string, videoElem: HTMLVideoElement) {
|
||||
await UrlReferer.set(new URL(url).hostname, domain);
|
||||
|
||||
async function playNative(url: string, videoElem: HTMLVideoElement) {
|
||||
videoElem.src = url;
|
||||
}
|
||||
|
||||
async function playHls(url: string, videoElem: HTMLVideoElement) {
|
||||
async function playHls(url: string, domain: string, videoElem: HTMLVideoElement) {
|
||||
if (videoElem.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
videoElem.src = url;
|
||||
} else if (Hls.isSupported()) {
|
||||
const hls = new Hls({
|
||||
enableWorker: false
|
||||
enableWorker: false,
|
||||
xhrSetup: async (xhr: XMLHttpRequest, url: string) => {
|
||||
await UrlReferer.set(new URL(url).hostname, domain);
|
||||
xhr.open('GET', url);
|
||||
}
|
||||
});
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(videoElem);
|
||||
@ -25,6 +31,7 @@ export async function play(videoElem: HTMLVideoElement) {
|
||||
const id = urlQuery.get('id') as string;
|
||||
const url = decodeURIComponent(urlQuery.get('url') as string);
|
||||
const domain = urlQuery.get('domain') as string;
|
||||
const type = urlQuery.get('type') as MatchMediaType;
|
||||
|
||||
const match = matches[id];
|
||||
if (match === undefined) {
|
||||
@ -32,11 +39,9 @@ export async function play(videoElem: HTMLVideoElement) {
|
||||
}
|
||||
document.title = `Stream Bypass (${domain})`;
|
||||
|
||||
await storageSet('referer', { domain: domain });
|
||||
|
||||
if (new URL(url).pathname.endsWith('.m3u8')) {
|
||||
await playHls(url, videoElem);
|
||||
} else {
|
||||
await playNative(url, videoElem);
|
||||
if (type === MatchMediaType.Hls) {
|
||||
await playHls(url, domain, videoElem);
|
||||
} else if (type === MatchMediaType.Native) {
|
||||
await playNative(url, domain, videoElem);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { type Match, matches } from '~/lib/match';
|
||||
import { matches, type Match } from '~/lib/match';
|
||||
import { Hosters, Other } from '~/lib/settings';
|
||||
import Toggle from './toggle.svelte';
|
||||
|
||||
@ -39,11 +39,11 @@
|
||||
<Toggle
|
||||
bind:checked={hostersEnabled}
|
||||
id="hosters-enabled"
|
||||
on:change={() => Hosters.setAll(hostersEnabled)}
|
||||
onChange={() => Hosters.setAll(hostersEnabled)}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
{#each hosters as hoster, i}
|
||||
{#each hosters as hoster, i (hoster.id)}
|
||||
<label for="hoster-{i}" style="cursor: {hostersEnabled ? 'pointer' : 'default'}"
|
||||
>{hoster.name}</label
|
||||
>
|
||||
@ -52,7 +52,7 @@
|
||||
bind:checked={hoster.active}
|
||||
disabled={!hostersEnabled}
|
||||
id="hoster-{i}"
|
||||
on:change={async () => {
|
||||
onChange={async () => {
|
||||
if (hoster.active) {
|
||||
await Hosters.enable(hoster);
|
||||
} else {
|
||||
@ -73,7 +73,7 @@
|
||||
<Toggle
|
||||
bind:checked={ff2mpvEnabled}
|
||||
id="ff2mpv"
|
||||
on:change={async () => {
|
||||
onChange={async () => {
|
||||
ff2mpvEnabled = !ff2mpvEnabled;
|
||||
if (await browser.permissions.request({ permissions: ['nativeMessaging'] })) {
|
||||
await Other.setFf2mpv(ff2mpvEnabled);
|
||||
|
@ -7,9 +7,10 @@
|
||||
</head>
|
||||
<body style="overflow-y: scroll">
|
||||
<script type="module">
|
||||
import { mount } from 'svelte';
|
||||
import Popup from '~/entries/popup/Popup.svelte';
|
||||
|
||||
new Popup({
|
||||
mount(Popup, {
|
||||
target: document.body
|
||||
});
|
||||
</script>
|
||||
|
@ -1,5 +0,0 @@
|
||||
import App from './Popup.svelte';
|
||||
|
||||
new App({
|
||||
target: document.getElementById('app') as Element
|
||||
});
|
@ -1,18 +1,21 @@
|
||||
<!-- https://flowbite.com/docs/forms/toggle/ -->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
export let checked = false;
|
||||
export let disabled = false;
|
||||
export let id: string | null = null;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
type Props = {
|
||||
checked?: boolean;
|
||||
disabled?: boolean;
|
||||
id?: string | null;
|
||||
onChange?: () => void;
|
||||
children?: Snippet;
|
||||
};
|
||||
let { checked = $bindable(), disabled, id = null, onChange, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<label class="toggle">
|
||||
<slot />
|
||||
<input type="checkbox" {id} bind:checked {disabled} on:change={(e) => dispatch('change', e)} />
|
||||
<span />
|
||||
{@render children?.()}
|
||||
<input type="checkbox" {id} bind:checked {disabled} onchange={onChange} />
|
||||
<span></span>
|
||||
</label>
|
||||
|
||||
<style lang="scss" global>
|
||||
|
235
src/lib/match.ts
235
src/lib/match.ts
@ -1,15 +1,28 @@
|
||||
import { unpack } from './utils';
|
||||
import { Hosters, Redirect } from './settings';
|
||||
import { Hosters, Redirect, TmpHost } from './settings';
|
||||
import { lastPathSegment } from './util/extract';
|
||||
import { unpack } from './util/userspace';
|
||||
|
||||
export interface Match {
|
||||
name: string;
|
||||
id: string;
|
||||
domains: string[];
|
||||
replace?: boolean;
|
||||
regex: RegExp;
|
||||
regex: RegExp[];
|
||||
notice?: string;
|
||||
|
||||
match(match: RegExpMatchArray): Promise<string | null>;
|
||||
match(
|
||||
match: RegExpMatchArray
|
||||
): Promise<
|
||||
string | { [MatchMediaType.Hls]: string } | { [MatchMediaType.Native]: string } | null
|
||||
>;
|
||||
|
||||
// allow other properties that may be implemented by the objects that use this interface declaration
|
||||
[other: string]: any;
|
||||
}
|
||||
|
||||
export enum MatchMediaType {
|
||||
Hls = 'hls',
|
||||
Native = 'native'
|
||||
}
|
||||
|
||||
export const Doodstream: Match = {
|
||||
@ -23,6 +36,7 @@ export const Doodstream: Match = {
|
||||
'dood.cx',
|
||||
'dood.sh',
|
||||
'dood.watch',
|
||||
'dood.work',
|
||||
'dood.to',
|
||||
'dood.so',
|
||||
'dood.la',
|
||||
@ -35,9 +49,9 @@ export const Doodstream: Match = {
|
||||
'd000d.com'
|
||||
],
|
||||
replace: true,
|
||||
regex: /(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s,
|
||||
regex: [/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
||||
headers: {
|
||||
Range: 'bytes=0-'
|
||||
@ -53,10 +67,10 @@ export const Doodstream: Match = {
|
||||
export const DropLoad: Match = {
|
||||
name: 'Dropload',
|
||||
id: 'dropload',
|
||||
domains: ['dropload.ui'],
|
||||
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||
domains: ['dropload.io'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
}
|
||||
@ -65,12 +79,20 @@ export const DropLoad: Match = {
|
||||
export const Filemoon: Match = {
|
||||
name: 'Filemoon',
|
||||
id: 'filemoon',
|
||||
domains: ['filemoon.sx', 'filemoon.in'],
|
||||
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||
domains: ['filemoon.sx', 'filemoon.to', 'filemoon.in'],
|
||||
regex: [/(?<=<iframe\s*src=")\S*(?=")/s, /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
replace: true,
|
||||
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (window.location.host.startsWith('filemoon')) {
|
||||
await TmpHost.set(new URL(match[0]).host, Filemoon);
|
||||
return null;
|
||||
}
|
||||
|
||||
await TmpHost.delete();
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
return unpacked.match(/(?<=file:")\S*(?=")/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
@ -78,9 +100,9 @@ export const GoodStream: Match = {
|
||||
name: 'Goodstream',
|
||||
id: 'goodstream',
|
||||
domains: ['goodstream.uno'],
|
||||
regex: /(?<=file:\s+").*(?=")/g,
|
||||
regex: [/(?<=file:\s+").*(?=")/g],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
@ -89,21 +111,80 @@ export const Kwik: Match = {
|
||||
name: 'Kwik',
|
||||
id: 'kwik',
|
||||
domains: ['kwik.cx'],
|
||||
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=source=').*(?=')/)![0];
|
||||
}
|
||||
};
|
||||
|
||||
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 = {
|
||||
name: 'Mixdrop',
|
||||
id: 'mixdrop',
|
||||
domains: ['mixdrop.co', 'mixdrop.to', 'mixdrop.ch', 'mixdrop.bz', 'mixdrop.gl'],
|
||||
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||
domains: ['mixdrop.bz', 'mixdrop.ch', 'mixdrop.co', 'mixdrop.gl', 'mixdrop.my', 'mixdrop.to'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
const url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)![0];
|
||||
return `https:${url}`;
|
||||
@ -115,9 +196,9 @@ export const Mp4Upload: Match = {
|
||||
id: 'mp4upload',
|
||||
domains: ['mp4upload.com'],
|
||||
replace: true,
|
||||
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=player.src\(").*(?=")/)![0];
|
||||
}
|
||||
@ -127,7 +208,7 @@ export const Newgrounds: Match = {
|
||||
name: 'Newgrounds',
|
||||
id: 'newgrounds',
|
||||
domains: ['newgrounds.com'],
|
||||
regex: /.*/gm,
|
||||
regex: [/.*/gm],
|
||||
|
||||
match: async () => {
|
||||
const id = window.location.pathname.split('/').slice(-1)[0];
|
||||
@ -145,9 +226,9 @@ export const StreamA2z: Match = {
|
||||
name: 'Stream2Az',
|
||||
id: 'stream2az',
|
||||
domains: ['streama2z.com', 'streama2z.xyz'],
|
||||
regex: /https?:\/\/\S*m3u8.+(?=['"])/gm,
|
||||
regex: [/https?:\/\/\S*m3u8.+(?=['"])/gm],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (StreamA2z.domains.indexOf(window.location.hostname) !== -1) {
|
||||
await Redirect.set(StreamA2z);
|
||||
return null;
|
||||
@ -160,9 +241,9 @@ export const Streamtape: Match = {
|
||||
name: 'Streamtape',
|
||||
id: 'streamtape',
|
||||
domains: ['streamtape.com', 'streamtape.net', 'shavetape.cash'],
|
||||
regex: /id=.*(?=')/gm,
|
||||
regex: [/id=.*(?=')/gm],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
let i = 0;
|
||||
while (i < match.length) {
|
||||
if (match[++i - 1] == match[i]) {
|
||||
@ -179,9 +260,9 @@ export const Streamzz: Match = {
|
||||
name: 'Streamzz',
|
||||
id: 'streamzz',
|
||||
domains: ['streamzz.to', 'streamz.ws'],
|
||||
regex: /(?<=\|)\w{2,}/gm,
|
||||
regex: [/(?<=\|)\w{2,}/gm],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return `https://get.${location.hostname.split('.')[0]}.tw/getlink-${
|
||||
match.sort((a, b) => b.length - a.length)[0]
|
||||
}.dll`;
|
||||
@ -191,10 +272,10 @@ export const Streamzz: Match = {
|
||||
export const SuperVideo: Match = {
|
||||
name: 'Supervideo',
|
||||
id: 'supervideo',
|
||||
domains: ['supervideo.tv'],
|
||||
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||
domains: ['supervideo.cc', 'supervideo.tv'],
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
}
|
||||
@ -204,9 +285,9 @@ export const Upstream: Match = {
|
||||
name: 'Upstream',
|
||||
id: 'upstream',
|
||||
domains: ['upstream.to'],
|
||||
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
const unpacked = await unpack(match[0]);
|
||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
||||
}
|
||||
@ -216,10 +297,10 @@ export const Vidmoly: Match = {
|
||||
name: 'Vidmoly',
|
||||
id: 'vidmoly',
|
||||
domains: ['vidmoly.me', 'vidmoly.to'],
|
||||
regex: /(?<=file:").+\.m3u8/gm,
|
||||
regex: [/(?<=file:").+\.m3u8/gm],
|
||||
replace: true,
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
alert('a');
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
@ -227,11 +308,11 @@ export const Vidmoly: Match = {
|
||||
export const Vidoza: Match = {
|
||||
name: 'Vidoza',
|
||||
id: 'vidoza',
|
||||
domains: ['vidoza.net'],
|
||||
regex: /(?<=src:\s?").+?(?=")/gm,
|
||||
domains: ['vidoza.net', 'videzz.net'],
|
||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
||||
replace: true,
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
@ -240,10 +321,67 @@ export const Voe: Match = {
|
||||
name: 'Voe',
|
||||
id: 'voe',
|
||||
domains: ['voe.sx'],
|
||||
regex: /https?:\/\/\S*m3u8.+(?=['"])/gm,
|
||||
regex: [
|
||||
// voe.sx
|
||||
/(?<=window\.location\.href\s=\s')\S*(?=')/gm,
|
||||
// whatever site voe.sx redirects to
|
||||
/(?<=<script type="application\/json">).*(?=<\/script>)/m
|
||||
],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
return match[0];
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
if (window.location.host === 'voe.sx') {
|
||||
const redirectUrl = new URL(match[0]);
|
||||
await TmpHost.set(redirectUrl.host, Voe);
|
||||
return null;
|
||||
} else {
|
||||
let json = match[0];
|
||||
json = JSON.parse(json);
|
||||
|
||||
let deobfuscated = json[0];
|
||||
deobfuscated = this.rot13(deobfuscated);
|
||||
deobfuscated = this.removeSpecialSequences(deobfuscated);
|
||||
deobfuscated = atob(deobfuscated);
|
||||
deobfuscated = this.shiftString(deobfuscated);
|
||||
deobfuscated = deobfuscated.split('').reverse().join('');
|
||||
deobfuscated = atob(deobfuscated);
|
||||
|
||||
const payload = JSON.parse(deobfuscated);
|
||||
|
||||
return payload['source'];
|
||||
}
|
||||
},
|
||||
|
||||
rot13: function (encrypted: string) {
|
||||
let decrypted = '';
|
||||
for (let i = 0; i < encrypted.length; i++) {
|
||||
let char = encrypted.charCodeAt(i);
|
||||
if (char >= 65 && char <= 90) {
|
||||
char = ((char - 65 + 13) % 26) + 65;
|
||||
} else if (char >= 97 && char <= 122) {
|
||||
char = ((char - 97 + 13) % 26) + 97;
|
||||
}
|
||||
decrypted += String.fromCharCode(char);
|
||||
}
|
||||
return decrypted;
|
||||
},
|
||||
removeSpecialSequences: function (input: string) {
|
||||
return input
|
||||
.replaceAll(/@\$/g, '')
|
||||
.replaceAll(/\^\^/g, '')
|
||||
.replaceAll(/~@/g, '')
|
||||
.replaceAll(/%\?/g, '')
|
||||
.replaceAll(/\*~/g, '')
|
||||
.replaceAll(/!!/g, '')
|
||||
.replaceAll(/#&/g, '');
|
||||
},
|
||||
shiftString: function (input: string) {
|
||||
let shifted = '';
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const char = input.charCodeAt(i);
|
||||
const shiftedChar = char - 3;
|
||||
shifted += String.fromCharCode(shiftedChar);
|
||||
}
|
||||
return shifted;
|
||||
}
|
||||
};
|
||||
|
||||
@ -251,9 +389,9 @@ export const Vupload: Match = {
|
||||
name: 'Vupload',
|
||||
id: 'vupload',
|
||||
domains: ['vupload.com'],
|
||||
regex: /(?<=src:\s?").+?(?=")/gm,
|
||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
match: async function (match: RegExpMatchArray) {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
@ -264,6 +402,8 @@ export const matches = {
|
||||
[Filemoon.id]: Filemoon,
|
||||
[GoodStream.id]: GoodStream,
|
||||
[Kwik.id]: Kwik,
|
||||
[LoadX.id]: LoadX,
|
||||
[Luluvdo.id]: Luluvdo,
|
||||
[Mixdrop.id]: Mixdrop,
|
||||
[Mp4Upload.id]: Mp4Upload,
|
||||
[Newgrounds.id]: Newgrounds,
|
||||
@ -292,5 +432,10 @@ export async function getMatch(domain: string): Promise<Match | null> {
|
||||
}
|
||||
}
|
||||
|
||||
const tmpHost = await TmpHost.get();
|
||||
if (tmpHost && tmpHost[0] === domain) {
|
||||
return tmpHost[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { Match } from './match';
|
||||
import { matches } from './match';
|
||||
import { matches, type Match } from './match';
|
||||
|
||||
export const Hosters = {
|
||||
getDisabled: async () => {
|
||||
@ -42,6 +41,34 @@ export const Redirect = {
|
||||
}
|
||||
};
|
||||
|
||||
export const TmpHost = {
|
||||
get: async (): Promise<[string, Match] | null> => {
|
||||
const tmphost = await storageGet<[string, number]>('tmphost');
|
||||
if (tmphost === undefined) {
|
||||
return null;
|
||||
}
|
||||
return [tmphost[0], matches[tmphost[1]]];
|
||||
},
|
||||
set: async (domain: string, match: Match) => {
|
||||
await storageSet('tmphost', [domain, match.id]);
|
||||
},
|
||||
delete: async () => {
|
||||
await storageDelete('tmphost');
|
||||
}
|
||||
};
|
||||
|
||||
export const UrlReferer = {
|
||||
get: async (url: string): Promise<string | null> => {
|
||||
return (await storageGet(`urlReferer.${url}`)) || null;
|
||||
},
|
||||
set: async (url: string, referer: string) => {
|
||||
await storageSet(`urlReferer.${url}`, referer);
|
||||
},
|
||||
delete: async (url: string) => {
|
||||
await storageDelete(`urlReferer.${url}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const Other = {
|
||||
getFf2mpv: async () => {
|
||||
return await storageGet('other.ff2mpv', false);
|
||||
@ -51,7 +78,7 @@ export const Other = {
|
||||
}
|
||||
};
|
||||
|
||||
export async function storageGet<T>(key: string, defaultValue?: T): Promise<T | undefined> {
|
||||
async function storageGet<T>(key: string, defaultValue?: T): Promise<T | undefined> {
|
||||
let resolve: (value: T | undefined) => void;
|
||||
const promise = new Promise<T | undefined>((r) => (resolve = r));
|
||||
|
||||
@ -63,7 +90,7 @@ export async function storageGet<T>(key: string, defaultValue?: T): Promise<T |
|
||||
return promise;
|
||||
}
|
||||
|
||||
export async function storageSet<T>(key: string, value: T) {
|
||||
async function storageSet<T>(key: string, value: T) {
|
||||
let resolve: () => void;
|
||||
const promise = new Promise<void>((r) => (resolve = r));
|
||||
|
||||
@ -75,7 +102,7 @@ export async function storageSet<T>(key: string, value: T) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
export async function storageDelete(key: string) {
|
||||
async function storageDelete(key: string) {
|
||||
let resolve: () => void;
|
||||
const promise = new Promise<void>((r) => (resolve = r));
|
||||
|
||||
|
6
src/lib/util/extract.ts
Normal file
6
src/lib/util/extract.ts
Normal 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);
|
||||
}
|
@ -16,7 +16,7 @@ export async function unpack(packed: string): Promise<string> {
|
||||
${packed}
|
||||
};
|
||||
return packed;
|
||||
}'
|
||||
}
|
||||
`;
|
||||
|
||||
const res = (await runInPageContext(toExecute)) as string;
|
||||
@ -32,8 +32,9 @@ export async function unpack(packed: string): Promise<string> {
|
||||
async function runInPageContext<T>(toExecute: string): Promise<T | null> {
|
||||
// test that we are running with the allow-scripts permission
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
window.sessionStorage;
|
||||
} catch (ignore) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -48,18 +49,17 @@ async function runInPageContext<T>(toExecute: string): Promise<T | null> {
|
||||
scriptElm.textContent = `
|
||||
(
|
||||
async function () {
|
||||
const response = {
|
||||
id: '${resultMessageId}'
|
||||
};
|
||||
|
||||
const response = {
|
||||
id: ${resultMessageId}
|
||||
};
|
||||
|
||||
try {
|
||||
response.result = JSON.stringify(await (${toExecute})() ); // run script
|
||||
} catch(err) {
|
||||
response.error = JSON.stringify(err);
|
||||
}
|
||||
try {
|
||||
response.result = JSON.stringify(await (${toExecute})() ); // run script
|
||||
} catch(err) {
|
||||
response.error = JSON.stringify(err);
|
||||
}
|
||||
|
||||
window.postMessage(response, '*');
|
||||
window.postMessage(response, '*');
|
||||
}
|
||||
)();
|
||||
`;
|
@ -2,7 +2,7 @@
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2019",
|
||||
"target": "es2021",
|
||||
"module": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"resolveJsonModule": true,
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
import { fileURLToPath } from 'url';
|
||||
import webExtension from '@samrum/vite-plugin-web-extension';
|
||||
import path from 'path';
|
||||
import { getManifest } from './src/manifest';
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
import { defineConfig, loadEnv, type PluginOption } from 'vite';
|
||||
import { matches } from './src/lib/match';
|
||||
import { getManifest } from './src/manifest';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => {
|
||||
@ -11,7 +11,7 @@ export default defineConfig(({ mode }) => {
|
||||
|
||||
return {
|
||||
plugins: [
|
||||
svelte(),
|
||||
svelte() as PluginOption,
|
||||
webExtension({
|
||||
manifest: getManifest(Number(env.MANIFEST_VERSION)),
|
||||
additionalInputs: {
|
||||
@ -27,12 +27,10 @@ export default defineConfig(({ mode }) => {
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}) as unknown as PluginOption
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': path.resolve(__dirname, './src')
|
||||
}
|
||||
alias: [{ find: '~', replacement: fileURLToPath(new URL('./src', import.meta.url)) }]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user