mirror of
https://github.com/bytedream/stream-bypass.git
synced 2025-05-13 22:20:59 +02:00
Compare commits
No commits in common. "main" and "v3.0.0" have entirely different histories.
9
.eslintignore
Normal file
9
.eslintignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
/release
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
package-lock.json
|
33
.eslintrc.cjs
Normal file
33
.eslintrc.cjs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
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'
|
||||||
|
}
|
||||||
|
};
|
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@ -9,12 +9,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install nodejs
|
- name: Install nodejs
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 18
|
||||||
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@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install nodejs
|
- name: Install nodejs
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 18
|
||||||
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@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: stream-bypass-mv${{ matrix.manifest_version }}
|
name: stream-bypass-mv${{ matrix.manifest_version }}
|
||||||
path: ./dist
|
path: ./dist
|
||||||
|
8
.prettierrc
Normal file
8
.prettierrc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"useTabs": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 100,
|
||||||
|
"plugins": ["prettier-plugin-svelte"],
|
||||||
|
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
// @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
|
|
||||||
};
|
|
103
README.md
103
README.md
@ -3,18 +3,21 @@
|
|||||||
A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.
|
A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/bytedream/stream-bypass/releases/latest">
|
<a href="https://github.com/ByteDream/stream-bypass/releases/latest">
|
||||||
<img src="https://img.shields.io/github/v/release/ByteDream/stream-bypass?label=Version&style=flat-square" alt="Version">
|
<img src="https://img.shields.io/github/v/release/ByteDream/stream-bypass?label=Version&style=flat-square" alt="Version">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://addons.mozilla.org/de/firefox/addon/stream-bypass/">
|
<a href="https://addons.mozilla.org/de/firefox/addon/stream-bypass/">
|
||||||
<img src="https://img.shields.io/amo/users/stream-bypass?label=Firefox%20Users&style=flat-square" alt="Firefox Addon Store">
|
<img src="https://img.shields.io/amo/users/stream-bypass?label=Firefox%20Store%20Downloads&style=flat-square" alt="Firefox Addon Store">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://chromewebstore.google.com/detail/ddfpfjomnakfckhmilacnbokdaknamdb">
|
<a href="https://addons.mozilla.org/de/firefox/addon/stream-bypass/">
|
||||||
<img src="https://img.shields.io/chrome-web-store/users/ddfpfjomnakfckhmilacnbokdaknamdb?style=flat-square&label=Chrome%20Users" alt="Chrome Store">
|
<img src="https://img.shields.io/amo/stars/stream-bypass?label=Firefox%20Store%20Stars&style=flat-square" alt="Firefox Addon Stars">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/bytedream/stream-bypass/releases/latest">
|
<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">
|
<img src="https://img.shields.io/github/downloads/ByteDream/stream-bypass/total?label=GitHub%20Downloads&style=flat-square" alt="GitHub Downloads">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://discord.gg/gUWwekeNNg">
|
||||||
|
<img src="https://img.shields.io/discord/915659846836162561?label=Discord&style=flat-square" alt="Discord">
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -22,9 +25,7 @@ A multi-browser addon / extension for multiple streaming providers which redirec
|
|||||||
•
|
•
|
||||||
<a href="#-installation">Installation 📥</a>
|
<a href="#-installation">Installation 📥</a>
|
||||||
•
|
•
|
||||||
<a href="#-features">Features ✨</a>
|
<a href="#-supported-sites">Supported Sites 📜</a>
|
||||||
•
|
|
||||||
<a href="#-supported-websites">Supported Websites 📜</a>
|
|
||||||
•
|
•
|
||||||
<a href="#%EF%B8%8F-building">Building 🛠️</a>
|
<a href="#%EF%B8%8F-building">Building 🛠️</a>
|
||||||
•
|
•
|
||||||
@ -50,8 +51,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:
|
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 for Android is supported too!)
|
- [Firefox Addon Store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/)
|
||||||
- [Chrome Web Store](https://chromewebstore.google.com/detail/ddfpfjomnakfckhmilacnbokdaknamdb)
|
|
||||||
|
|
||||||
### Manual installation
|
### Manual installation
|
||||||
|
|
||||||
@ -61,54 +61,49 @@ The best way to install the extension are the official browser extension stores:
|
|||||||
- Click the `Load Temporary Add-on...` button and choose the `manifest.json` file in the unzipped directory
|
- Click the `Load Temporary Add-on...` button and choose the `manifest.json` file in the unzipped directory
|
||||||
- Chromium / Google Chrome
|
- Chromium / Google Chrome
|
||||||
> As nearly every browser other than Firefox is based on Chromium, this should be the same for most of them
|
> As nearly every browser other than Firefox is based on Chromium, this should be the same for most of them
|
||||||
- Download `stream-bypass-<version>-mv3.zip` from the [latest release](https://github.com/ByteDream/stream-bypass/releases/latest) and unzip it (with [7zip](https://www.7-zip.org/) or something like that)
|
- Download `enhance-crunchyroll-<version>-mv3.zip` from the [latest release](https://github.com/ByteDream/stream-bypass/releases/latest) and unzip it (with [7zip](https://www.7-zip.org/) or something like that)
|
||||||
- Go into your browser and type `chrome://extensions` in the address bar
|
- Go into your browser and type `chrome://extensions` in the address bar
|
||||||
- Turn on the developer mode by checking the switch in the top right corner
|
- Turn on the developer mode by checking the switch in the top right corner
|
||||||
- Click `Load unpacked` and choose the unzipped directory
|
- Click `Load unpacked` and choose the unzipped directory
|
||||||
|
|
||||||
## ✨ Features
|
## 📜 Supported sites
|
||||||
|
|
||||||
| Feature | Firefox | Chrome | Firefox for Android |
|
| Site | Supported | Note |
|
||||||
| --------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | ------------------- |
|
| --------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------ |
|
||||||
| Replace site-speicifc video player with browser native video player | ✔ | ✔ | ✔ |
|
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔️ | |
|
||||||
| Support websites that are accessed via a redirect | ✔ | ❌ | ✔ |
|
| [filemoon.sx](https://filemoon.sx) | ✔ | |
|
||||||
| Open video in mpv (with [ff2mpv](https://github.com/ByteDream/stream-bypass/tree/master#ff2mpv-use-mpv-to-directly-play-streams)) | ✔ | ✔ | ❌ |
|
| [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) | ✔ ️ | |
|
||||||
- ✔️: Supported.
|
| [mp4upload.com](https://mp4upload.com) | ✔ | |
|
||||||
- ❌: Not supported.
|
| [newgrounds.com](https://newgrounds.com) | ✔ | |
|
||||||
|
| [streamtape.com](https://streamtape.com) | ✔ | |
|
||||||
## 📜 Supported websites
|
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | |
|
||||||
|
| [upstream.to](https://upstream.to) | ✔ | |
|
||||||
| Site | Firefox & Firefox for Android | Chrome & Chromium based |
|
| [videovard.sx](https://videovard.sx) | ❌ | Reverse engineering the site costs too much time |
|
||||||
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
| [vidoza.net](https://vidoza.net) | ✔ | |
|
||||||
| [dropload.io](https://dropload.io) | ✔ | ✔ |
|
| [vidstream.pro](https://vidstream.pro) | ❌ | Reverse engineering the site costs too much time ([#5](https://github.com/ByteDream/stream-bypass/issues/5)) |
|
||||||
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔ | ⚠ (redirect probably required) |
|
| [voe.sx](https://voe.sx) | ✔ | |
|
||||||
| [filemoon.to](https://filemoon.to) | ✔ | ✔ |
|
| [vupload.com](https://vupload.com) | ✔ | |
|
||||||
| [goodstream.uno](https://goodstream.uno) | ✔ | ✔ |
|
| [kwik.cx](https://kwik.cx) | ✔ | |
|
||||||
| [kwik.cx](https://kwik.cx) | ✔ | ✔ |
|
| [dropload.io](https://dropload.io) | ✔ | |
|
||||||
| [loadx.ws](https://loadx.ws) | ✔ | ❌ (background request always required) |
|
| [supervideo.tv](https://supervideo.tv) | ✔ | |
|
||||||
| [luluvdo.com](https://luluvdo.com) | ✔ | ❌ (background request always required) |
|
| [goodstream.uno](https://goodstream.uno) | ✔ | |
|
||||||
| [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.
|
- ✔️: Everything ok.
|
||||||
- ⚠: Works with limitations.
|
- ⚠: 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 supported.
|
- ❌: Not included / supported by the addon. This can have various reasons. See `Note` for an explanation.
|
||||||
|
|
||||||
_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.
|
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
|
## 🛠️ Building
|
||||||
|
|
||||||
If you want to build the addon from source and not using the [installation](#installation) way, follow the instructions.
|
If you want to build the addon from source and not using the [installation](#installation) way, follow the instructions.
|
||||||
@ -124,16 +119,16 @@ If the requirements are satisfied, you can continue with the following commands:
|
|||||||
# install all dependencies
|
# install all dependencies
|
||||||
$ npm install
|
$ npm install
|
||||||
|
|
||||||
# build the extension source to the dist/ directory
|
# build the extension source to a build/ directory
|
||||||
$ npm run build
|
$ npm run build
|
||||||
|
|
||||||
# same as build + more optimizations and browser specific settings at release/
|
# same as build + create a bundle zipfile at dist/
|
||||||
$ npm run release:firefox # or "release:chrome" to create a release for chromium based browsers
|
$ npm run bundle
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Install
|
##### Install
|
||||||
|
|
||||||
If you want to use the addon in Chromium or any browser which is based on it, follow the steps in [installation](#-installation).
|
If you want to use the addon in Chromium or any browser which is based on it (almost every other, Google Chrome, Opera, ...), follow the steps in [installation](#-installation).
|
||||||
When using firefox, use the following:
|
When using firefox, use the following:
|
||||||
|
|
||||||
1. Type `about:debugging` in the browser's address bar.
|
1. Type `about:debugging` in the browser's address bar.
|
||||||
@ -163,11 +158,11 @@ Steps to get it set up:
|
|||||||
- Chrome/Chromium:
|
- Chrome/Chromium:
|
||||||
- Go To: Settings -> Extensions
|
- Go To: Settings -> Extensions
|
||||||
- Click on `Details` of the Stream Bypass extension and copy the ID
|
- Click on `Details` of the Stream Bypass extension and copy the ID
|
||||||
- Add `chrome-extension://ddfpfjomnakfckhmilacnbokdaknamdb/` to `allowed_origins` ->
|
- Add `chrome-extension://your-id-here/` to `allowed_origins` ->
|
||||||
```
|
```
|
||||||
"allowed_origins": [
|
"allowed_origins": [
|
||||||
"chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/",
|
"chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/",
|
||||||
"chrome-extension://ddfpfjomnakfckhmilacnbokdaknamdb/"
|
"chrome-extension://your-id-her/"
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
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'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
11345
package-lock.json
generated
11345
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
55
package.json
55
package.json
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "stream-bypass",
|
"name": "stream-bypass",
|
||||||
"version": "3.1.6",
|
"version": "3.0.0",
|
||||||
"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",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
@ -11,41 +11,42 @@
|
|||||||
"serve:firefox": "web-ext run --start-url \"about:debugging#/runtime/this-firefox\" --source-dir ./dist/",
|
"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/",
|
"serve:chrome": "web-ext run -t chromium --start-url \"https://example.com\" --source-dir ./dist/",
|
||||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check --plugin prettier-plugin-svelte . && eslint .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write --plugin prettier-plugin-svelte .",
|
||||||
"release:firefox": "MANIFEST_VERSION=2 vite build --outDir release/firefox",
|
"release:firefox": "MANIFEST_VERSION=2 vite build --outDir release/firefox",
|
||||||
"release:chrome": "MANIFEST_VERSION=3 vite build --outDir release/chrome"
|
"release:chrome": "MANIFEST_VERSION=3 vite build --outDir release/chrome"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/bytedream/stream-bypass.git"
|
"url": "git+https://github.com/ByteDream/stream-bypass.git"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/bytedream/stream-bypass/issues"
|
"url": "https://github.com/ByteDream/stream-bypass/issues"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
|
"@samrum/vite-plugin-web-extension": "^5.0.0",
|
||||||
"@samrum/vite-plugin-web-extension": "^5.1.1",
|
"@sveltejs/vite-plugin-svelte": "^2.1.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
"@tsconfig/svelte": "^4.0.1",
|
||||||
"@tsconfig/svelte": "^5.0.4",
|
"@types/chrome": "^0.0.228",
|
||||||
"@types/chrome": "^0.0.320",
|
"@types/webextension-polyfill": "^0.10.0",
|
||||||
"@types/firefox-webext-browser": "^120.0.4",
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||||
"eslint": "^9.26.0",
|
"@typescript-eslint/parser": "^6.10.0",
|
||||||
"eslint-config-prettier": "^10.1.3",
|
"eslint": "^8.53.0",
|
||||||
"eslint-plugin-svelte": "^3.5.1",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"hls.js": "^1.6.2",
|
"eslint-plugin-svelte": "^2.35.0",
|
||||||
"prettier": "^3.5.3",
|
"hls.js": "^1.4.12",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier": "^3.0.3",
|
||||||
"sass": "^1.87.0",
|
"prettier-plugin-svelte": "^3.1.0",
|
||||||
"svelte": "^5.28.2",
|
"sass": "^1.69.5",
|
||||||
"svelte-check": "^4.1.7",
|
"svelte": "^3.58.0",
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-check": "^3.2.0",
|
||||||
"tslib": "^2.8.1",
|
"svelte-preprocess": "^5.0.3",
|
||||||
"typescript": "^5.8.3",
|
"tslib": "^2.5.0",
|
||||||
"typescript-eslint": "^8.32.0",
|
"typescript": "^5.0.4",
|
||||||
"vite": "^6.3.5",
|
"vite": "~4.3.3",
|
||||||
"web-ext": "^8.6.0"
|
"web-ext": "^7.6.2",
|
||||||
|
"webextension-polyfill": "^0.10.0"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
24
src/entries/background/main.ts
Normal file
24
src/entries/background/main.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import type { Match } from '~/lib/match';
|
||||||
|
import { storageDelete, storageGet, storageSet } from '~/lib/settings';
|
||||||
|
import { getMatch } from '~/lib/match';
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener(async (message) => {
|
||||||
|
if (message.action == 'ff2mpv') {
|
||||||
|
await chrome.runtime.sendNativeMessage('ff2mpv', { url: message.url });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.webRequest.onBeforeRedirect.addListener(
|
||||||
|
async (details) => {
|
||||||
|
// check if redirects origins from a previous redirect
|
||||||
|
if ((await storageGet('redirect')) === undefined) {
|
||||||
|
let match: Match;
|
||||||
|
if ((match = await getMatch(new URL(details.url).hostname)) !== undefined) {
|
||||||
|
await storageSet('redirect', match.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await storageDelete('redirect');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ urls: ['<all_urls>'], types: ['main_frame', 'sub_frame'] }
|
||||||
|
);
|
@ -1,39 +0,0 @@
|
|||||||
import './shared';
|
|
||||||
|
|
||||||
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 = 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}/`
|
|
||||||
});
|
|
||||||
|
|
||||||
return { requestHeaders: details.requestHeaders };
|
|
||||||
},
|
|
||||||
{ urls: ['<all_urls>'], types: ['xmlhttprequest'] },
|
|
||||||
['blocking', 'requestHeaders']
|
|
||||||
);
|
|
||||||
|
|
||||||
chrome.webRequest.onBeforeRedirect.addListener(
|
|
||||||
async (details) => {
|
|
||||||
// check if redirects origins from a previous redirect
|
|
||||||
if ((await Redirect.get()) == null) {
|
|
||||||
let match: Match | null;
|
|
||||||
if ((match = await getMatch(new URL(details.url).hostname)) !== null) {
|
|
||||||
await Redirect.set(match);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await Redirect.delete();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ urls: ['<all_urls>'], types: ['main_frame', 'sub_frame'] }
|
|
||||||
);
|
|
@ -1 +0,0 @@
|
|||||||
import './shared';
|
|
@ -1,5 +0,0 @@
|
|||||||
chrome.runtime.onMessage.addListener(async (message) => {
|
|
||||||
if (message.action == 'ff2mpv') {
|
|
||||||
await chrome.runtime.sendNativeMessage('ff2mpv', { url: message.url });
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,8 +1,10 @@
|
|||||||
import { getMatch, MatchMediaType, type Match } from '~/lib/match';
|
import type { Match } from '~/lib/match';
|
||||||
|
import { getMatch } from '~/lib/match';
|
||||||
import { Other, Redirect } from '~/lib/settings';
|
import { Other, Redirect } from '~/lib/settings';
|
||||||
|
import browser from 'webextension-polyfill';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
let match: Match | null;
|
let match: Match;
|
||||||
let redirect = false;
|
let redirect = false;
|
||||||
if ((match = await getMatch(window.location.host)) === null) {
|
if ((match = await getMatch(window.location.host)) === null) {
|
||||||
if ((match = await Redirect.get()) === null) {
|
if ((match = await Redirect.get()) === null) {
|
||||||
@ -11,91 +13,36 @@ async function main() {
|
|||||||
redirect = true;
|
redirect = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// some sites have a javascript based redirect, e.g. example.com redirects to example.org by changing
|
const re = document.body.innerHTML.match(match.regex);
|
||||||
// window.location.href instead of a 3XX http redirect. an empty body is a sign that such a javascript redirect
|
|
||||||
// occurred
|
|
||||||
if (document.body == null) {
|
|
||||||
await Redirect.set(match);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let re = null;
|
|
||||||
for (const regex of match.regex) {
|
|
||||||
if ((re = document.body.innerHTML.match(regex)) !== null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (re === null) {
|
if (re === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
await Redirect.delete();
|
await Redirect.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
let url: string | null;
|
const url = await match.match(re);
|
||||||
let urlType: MatchMediaType | null;
|
|
||||||
try {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
if (!url || !urlType) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send the url to the ff2mpv (https://github.com/woodruffw/ff2mpv) application
|
// send the url to the ff2mpv (https://github.com/woodruffw/ff2mpv) application
|
||||||
if (await Other.getFf2mpv()) {
|
if (await Other.getFf2mpv()) {
|
||||||
await chrome.runtime.sendMessage({ action: 'ff2mpv', url: url });
|
await browser.runtime.sendMessage({ action: 'ff2mpv', url: url });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match.replace && urlType != MatchMediaType.Hls) {
|
if (match.replace && !url.includes('.m3u8')) {
|
||||||
// this destroys all intervals that may spawn popups or events
|
|
||||||
let intervalId = window.setInterval(() => {}, 0);
|
|
||||||
while (intervalId--) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
}
|
|
||||||
let timeoutId = window.setTimeout(() => {}, 0);
|
|
||||||
while (timeoutId--) {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear completed document
|
|
||||||
document.documentElement.innerHTML = '';
|
|
||||||
|
|
||||||
document.body.style.backgroundColor = '#131313';
|
|
||||||
|
|
||||||
// video player
|
|
||||||
const player = document.createElement('video');
|
const player = document.createElement('video');
|
||||||
player.style.width = '100%';
|
player.style.width = '100%';
|
||||||
player.style.height = '100%';
|
player.style.height = '100%';
|
||||||
player.controls = true;
|
player.controls = true;
|
||||||
player.src = url;
|
player.src = url;
|
||||||
|
|
||||||
// add video player to document body
|
document.body.innerHTML = '';
|
||||||
document.body.style.margin = '0';
|
|
||||||
document.body.append(player);
|
document.body.append(player);
|
||||||
} else {
|
} else {
|
||||||
window.location.assign(
|
window.location.assign(
|
||||||
chrome.runtime.getURL(
|
browser.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}`
|
}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { play } from '~/entries/player/player';
|
import { play } from '~/entries/player/player';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
let errorMessage: string | null = $state(null);
|
let errorMessage: string | null = null;
|
||||||
|
|
||||||
let videoElem: HTMLVideoElement;
|
let videoElem: HTMLVideoElement;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
@ -11,13 +10,13 @@
|
|||||||
await play(videoElem);
|
await play(videoElem);
|
||||||
videoElem.controls = true;
|
videoElem.controls = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorMessage = e as string;
|
errorMessage = e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y_media_has_caption -->
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
<video id="video" bind:this={videoElem}></video>
|
<video id="video" bind:this={videoElem} />
|
||||||
{#if errorMessage}
|
{#if errorMessage}
|
||||||
<div id="message-container">
|
<div id="message-container">
|
||||||
<p>
|
<p>
|
||||||
|
@ -6,10 +6,9 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { mount } from 'svelte';
|
|
||||||
import Player from '~/entries/player/Player.svelte';
|
import Player from '~/entries/player/Player.svelte';
|
||||||
|
|
||||||
mount(Player, {
|
new Player({
|
||||||
target: document.body
|
target: document.body
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
|
import { matches } from '~/lib/match';
|
||||||
import Hls from 'hls.js';
|
import Hls from 'hls.js';
|
||||||
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;
|
videoElem.src = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function playHls(url: string, domain: string, videoElem: HTMLVideoElement) {
|
async function playHls(url: string, videoElem: HTMLVideoElement) {
|
||||||
if (videoElem.canPlayType('application/vnd.apple.mpegurl')) {
|
if (videoElem.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
videoElem.src = url;
|
videoElem.src = url;
|
||||||
} else if (Hls.isSupported()) {
|
} else if (Hls.isSupported()) {
|
||||||
const hls = new Hls({
|
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.loadSource(url);
|
||||||
hls.attachMedia(videoElem);
|
hls.attachMedia(videoElem);
|
||||||
@ -28,10 +21,9 @@ async function playHls(url: string, domain: string, videoElem: HTMLVideoElement)
|
|||||||
|
|
||||||
export async function play(videoElem: HTMLVideoElement) {
|
export async function play(videoElem: HTMLVideoElement) {
|
||||||
const urlQuery = new URLSearchParams(window.location.search);
|
const urlQuery = new URLSearchParams(window.location.search);
|
||||||
const id = urlQuery.get('id') as string;
|
const id = urlQuery.get('id');
|
||||||
const url = decodeURIComponent(urlQuery.get('url') as string);
|
const url = decodeURIComponent(urlQuery.get('url'));
|
||||||
const domain = urlQuery.get('domain') as string;
|
const domain = urlQuery.get('domain');
|
||||||
const type = urlQuery.get('type') as MatchMediaType;
|
|
||||||
|
|
||||||
const match = matches[id];
|
const match = matches[id];
|
||||||
if (match === undefined) {
|
if (match === undefined) {
|
||||||
@ -39,9 +31,9 @@ export async function play(videoElem: HTMLVideoElement) {
|
|||||||
}
|
}
|
||||||
document.title = `Stream Bypass (${domain})`;
|
document.title = `Stream Bypass (${domain})`;
|
||||||
|
|
||||||
if (type === MatchMediaType.Hls) {
|
if (new URL(url).pathname.endsWith('.m3u8')) {
|
||||||
await playHls(url, domain, videoElem);
|
await playHls(url, videoElem);
|
||||||
} else if (type === MatchMediaType.Native) {
|
} else {
|
||||||
await playNative(url, domain, videoElem);
|
await playNative(url, videoElem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,97 +1,123 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { matches, type Match } from '~/lib/match';
|
import { matches, Reliability } from '~/lib/match';
|
||||||
import { Hosters, Other } from '~/lib/settings';
|
import { Hosters, Other } from '~/lib/settings';
|
||||||
import Toggle from './toggle.svelte';
|
|
||||||
|
|
||||||
let hostersEnabled: boolean;
|
let hostersEnabled: boolean;
|
||||||
let hosters: (Match & { active: boolean; disabled: boolean })[] = [];
|
let hosters = [];
|
||||||
(async () => {
|
(async () => {
|
||||||
hostersEnabled = !(await Hosters.getAllDisabled());
|
hostersEnabled = !(await Hosters.getAllDisabled());
|
||||||
|
|
||||||
const disabled = await Hosters.getDisabled();
|
const disabled = await Hosters.getDisabled();
|
||||||
hosters = Object.values(matches).map((m: any) => {
|
hosters = Object.values(matches).map((m) => {
|
||||||
m['active'] = disabled.findIndex((p) => p.id == m.id) == -1;
|
m['active'] = disabled.findIndex((p) => p.id == m.id) == -1;
|
||||||
|
m['disabled'] = !hostersEnabled;
|
||||||
return m;
|
return m;
|
||||||
}) as typeof hosters;
|
});
|
||||||
})();
|
|
||||||
|
|
||||||
let isMobile: boolean;
|
|
||||||
(async () => {
|
|
||||||
isMobile = (await browser.runtime.getPlatformInfo()).os === 'android';
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
let ff2mpvEnabled: boolean;
|
let ff2mpvEnabled: boolean;
|
||||||
(async () => {
|
(async () => {
|
||||||
ff2mpvEnabled = (await Other.getFf2mpv()) as boolean;
|
ff2mpvEnabled = await Other.getFf2mpv();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main
|
<main>
|
||||||
style={isMobile
|
<div>
|
||||||
? 'height: 100vh; display: flex; flex-direction: column; align-items: center'
|
<h3 class="header">Hosters</h3>
|
||||||
: 'height: 500px'}
|
<div class="buttons super-buttons">
|
||||||
>
|
<button
|
||||||
<fieldset>
|
class:active={hostersEnabled}
|
||||||
<legend>Hoster</legend>
|
on:click={async () => {
|
||||||
<div class="setting-container" style={isMobile ? 'grid-column-gap: 5rem' : ''}>
|
await Hosters.enableAll();
|
||||||
<label for="hosters-enabled">Enabled</label>
|
hostersEnabled = true;
|
||||||
<div>
|
hosters = hosters.map((m) => {
|
||||||
<Toggle
|
m['disabled'] = false;
|
||||||
bind:checked={hostersEnabled}
|
return m;
|
||||||
id="hosters-enabled"
|
});
|
||||||
onChange={() => Hosters.setAll(hostersEnabled)}
|
}}>On</button
|
||||||
/>
|
>
|
||||||
</div>
|
<button
|
||||||
<hr />
|
class:active={!hostersEnabled}
|
||||||
{#each hosters as hoster, i (hoster.id)}
|
on:click={async () => {
|
||||||
<label for="hoster-{i}" style="cursor: {hostersEnabled ? 'pointer' : 'default'}"
|
await Hosters.disableAll();
|
||||||
>{hoster.name}</label
|
hostersEnabled = false;
|
||||||
>
|
hosters = hosters.map((m) => {
|
||||||
<div>
|
m['disabled'] = true;
|
||||||
<Toggle
|
return m;
|
||||||
bind:checked={hoster.active}
|
});
|
||||||
disabled={!hostersEnabled}
|
}}>Off</button
|
||||||
id="hoster-{i}"
|
>
|
||||||
onChange={async () => {
|
|
||||||
if (hoster.active) {
|
|
||||||
await Hosters.enable(hoster);
|
|
||||||
} else {
|
|
||||||
await Hosters.disable(hoster);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
></Toggle>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
<table class="setting-table">
|
||||||
{#if !isMobile}
|
{#each hosters as hoster}
|
||||||
<fieldset>
|
<tr>
|
||||||
<legend>Other</legend>
|
<td class="setting-name">
|
||||||
<div class="setting-container">
|
<p
|
||||||
<label for="ff2mpv">ff2mpv</label>
|
class:reliability-low={hoster.reliability === Reliability.LOW}
|
||||||
<div>
|
class:reliability-normal={hoster.reliability === Reliability.NORMAL}
|
||||||
<Toggle
|
class:reliability-high={hoster.reliability === Reliability.HIGH}
|
||||||
bind:checked={ff2mpvEnabled}
|
>
|
||||||
id="ff2mpv"
|
{hoster.name}
|
||||||
onChange={async () => {
|
</p>
|
||||||
ff2mpvEnabled = !ff2mpvEnabled;
|
</td>
|
||||||
if (await browser.permissions.request({ permissions: ['nativeMessaging'] })) {
|
<td class="buttons">
|
||||||
await Other.setFf2mpv(ff2mpvEnabled);
|
<button
|
||||||
ff2mpvEnabled = !ff2mpvEnabled;
|
class:disabled={hoster.disabled}
|
||||||
}
|
class:active={hoster.active}
|
||||||
}}
|
on:click={async () => {
|
||||||
></Toggle>
|
if (hoster.disabled) return;
|
||||||
<a
|
await Hosters.enable(hoster);
|
||||||
class="info-questionmark"
|
hoster.active = true;
|
||||||
href="https://github.com/ByteDream/stream-bypass/tree/master#ff2mpv-use-mpv-to-directly-play-streams"
|
}}>On</button
|
||||||
>?</a
|
>
|
||||||
|
<button
|
||||||
|
class:disabled={hoster.disabled}
|
||||||
|
class:active={!hoster.active}
|
||||||
|
on:click={async () => {
|
||||||
|
if (hoster.disabled) return;
|
||||||
|
await Hosters.disable(hoster);
|
||||||
|
hoster.active = false;
|
||||||
|
}}>Off</button
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div>
|
||||||
|
<h3 class="header">Other</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td class="setting-name">
|
||||||
|
<p>ff2mpv</p>
|
||||||
|
</td>
|
||||||
|
<td class="buttons">
|
||||||
|
<button
|
||||||
|
class:active={ff2mpvEnabled}
|
||||||
|
on:click={async () => {
|
||||||
|
await Other.setFf2mpv(true);
|
||||||
|
ff2mpvEnabled = true;
|
||||||
|
}}>On</button
|
||||||
>
|
>
|
||||||
</div>
|
<button
|
||||||
</div>
|
class:active={!ff2mpvEnabled}
|
||||||
</fieldset>
|
on:click={async () => {
|
||||||
{/if}
|
await Other.setFf2mpv(false);
|
||||||
<a id="report-notice" href="https://github.com/ByteDream/stream-bypass/issues"
|
ff2mpvEnabled = false;
|
||||||
>Report issues or requests</a
|
}}>Off</button
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="https://github.com/ByteDream/stream-bypass/tree/master#ff2mpv-use-mpv-to-directly-play-streams"
|
||||||
|
>🛈</a
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<a id="bug-notice" href="https://github.com/ByteDream/stream-bypass/issues"
|
||||||
|
>Something does not work</a
|
||||||
>
|
>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@ -101,16 +127,11 @@
|
|||||||
background-color: #2b2a33;
|
background-color: #2b2a33;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin: 0;
|
overflow-x: hidden;
|
||||||
padding: 0 8px;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset {
|
#bug-notice {
|
||||||
border-radius: 5px;
|
|
||||||
border-color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
#report-notice {
|
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
display: block;
|
display: block;
|
||||||
@ -133,33 +154,65 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-container {
|
.setting-table {
|
||||||
display: grid;
|
border-collapse: collapse;
|
||||||
grid-template-columns: auto auto;
|
border-spacing: 0;
|
||||||
grid-column-gap: 5px;
|
}
|
||||||
grid-row-gap: 4px;
|
|
||||||
align-items: end;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
& > label {
|
.setting-name {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
|
|
||||||
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
user-select: none;
|
cursor: default;
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > hr {
|
|
||||||
grid-column: 1 / span 2;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-questionmark {
|
.buttons {
|
||||||
display: inline-block;
|
display: flex;
|
||||||
transform: translateX(-40%) translateY(-100%);
|
flex-direction: row;
|
||||||
color: black;
|
height: 34px;
|
||||||
text-decoration: none;
|
|
||||||
|
button,
|
||||||
|
a {
|
||||||
|
border: 1px solid #281515;
|
||||||
|
background-color: transparent;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px 8px;
|
||||||
|
margin: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: rgba(255, 65, 65, 0.74);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background-color: gray;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.super-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reliability-low {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: red;
|
||||||
|
}
|
||||||
|
.reliability-normal {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: yellow;
|
||||||
|
}
|
||||||
|
.reliability-high {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: #00ff00;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html style="overflow-y: hidden" lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<title>Stream Bypass</title>
|
<title>Stream Bypass</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="overflow-y: scroll">
|
<body style="width: fit-content; height: 500px">
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { mount } from 'svelte';
|
|
||||||
import Popup from '~/entries/popup/Popup.svelte';
|
import Popup from '~/entries/popup/Popup.svelte';
|
||||||
|
|
||||||
mount(Popup, {
|
new Popup({
|
||||||
target: document.body
|
target: document.body
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
5
src/entries/popup/main.ts
Normal file
5
src/entries/popup/main.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import App from './Popup.svelte';
|
||||||
|
|
||||||
|
new App({
|
||||||
|
target: document.getElementById('app')
|
||||||
|
});
|
@ -1,68 +0,0 @@
|
|||||||
<!-- https://flowbite.com/docs/forms/toggle/ -->
|
|
||||||
<script lang="ts">
|
|
||||||
import type { Snippet } from 'svelte';
|
|
||||||
|
|
||||||
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">
|
|
||||||
{@render children?.()}
|
|
||||||
<input type="checkbox" {id} bind:checked {disabled} onchange={onChange} />
|
|
||||||
<span></span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<style lang="scss" global>
|
|
||||||
.toggle {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
input {
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
&:checked + span {
|
|
||||||
background: limegreen;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
transform: translateX(100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled + span {
|
|
||||||
background: gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
position: relative;
|
|
||||||
width: 2.75rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
background: #cf0000;
|
|
||||||
border-radius: 9999px;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 2px;
|
|
||||||
inset-inline-start: 2px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 9999px;
|
|
||||||
height: 1.25rem;
|
|
||||||
width: 1.25rem;
|
|
||||||
transition: all 0.15s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:has(input:disabled) {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
294
src/lib/match.ts
294
src/lib/match.ts
@ -1,33 +1,28 @@
|
|||||||
import { Hosters, Redirect, TmpHost } from './settings';
|
import { unpack } from '~/lib/utils';
|
||||||
import { lastPathSegment } from './util/extract';
|
import { Hosters } from '~/lib/settings';
|
||||||
import { unpack } from './util/userspace';
|
|
||||||
|
export enum Reliability {
|
||||||
|
HIGH,
|
||||||
|
NORMAL,
|
||||||
|
LOW
|
||||||
|
}
|
||||||
|
|
||||||
export interface Match {
|
export interface Match {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
reliability: Reliability;
|
||||||
domains: string[];
|
domains: string[];
|
||||||
replace?: boolean;
|
replace?: boolean;
|
||||||
regex: RegExp[];
|
regex: RegExp;
|
||||||
notice?: string;
|
notice?: string;
|
||||||
|
|
||||||
match(
|
match(match: RegExpMatchArray): Promise<string>;
|
||||||
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 = {
|
export const Doodstream: Match = {
|
||||||
name: 'Doodstream',
|
name: 'Doodstream',
|
||||||
id: 'doodstream',
|
id: 'doodstream',
|
||||||
|
reliability: Reliability.NORMAL,
|
||||||
domains: [
|
domains: [
|
||||||
'doodstream.com',
|
'doodstream.com',
|
||||||
'dood.pm',
|
'dood.pm',
|
||||||
@ -36,22 +31,18 @@ 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',
|
||||||
'dood.li',
|
|
||||||
'dood.re',
|
'dood.re',
|
||||||
'dood.yt',
|
'dood.yt',
|
||||||
'doods.pro',
|
|
||||||
'ds2play.com',
|
'ds2play.com',
|
||||||
'dooood.com',
|
'dooood.com'
|
||||||
'd000d.com'
|
|
||||||
],
|
],
|
||||||
replace: true,
|
replace: true,
|
||||||
regex: [/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s],
|
regex: /(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Range: 'bytes=0-'
|
Range: 'bytes=0-'
|
||||||
@ -67,42 +58,37 @@ export const Doodstream: Match = {
|
|||||||
export const DropLoad: Match = {
|
export const DropLoad: Match = {
|
||||||
name: 'Dropload',
|
name: 'Dropload',
|
||||||
id: 'dropload',
|
id: 'dropload',
|
||||||
domains: ['dropload.io'],
|
reliability: Reliability.HIGH,
|
||||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
domains: ['dropload.ui'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
const unpacked = await unpack(match[0]);
|
const unpacked = await unpack(match[0]);
|
||||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Filemoon: Match = {
|
export const Filemoon: Match = {
|
||||||
name: 'Filemoon',
|
name: 'Filemoon',
|
||||||
id: 'filemoon',
|
id: 'filemoon',
|
||||||
domains: ['filemoon.sx', 'filemoon.to', 'filemoon.in'],
|
reliability: Reliability.HIGH,
|
||||||
regex: [/(?<=<iframe\s*src=")\S*(?=")/s, /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
domains: ['filemoon.sx', 'filemoon.in'],
|
||||||
replace: true,
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
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]);
|
const unpacked = await unpack(match[0]);
|
||||||
return unpacked.match(/(?<=file:")\S*(?=")/)![0];
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GoodStream: Match = {
|
export const GoodStream: Match = {
|
||||||
name: 'Goodstream',
|
name: 'Goodstream',
|
||||||
id: 'goodstream',
|
id: 'goodstream',
|
||||||
|
reliability: Reliability.NORMAL,
|
||||||
domains: ['goodstream.uno'],
|
domains: ['goodstream.uno'],
|
||||||
regex: [/(?<=file:\s+").*(?=")/g],
|
regex: /(?<=file:\s+").*(?=")/g,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
return match[0];
|
return match[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -110,83 +96,26 @@ export const GoodStream: Match = {
|
|||||||
export const Kwik: Match = {
|
export const Kwik: Match = {
|
||||||
name: 'Kwik',
|
name: 'Kwik',
|
||||||
id: 'kwik',
|
id: 'kwik',
|
||||||
|
reliability: Reliability.HIGH,
|
||||||
domains: ['kwik.cx'],
|
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 function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
const unpacked = await unpack(match[0]);
|
const unpacked = await unpack(match[0]);
|
||||||
return unpacked.match(/(?<=source=').*(?=')/)![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 = {
|
export const Mixdrop: Match = {
|
||||||
name: 'Mixdrop',
|
name: 'Mixdrop',
|
||||||
id: 'mixdrop',
|
id: 'mixdrop',
|
||||||
domains: ['mixdrop.bz', 'mixdrop.ch', 'mixdrop.co', 'mixdrop.gl', 'mixdrop.my', 'mixdrop.to'],
|
reliability: Reliability.HIGH,
|
||||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
domains: ['mixdrop.co', 'mixdrop.to', 'mixdrop.ch', 'mixdrop.bz', 'mixdrop.gl'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
const unpacked = await unpack(match[0]);
|
const unpacked = await unpack(match[0]);
|
||||||
const url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)![0];
|
const url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)[0];
|
||||||
return `https:${url}`;
|
return `https:${url}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -194,21 +123,23 @@ export const Mixdrop: Match = {
|
|||||||
export const Mp4Upload: Match = {
|
export const Mp4Upload: Match = {
|
||||||
name: 'Mp4Upload',
|
name: 'Mp4Upload',
|
||||||
id: 'mp4upload',
|
id: 'mp4upload',
|
||||||
|
reliability: Reliability.HIGH,
|
||||||
domains: ['mp4upload.com'],
|
domains: ['mp4upload.com'],
|
||||||
replace: true,
|
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 function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
const unpacked = await unpack(match[0]);
|
const unpacked = await unpack(match[0]);
|
||||||
return unpacked.match(/(?<=player.src\(").*(?=")/)![0];
|
return unpacked.match(/(?<=player.src\(").*(?=")/)[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Newgrounds: Match = {
|
export const Newgrounds: Match = {
|
||||||
name: 'Newgrounds',
|
name: 'Newgrounds',
|
||||||
id: 'newgrounds',
|
id: 'newgrounds',
|
||||||
|
reliability: Reliability.HIGH,
|
||||||
domains: ['newgrounds.com'],
|
domains: ['newgrounds.com'],
|
||||||
regex: [/.*/gm],
|
regex: /.*/gm,
|
||||||
|
|
||||||
match: async () => {
|
match: async () => {
|
||||||
const id = window.location.pathname.split('/').slice(-1)[0];
|
const id = window.location.pathname.split('/').slice(-1)[0];
|
||||||
@ -222,28 +153,14 @@ export const Newgrounds: Match = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StreamA2z: Match = {
|
|
||||||
name: 'Stream2Az',
|
|
||||||
id: 'stream2az',
|
|
||||||
domains: ['streama2z.com', 'streama2z.xyz'],
|
|
||||||
regex: [/https?:\/\/\S*m3u8.+(?=['"])/gm],
|
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
|
||||||
if (StreamA2z.domains.indexOf(window.location.hostname) !== -1) {
|
|
||||||
await Redirect.set(StreamA2z);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return match[0];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Streamtape: Match = {
|
export const Streamtape: Match = {
|
||||||
name: 'Streamtape',
|
name: 'Streamtape',
|
||||||
id: 'streamtape',
|
id: 'streamtape',
|
||||||
|
reliability: Reliability.HIGH,
|
||||||
domains: ['streamtape.com', 'streamtape.net', 'shavetape.cash'],
|
domains: ['streamtape.com', 'streamtape.net', 'shavetape.cash'],
|
||||||
regex: [/id=.*(?=')/gm],
|
regex: /id=.*(?=')/gm,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < match.length) {
|
while (i < match.length) {
|
||||||
if (match[++i - 1] == match[i]) {
|
if (match[++i - 1] == match[i]) {
|
||||||
@ -259,10 +176,11 @@ export const Streamtape: Match = {
|
|||||||
export const Streamzz: Match = {
|
export const Streamzz: Match = {
|
||||||
name: 'Streamzz',
|
name: 'Streamzz',
|
||||||
id: 'streamzz',
|
id: 'streamzz',
|
||||||
|
reliability: Reliability.LOW,
|
||||||
domains: ['streamzz.to', 'streamz.ws'],
|
domains: ['streamzz.to', 'streamz.ws'],
|
||||||
regex: [/(?<=\|)\w{2,}/gm],
|
regex: /(?<=\|)\w{2,}/gm,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
return `https://get.${location.hostname.split('.')[0]}.tw/getlink-${
|
return `https://get.${location.hostname.split('.')[0]}.tw/getlink-${
|
||||||
match.sort((a, b) => b.length - a.length)[0]
|
match.sort((a, b) => b.length - a.length)[0]
|
||||||
}.dll`;
|
}.dll`;
|
||||||
@ -272,47 +190,37 @@ export const Streamzz: Match = {
|
|||||||
export const SuperVideo: Match = {
|
export const SuperVideo: Match = {
|
||||||
name: 'Supervideo',
|
name: 'Supervideo',
|
||||||
id: 'supervideo',
|
id: 'supervideo',
|
||||||
domains: ['supervideo.cc', 'supervideo.tv'],
|
reliability: Reliability.HIGH,
|
||||||
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
|
domains: ['supervideo.tv'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
const unpacked = await unpack(match[0]);
|
const unpacked = await unpack(match[0]);
|
||||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Upstream: Match = {
|
export const Upstream: Match = {
|
||||||
name: 'Upstream',
|
name: 'Upstream',
|
||||||
id: 'upstream',
|
id: 'upstream',
|
||||||
|
reliability: Reliability.HIGH,
|
||||||
domains: ['upstream.to'],
|
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 function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
const unpacked = await unpack(match[0]);
|
const unpacked = await unpack(match[0]);
|
||||||
return unpacked.match(/(?<=file:").*(?=")/)![0];
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Vidmoly: Match = {
|
|
||||||
name: 'Vidmoly',
|
|
||||||
id: 'vidmoly',
|
|
||||||
domains: ['vidmoly.me', 'vidmoly.to'],
|
|
||||||
regex: [/(?<=file:").+\.m3u8/gm],
|
|
||||||
replace: true,
|
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
|
||||||
return match[0];
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Vidoza: Match = {
|
export const Vidoza: Match = {
|
||||||
name: 'Vidoza',
|
name: 'Vidoza',
|
||||||
id: 'vidoza',
|
id: 'vidoza',
|
||||||
domains: ['vidoza.net', 'videzz.net'],
|
reliability: Reliability.HIGH,
|
||||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
domains: ['vidoza.net'],
|
||||||
replace: true,
|
regex: /(?<=src:\s?").+?(?=")/gm,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
return match[0];
|
return match[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -320,78 +228,23 @@ export const Vidoza: Match = {
|
|||||||
export const Voe: Match = {
|
export const Voe: Match = {
|
||||||
name: 'Voe',
|
name: 'Voe',
|
||||||
id: 'voe',
|
id: 'voe',
|
||||||
|
reliability: Reliability.HIGH,
|
||||||
domains: ['voe.sx'],
|
domains: ['voe.sx'],
|
||||||
regex: [
|
regex: /https?:\/\/\S*m3u8.+(?=')/gm,
|
||||||
// voe.sx
|
|
||||||
/(?<=window\.location\.href\s=\s')\S*(?=')/gm,
|
|
||||||
// whatever site voe.sx redirects to
|
|
||||||
/(?<=<script type="application\/json">).*(?=<\/script>)/m
|
|
||||||
],
|
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
if (window.location.host === 'voe.sx') {
|
return match[0];
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Vupload: Match = {
|
export const Vupload: Match = {
|
||||||
name: 'Vupload',
|
name: 'Vupload',
|
||||||
id: 'vupload',
|
id: 'vupload',
|
||||||
|
reliability: Reliability.HIGH,
|
||||||
domains: ['vupload.com'],
|
domains: ['vupload.com'],
|
||||||
regex: [/(?<=src:\s?").+?(?=")/gm],
|
regex: /(?<=src:\s?").+?(?=")/gm,
|
||||||
|
|
||||||
match: async function (match: RegExpMatchArray) {
|
match: async (match: RegExpMatchArray) => {
|
||||||
return match[0];
|
return match[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -402,17 +255,13 @@ 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,
|
||||||
[StreamA2z.id]: StreamA2z,
|
|
||||||
[Streamtape.id]: Streamtape,
|
[Streamtape.id]: Streamtape,
|
||||||
[Streamzz.id]: Streamzz,
|
[Streamzz.id]: Streamzz,
|
||||||
[SuperVideo.id]: SuperVideo,
|
[SuperVideo.id]: SuperVideo,
|
||||||
[Upstream.id]: Upstream,
|
[Upstream.id]: Upstream,
|
||||||
[Vidmoly.id]: Vidmoly,
|
|
||||||
[Vidoza.id]: Vidoza,
|
[Vidoza.id]: Vidoza,
|
||||||
[Voe.id]: Voe,
|
[Voe.id]: Voe,
|
||||||
[Vupload.id]: Vupload
|
[Vupload.id]: Vupload
|
||||||
@ -432,10 +281,5 @@ export async function getMatch(domain: string): Promise<Match | null> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tmpHost = await TmpHost.get();
|
|
||||||
if (tmpHost && tmpHost[0] === domain) {
|
|
||||||
return tmpHost[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { matches, type Match } from './match';
|
import browser from 'webextension-polyfill';
|
||||||
|
import type { Match } from '~/lib/match';
|
||||||
|
import { matches } from '~/lib/match';
|
||||||
|
|
||||||
export const Hosters = {
|
export const Hosters = {
|
||||||
getDisabled: async () => {
|
getDisabled: async () => {
|
||||||
const disabled = (await storageGet('hosters.disabled', [])) as string[];
|
const disabled = await storageGet<string[]>('hosters.disabled', []);
|
||||||
return disabled.map((id) => matches[id]).filter((m) => m !== undefined);
|
return disabled.map((id) => matches[id]).filter((m) => m !== undefined);
|
||||||
},
|
},
|
||||||
disable: async (match: Match) => {
|
disable: async (match: Match) => {
|
||||||
const disabled = (await storageGet('hosters.disabled', [])) as string[];
|
const disabled = await storageGet('hosters.disabled', []);
|
||||||
const index = disabled.indexOf(match.id);
|
const index = disabled.indexOf(match.id);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
disabled.push(match.id);
|
disabled.push(match.id);
|
||||||
@ -14,7 +16,7 @@ export const Hosters = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
enable: async (match: Match) => {
|
enable: async (match: Match) => {
|
||||||
const disabled = (await storageGet('hosters.disabled', [])) as string[];
|
const disabled = await storageGet('hosters.disabled', []);
|
||||||
const index = disabled.indexOf(match.id);
|
const index = disabled.indexOf(match.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
disabled.splice(index, 1);
|
disabled.splice(index, 1);
|
||||||
@ -24,14 +26,17 @@ export const Hosters = {
|
|||||||
getAllDisabled: async () => {
|
getAllDisabled: async () => {
|
||||||
return await storageGet<boolean>('hosters.allDisabled', false);
|
return await storageGet<boolean>('hosters.allDisabled', false);
|
||||||
},
|
},
|
||||||
setAll: async (enable: boolean) => {
|
disableAll: async () => {
|
||||||
await storageSet('hosters.allDisabled', !enable);
|
await storageSet('hosters.allDisabled', true);
|
||||||
|
},
|
||||||
|
enableAll: async () => {
|
||||||
|
await storageSet('hosters.allDisabled', false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Redirect = {
|
export const Redirect = {
|
||||||
get: async (): Promise<Match | null> => {
|
get: async (): Promise<Match | null> => {
|
||||||
return matches[(await storageGet('redirect')) as string] || null;
|
return matches[await storageGet<string>('redirect')] || null;
|
||||||
},
|
},
|
||||||
set: async (match: Match) => {
|
set: async (match: Match) => {
|
||||||
await storageSet('redirect', match.id);
|
await storageSet('redirect', match.id);
|
||||||
@ -41,72 +46,28 @@ 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 = {
|
export const Other = {
|
||||||
getFf2mpv: async () => {
|
getFf2mpv: async () => {
|
||||||
return await storageGet('other.ff2mpv', false);
|
return await storageGet('other.ff2mpv', true);
|
||||||
},
|
},
|
||||||
setFf2mpv: async (enable: boolean) => {
|
setFf2mpv: async (enable: boolean) => {
|
||||||
await storageSet('other.ff2mpv', enable);
|
await storageSet('other.ff2mpv', enable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function storageGet<T>(key: string, defaultValue?: T): Promise<T | undefined> {
|
export async function storageGet<T>(key: string, defaultValue?: T): Promise<T | undefined> {
|
||||||
let resolve: (value: T | undefined) => void;
|
const entry = await browser.storage.local.get(key);
|
||||||
const promise = new Promise<T | undefined>((r) => (resolve = r));
|
const value = entry[key];
|
||||||
|
return value === undefined ? defaultValue : value;
|
||||||
chrome.storage.local.get(key, (entry) => {
|
|
||||||
const value = entry[key];
|
|
||||||
resolve(value === undefined ? defaultValue : value);
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function storageSet<T>(key: string, value: T) {
|
export async function storageSet<T>(key: string, value: T) {
|
||||||
let resolve: () => void;
|
|
||||||
const promise = new Promise<void>((r) => (resolve = r));
|
|
||||||
|
|
||||||
const obj = {
|
const obj = {
|
||||||
[key]: value
|
[key]: value
|
||||||
};
|
};
|
||||||
chrome.storage.local.set(obj, () => resolve());
|
await browser.storage.local.set(obj);
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function storageDelete(key: string) {
|
export async function storageDelete(key: string) {
|
||||||
let resolve: () => void;
|
await browser.storage.local.remove(key);
|
||||||
const promise = new Promise<void>((r) => (resolve = r));
|
|
||||||
|
|
||||||
chrome.storage.local.remove(key, () => resolve());
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
export function lastPathSegment(path: string): string {
|
|
||||||
while (path.endsWith('/')) {
|
|
||||||
path = path.slice(0, -1);
|
|
||||||
}
|
|
||||||
return path.substring(path.lastIndexOf('/') + 1);
|
|
||||||
}
|
|
@ -16,10 +16,10 @@ export async function unpack(packed: string): Promise<string> {
|
|||||||
${packed}
|
${packed}
|
||||||
};
|
};
|
||||||
return packed;
|
return packed;
|
||||||
}
|
}'
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const res = (await runInPageContext(toExecute)) as string;
|
const res: string = await runInPageContext(toExecute);
|
||||||
return res
|
return res
|
||||||
.replace(/;/g, ';\n')
|
.replace(/;/g, ';\n')
|
||||||
.replace(/{/g, '\n{\n')
|
.replace(/{/g, '\n{\n')
|
||||||
@ -29,12 +29,11 @@ export async function unpack(packed: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adapted from: https://github.com/arikw/extension-page-context
|
// Adapted from: https://github.com/arikw/extension-page-context
|
||||||
async function runInPageContext<T>(toExecute: string): Promise<T | null> {
|
async function runInPageContext<T>(toExecute: string): Promise<T> {
|
||||||
// test that we are running with the allow-scripts permission
|
// test that we are running with the allow-scripts permission
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
||||||
window.sessionStorage;
|
window.sessionStorage;
|
||||||
} catch {
|
} catch (ignore) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,25 +44,28 @@ async function runInPageContext<T>(toExecute: string): Promise<T | null> {
|
|||||||
const scriptElm = document.createElement('script');
|
const scriptElm = document.createElement('script');
|
||||||
scriptElm.setAttribute('type', 'application/javascript');
|
scriptElm.setAttribute('type', 'application/javascript');
|
||||||
|
|
||||||
// inject the script
|
const code = `
|
||||||
scriptElm.textContent = `
|
|
||||||
(
|
(
|
||||||
async function () {
|
async function () {
|
||||||
const response = {
|
|
||||||
id: '${resultMessageId}'
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
const response = {
|
||||||
response.result = JSON.stringify(await (${toExecute})() ); // run script
|
id: ${resultMessageId}
|
||||||
} catch(err) {
|
};
|
||||||
response.error = JSON.stringify(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.postMessage(response, '*');
|
try {
|
||||||
|
response.result = JSON.stringify(await (${toExecute})() ); // run script
|
||||||
|
} catch(err) {
|
||||||
|
response.error = JSON.stringify(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.postMessage(response, '*');
|
||||||
}
|
}
|
||||||
)();
|
)();
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// inject the script
|
||||||
|
scriptElm.textContent = code;
|
||||||
|
|
||||||
// run the script
|
// run the script
|
||||||
document.documentElement.appendChild(scriptElm);
|
document.documentElement.appendChild(scriptElm);
|
||||||
|
|
@ -1,17 +1,15 @@
|
|||||||
import pkg from '../package.json';
|
import pkg from '../package.json';
|
||||||
import { matches } from './lib/match';
|
|
||||||
|
|
||||||
const sharedManifest: Partial<chrome.runtime.ManifestBase> = {
|
const sharedManifest: Partial<chrome.runtime.ManifestBase> = {
|
||||||
browser_specific_settings: {
|
browser_specific_settings: {
|
||||||
gecko: {
|
gecko: {
|
||||||
id: '{55dd42e8-3dd9-455a-b4fe-86664881b10c}'
|
id: '{55dd42e8-3dd9-455a-b4fe-86664881b10c}'
|
||||||
},
|
}
|
||||||
gecko_android: {}
|
|
||||||
},
|
},
|
||||||
content_scripts: [
|
content_scripts: [
|
||||||
{
|
{
|
||||||
all_frames: true,
|
all_frames: true,
|
||||||
matches: Object.values(matches).flatMap((m) => m.domains.map((d) => `*://${d}/*`)),
|
matches: ['<all_urls>'],
|
||||||
js: ['src/entries/contentScript/main.ts'],
|
js: ['src/entries/contentScript/main.ts'],
|
||||||
run_at: 'document_end'
|
run_at: 'document_end'
|
||||||
}
|
}
|
||||||
@ -23,8 +21,7 @@ const sharedManifest: Partial<chrome.runtime.ManifestBase> = {
|
|||||||
96: 'icons/stream-bypass@96px.png',
|
96: 'icons/stream-bypass@96px.png',
|
||||||
128: 'icons/stream-bypass@128px.png'
|
128: 'icons/stream-bypass@128px.png'
|
||||||
},
|
},
|
||||||
permissions: ['storage'],
|
permissions: ['storage', 'webRequest', 'nativeMessaging', '<all_urls>']
|
||||||
optional_permissions: ['nativeMessaging']
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const browserAction = {
|
const browserAction = {
|
||||||
@ -38,19 +35,18 @@ const browserAction = {
|
|||||||
const ManifestV2 = {
|
const ManifestV2 = {
|
||||||
...sharedManifest,
|
...sharedManifest,
|
||||||
background: {
|
background: {
|
||||||
scripts: ['src/entries/background/mv2.ts'],
|
scripts: ['src/entries/background/main.ts'],
|
||||||
persistent: true
|
persistent: true
|
||||||
},
|
},
|
||||||
content_scripts: [{ ...sharedManifest.content_scripts![0], matches: ['<all_urls>'] }],
|
|
||||||
browser_action: browserAction,
|
browser_action: browserAction,
|
||||||
permissions: [...sharedManifest.permissions, 'webRequest', 'webRequestBlocking', '<all_urls>']
|
permissions: [...sharedManifest.permissions]
|
||||||
};
|
};
|
||||||
|
|
||||||
const ManifestV3 = {
|
const ManifestV3 = {
|
||||||
...sharedManifest,
|
...sharedManifest,
|
||||||
action: browserAction,
|
action: browserAction,
|
||||||
background: {
|
background: {
|
||||||
service_worker: 'src/entries/background/mv3.ts'
|
service_worker: 'src/entries/background/main.ts'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,18 +65,15 @@ export function getManifest(
|
|||||||
...manifest,
|
...manifest,
|
||||||
...ManifestV2,
|
...ManifestV2,
|
||||||
manifest_version: manifestVersion
|
manifest_version: manifestVersion
|
||||||
} as chrome.runtime.ManifestV2;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manifestVersion === 3) {
|
if (manifestVersion === 3) {
|
||||||
return {
|
return {
|
||||||
...manifest,
|
...manifest,
|
||||||
// just like all the adblockers which are unable to fully work under MV3, we need access to every website
|
|
||||||
// the user enters in order to work correctly, which is forbidden when using MV3
|
|
||||||
name: `${manifest.name} Lite`,
|
|
||||||
...ManifestV3,
|
...ManifestV3,
|
||||||
manifest_version: manifestVersion
|
manifest_version: manifestVersion
|
||||||
} as chrome.runtime.ManifestV3;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Missing manifest definition for manifestVersion ${manifestVersion}`);
|
throw new Error(`Missing manifest definition for manifestVersion ${manifestVersion}`);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"target": "es2021",
|
"target": "es2019",
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { fileURLToPath } from 'url';
|
import { defineConfig, loadEnv } from 'vite';
|
||||||
import webExtension from '@samrum/vite-plugin-web-extension';
|
|
||||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
import { defineConfig, loadEnv, type PluginOption } from 'vite';
|
import webExtension from '@samrum/vite-plugin-web-extension';
|
||||||
import { matches } from './src/lib/match';
|
import path from 'path';
|
||||||
import { getManifest } from './src/manifest';
|
import { getManifest } from './src/manifest';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
@ -11,7 +10,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
plugins: [
|
plugins: [
|
||||||
svelte() as PluginOption,
|
svelte(),
|
||||||
webExtension({
|
webExtension({
|
||||||
manifest: getManifest(Number(env.MANIFEST_VERSION)),
|
manifest: getManifest(Number(env.MANIFEST_VERSION)),
|
||||||
additionalInputs: {
|
additionalInputs: {
|
||||||
@ -19,18 +18,18 @@ export default defineConfig(({ mode }) => {
|
|||||||
{
|
{
|
||||||
fileName: 'src/entries/player/player.html',
|
fileName: 'src/entries/player/player.html',
|
||||||
webAccessible: {
|
webAccessible: {
|
||||||
matches:
|
matches: ['<all_urls>'],
|
||||||
Number(env.MANIFEST_VERSION) === 3
|
excludeEntryFile: true
|
||||||
? Object.values(matches).flatMap((m) => m.domains.map((d) => `*://${d}/*`))
|
|
||||||
: ['<all_urls>']
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}) as unknown as PluginOption
|
})
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: [{ find: '~', replacement: fileURLToPath(new URL('./src', import.meta.url)) }]
|
alias: {
|
||||||
|
'~': path.resolve(__dirname, './src')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user