Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
c57cd03407 | |||
64cf565da3 | |||
bf8a7eb602 | |||
c643a39e2d | |||
fcfbc41fdb | |||
1251f079f5 | |||
ccfc77167b | |||
a10066458f | |||
6a8c705b06 | |||
76a5bac7fd | |||
3aca863a4b | |||
fe14edc0ef | |||
fffe23638c | |||
5214228a72 | |||
7000d5a08b | |||
a21f799e7d | |||
1fb85313dd | |||
6e7410b088 | |||
0054f5da0e | |||
48dc192964 | |||
7c45cacd36 | |||
00514e4e81 | |||
4c1d0d0b63 | |||
a920832945 | |||
7d8d8b6614 | |||
b4052834f2 | |||
9900c33243 | |||
98709bc934 |
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
@ -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'
|
||||||
|
}
|
||||||
|
};
|
56
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install nodejs
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- manifest_version: 2
|
||||||
|
- manifest_version: 3
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install nodejs
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
MANIFEST_VERSION: ${{ matrix.manifest_version }}
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Upload
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: stream-bypass-mv${{ matrix.manifest_version }}
|
||||||
|
path: ./dist
|
||||||
|
if-no-files-found: error
|
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
.idea/
|
.idea/
|
||||||
build/
|
|
||||||
dist/
|
dist/
|
||||||
|
release/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
2
.npmrc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
auto-install-peers=false
|
||||||
|
strict-peer-dependencies=false
|
8
.prettierignore
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
package-lock.json
|
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" } }]
|
||||||
|
}
|
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022 ByteDream
|
Copyright (c) 2022-NOW ByteDream
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
116
README.md
@ -12,12 +12,12 @@ A multi-browser addon / extension for multiple streaming providers which redirec
|
|||||||
<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/stars/stream-bypass?label=Firefox%20Store%20Stars&style=flat-square" alt="Firefox Addon Stars">
|
<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://chromewebstore.google.com/detail/ddfpfjomnakfckhmilacnbokdaknamdb">
|
||||||
|
<img src="https://img.shields.io/chrome-web-store/users/ddfpfjomnakfckhmilacnbokdaknamdb?style=flat-square&label=Chrome%20Users" alt="Chrome Users">
|
||||||
|
</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">
|
||||||
@ -27,7 +27,9 @@ A multi-browser addon / extension for multiple streaming providers which redirec
|
|||||||
•
|
•
|
||||||
<a href="#-supported-sites">Supported Sites 📜</a>
|
<a href="#-supported-sites">Supported Sites 📜</a>
|
||||||
•
|
•
|
||||||
<a href="#%EF%B8%8F-building">Building ⚙️</a>
|
<a href="#%EF%B8%8F-building">Building 🛠️</a>
|
||||||
|
•
|
||||||
|
<a href="#%EF%B8%8F-settings">Settings ⚙️</a>
|
||||||
•
|
•
|
||||||
<a href="#-license">License ⚖</a>
|
<a href="#-license">License ⚖</a>
|
||||||
</p>
|
</p>
|
||||||
@ -45,44 +47,49 @@ Additionally, this enables you to download the video by right-clicking it and ju
|
|||||||
|
|
||||||
## 📥 Installation
|
## 📥 Installation
|
||||||
|
|
||||||
### Firefox
|
### Official browser stores
|
||||||
|
|
||||||
Install the addon directly from the [firefox addon store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/).
|
The best way to install the extension are the official browser extension stores:
|
||||||
|
|
||||||
### Chromium / Google Chrome
|
- [Firefox Addon Store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/)
|
||||||
|
- [Chrome Web Store](https://chromewebstore.google.com/detail/ddfpfjomnakfckhmilacnbokdaknamdb)
|
||||||
|
|
||||||
1. Download the zipfile from the [latest release](https://smartrelease.bytedream.org/github/ByteDream/stream-bypass/stream_bypass-{tag}.zip) and unzip it (with [7zip](https://www.7-zip.org/) or something like that).
|
### Manual installation
|
||||||
2. Go into your browser and type `chrome://extensions` in the address bar.
|
|
||||||
3. Turn the developer mode in the top right corner on.
|
|
||||||
4. Click Load unpacked.
|
|
||||||
5. Choose the cloned / unzipped directory.
|
|
||||||
|
|
||||||
### Opera
|
- Firefox
|
||||||
|
- Download `stream-bypass-<version>-mv2.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)
|
||||||
1. Download the zipfile from the [latest release](https://smartrelease.bytedream.org/github/ByteDream/stream-bypass/stream_bypass-{tag}.zip) and unzip it (with [7zip](https://www.7-zip.org/) or something like that).
|
- Go into your browser and type `about:debugging#/runtime/this-firefox` in the address bar
|
||||||
2. Go into your browser and type `opera://extensions` in the address bar.
|
- Click the `Load Temporary Add-on...` button and choose the `manifest.json` file in the unzipped directory
|
||||||
3. Turn the developer mode in the top right corner on.
|
- Chromium / Google Chrome
|
||||||
4. Click Load unpacked.
|
> As nearly every browser other than Firefox is based on Chromium, this should be the same for most of them
|
||||||
5. Choose the cloned / unzipped directory.
|
- 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)
|
||||||
|
- 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
|
||||||
|
- Click `Load unpacked` and choose the unzipped directory
|
||||||
|
|
||||||
## 📜 Supported sites
|
## 📜 Supported sites
|
||||||
|
|
||||||
| Site | Supported | Note |
|
| Site | Supported | Note |
|
||||||
|-----------------------------------------------------------------------|-----------|--------------------------------------------------------------------------------------------------------------|
|
| --------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------ |
|
||||||
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔️ | |
|
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔️ | |
|
||||||
| [filemoon.sx](https://filemoon.sx) | ✔ | |
|
| [filemoon.sx](https://filemoon.sx) | ✔ | |
|
||||||
| [mcloud.to](https://mcloud.to/) | ❌ | Reverse engineering the site costs too much time ([#5](https://github.com/ByteDream/stream-bypass/issues/5)) |
|
| [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) | ✔ ️ | |
|
| [mixdrop.co](https://mixdrop.co) | ✔ ️ | |
|
||||||
| [mp4upload.com](https://mp4upload.com) | ✔ | |
|
| [mp4upload.com](https://mp4upload.com) | ✔ | |
|
||||||
| [newgrounds.com](https://newgrounds.com) | ✔ | |
|
| [newgrounds.com](https://newgrounds.com) | ✔ | |
|
||||||
| [streamtape.com](https://streamtape.com) | ✔ | |
|
| [streamtape.com](https://streamtape.com) | ✔ | |
|
||||||
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | |
|
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | |
|
||||||
| [upstream.to](https://upstream.to) | ✔ | |
|
| [upstream.to](https://upstream.to) | ✔ | |
|
||||||
| [videovard.sx](https://videovard.sx) | ❌ | Reverse engineering the site costs too much time |
|
| [videovard.sx](https://videovard.sx) | ❌ | Reverse engineering the site costs too much time |
|
||||||
| [vidoza.net](https://vidoza.net) | ✔ | |
|
| [vidmoly.me](https://vidmoly.me) | ✔ | |
|
||||||
| [vidstream.pro](https://vidstream.pro) | ❌ | Reverse engineering the site costs too much time ([#5](https://github.com/ByteDream/stream-bypass/issues/5)) |
|
| [vidoza.net](https://vidoza.net) | ✔ | |
|
||||||
| [voe.sx](https://voe.sx) | ✔ | |
|
| [vidstream.pro](https://vidstream.pro) | ❌ | Reverse engineering the site costs too much time ([#5](https://github.com/ByteDream/stream-bypass/issues/5)) |
|
||||||
| [vupload.com](https://vupload.com) | ✔ | |
|
| [voe.sx](https://voe.sx) | ✔ | |
|
||||||
|
| [vupload.com](https://vupload.com) | ✔ | |
|
||||||
|
| [kwik.cx](https://kwik.cx) | ✔ | |
|
||||||
|
| [dropload.io](https://dropload.io) | ✔ | |
|
||||||
|
| [supervideo.tv](https://supervideo.tv) | ✔ | |
|
||||||
|
| [goodstream.uno](https://goodstream.uno) | ✔ | |
|
||||||
|
|
||||||
- ✔️: Everything ok.
|
- ✔️: 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.
|
- ⚠: Included in the addon but will probably not work. See `Note` in this case, an explanation why will stand there in the most cases.
|
||||||
@ -99,35 +106,68 @@ Some sites put much effort in obfuscating their code / how they receive the vide
|
|||||||
</ul>
|
</ul>
|
||||||
</details>
|
</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.
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
|
|
||||||
- `npm` installed.
|
- `npm` installed.
|
||||||
- A copy of this repository and a shell / console open in the copied directory.
|
- A copy of this repository and a shell / console open in the copied directory.
|
||||||
|
|
||||||
If the requirements are satisfied, you can continue with the following commands:
|
If the requirements are satisfied, you can continue with the following commands:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# install all dependencies
|
# install all dependencies
|
||||||
$ npm install
|
$ npm install
|
||||||
|
|
||||||
# build the extension source to a build/ directory
|
# build the extension source to the dist/ directory
|
||||||
$ npm run build
|
$ npm run build
|
||||||
|
|
||||||
# same as build + create a bundle zipfile at dist/
|
# same as build + more optimizations and browser specific settings at release/
|
||||||
$ npm run bundle
|
$ npm run release:firefox # or "release:chrome" to create a release for chromium based browsers
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Install
|
##### Install
|
||||||
|
|
||||||
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).
|
If you want to use the addon in Chromium or any browser which is based on it, 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.
|
||||||
2. Select 'This Firefox' tab (maybe named different, depending on your language).
|
2. Select 'This Firefox' tab (maybe named different, depending on your language).
|
||||||
3. Under `Temporary Extensions`, click `Load Temporary Add-on`.
|
3. Under `Temporary Extensions`, click `Load Temporary Add-on`.
|
||||||
4. Choose any file in the directory where the compiled sources are.
|
4. Choose any file in the directory where the compiled sources are.
|
||||||
|
|
||||||
|
## ⚙️ Settings
|
||||||
|
|
||||||
|
### <ins>ff2mpv: use mpv to directly play streams</ins>
|
||||||
|
|
||||||
|
ff2mpv is located at this repository: https://github.com/woodruffw/ff2mpv
|
||||||
|
|
||||||
|
Steps to get it set up:
|
||||||
|
|
||||||
|
- In the [Usage](https://github.com/woodruffw/ff2mpv#usage) section of the ff2mpv repository pick the installation instruction for your operating system (Linux/Windows/macOS; you do not need the browser addon).
|
||||||
|
- Scroll down to `Install manually`
|
||||||
|
- Follow instructions for Firefox/Chrome
|
||||||
|
- Edit the `ff2mpv.json` you created:
|
||||||
|
- Firefox: Add `{55dd42e8-3dd9-455a-b4fe-86664881b10c}` to `allowed_extensions` ->
|
||||||
|
```
|
||||||
|
"allowed_extensions": [
|
||||||
|
"ff2mpv@yossarian.net",
|
||||||
|
"{55dd42e8-3dd9-455a-b4fe-86664881b10c}"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
- 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` ->
|
||||||
|
```
|
||||||
|
"allowed_origins": [
|
||||||
|
"chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/",
|
||||||
|
"chrome-extension://your-id-her/"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## ⚖ License
|
## ⚖ License
|
||||||
|
|
||||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.
|
||||||
|
8711
package-lock.json
generated
Normal file
87
package.json
@ -1,41 +1,50 @@
|
|||||||
{
|
{
|
||||||
"name": "stream-bypass",
|
"name": "stream-bypass",
|
||||||
"version": "2.1.7",
|
"version": "3.0.1",
|
||||||
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
|
"displayName": "Stream Bypass",
|
||||||
"main": "src/index.ts",
|
"author": "ByteDream",
|
||||||
"scripts": {
|
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
|
||||||
"build": "node tasks/build.ts",
|
"scripts": {
|
||||||
"bundle": "node tasks/build.ts && web-ext build -s build -a dist -o",
|
"build": "vite build",
|
||||||
"bundle:all": "node tasks/build.ts && web-ext build -s build -a dist -o && web-ext sign -s build -a dist",
|
"watch": "vite build --watch --mode development --minify false",
|
||||||
"check": "web-ext lint -s build"
|
"dev": "vite",
|
||||||
},
|
"serve:firefox": "web-ext run --start-url \"about:debugging#/runtime/this-firefox\" --source-dir ./dist/",
|
||||||
"repository": {
|
"serve:chrome": "web-ext run -t chromium --start-url \"https://example.com\" --source-dir ./dist/",
|
||||||
"type": "git",
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||||
"url": "git+https://github.com/ByteDream/stream-bypass.git"
|
"lint": "prettier --check --plugin prettier-plugin-svelte . && eslint .",
|
||||||
},
|
"format": "prettier --write --plugin prettier-plugin-svelte .",
|
||||||
"author": "ByteDream",
|
"release:firefox": "MANIFEST_VERSION=2 vite build --outDir release/firefox",
|
||||||
"license": "MIT",
|
"release:chrome": "MANIFEST_VERSION=3 vite build --outDir release/chrome"
|
||||||
"bugs": {
|
},
|
||||||
"url": "https://github.com/ByteDream/stream-bypass/issues"
|
"license": "MIT",
|
||||||
},
|
"repository": {
|
||||||
"homepage": "https://github.com/ByteDream/stream-bypass#readme",
|
"type": "git",
|
||||||
"devDependencies": {
|
"url": "git+https://github.com/ByteDream/stream-bypass.git"
|
||||||
"@rollup/plugin-commonjs": "^22.0.0",
|
},
|
||||||
"@rollup/plugin-node-resolve": "^14.0.1",
|
"bugs": {
|
||||||
"@rollup/plugin-replace": "^4.0.0",
|
"url": "https://github.com/ByteDream/stream-bypass/issues"
|
||||||
"@rollup/plugin-typescript": "^8.3.3",
|
},
|
||||||
"@typescript-eslint/eslint-plugin": "^5.27.1",
|
"devDependencies": {
|
||||||
"@typescript-eslint/parser": "^5.27.1",
|
"@samrum/vite-plugin-web-extension": "^5.1.0",
|
||||||
"@types/chrome": "^0.0.196",
|
"@sveltejs/vite-plugin-svelte": "^2.5.3",
|
||||||
"@types/node-sass": "^4.11.2",
|
"@tsconfig/svelte": "^5.0.4",
|
||||||
"@types/yazl": "^2.4.2",
|
"@types/chrome": "^0.0.268",
|
||||||
"eslint": "^8.17.0",
|
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
||||||
"hls.js": "^1.1.5",
|
"@typescript-eslint/parser": "^7.9.0",
|
||||||
"node-sass": "^7.0.1",
|
"eslint": "^8.57.0",
|
||||||
"node-sass-package-importer": "^5.3.2",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"rollup": "^2.75.6",
|
"eslint-plugin-svelte": "^2.39.0",
|
||||||
"tippy.js": "^6.3.7",
|
"hls.js": "^1.5.8",
|
||||||
"typescript": "^4.7.3",
|
"prettier": "^3.2.5",
|
||||||
"web-ext": "^7.0.0"
|
"prettier-plugin-svelte": "^3.2.3",
|
||||||
}
|
"sass": "^1.77.2",
|
||||||
|
"svelte": "^4.2.17",
|
||||||
|
"svelte-check": "^3.7.1",
|
||||||
|
"svelte-preprocess": "^5.1.4",
|
||||||
|
"tslib": "^2.6.2",
|
||||||
|
"typescript": "^5.4.5",
|
||||||
|
"vite": "^4.5.3",
|
||||||
|
"web-ext": "^7.11.0"
|
||||||
|
},
|
||||||
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
1
public/icons/stream-bypass.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="738.248" height="738.248" viewBox="0 0 195.328 195.328"><path d="M108.902 30.525c-53.858 0-97.664 43.806-97.664 97.664 0 53.86 43.806 97.665 97.664 97.665 53.86 0 97.664-43.806 97.664-97.665 0-53.858-43.805-97.664-97.664-97.664zm0 13.434c46.6 0 84.23 37.632 84.23 84.23 0 46.6-37.63 84.23-84.23 84.23-46.599 0-84.232-37.63-84.232-84.23 0-46.598 37.633-84.23 84.232-84.23z" style="color:#000;fill:#000;-inkscape-stroke:none" transform="translate(-11.238 -30.525)"/><path d="M8.986 105.297v96.926l83.94-48.463-7.7-4.446zm10.266 17.781 53.143 30.682-53.143 30.681z" style="color:#000;fill:#000;-inkscape-stroke:none" transform="matrix(1.30853 0 0 1.30853 44.795 -103.534)"/><path d="M345.131 686.085c-122.49-10.29-225.617-86.136-271.115-199.397-9.596-23.888-15.72-47.197-20.05-76.315-2.763-18.58-2.76-64.116.004-82.784 7.5-50.64 23.648-93.74 49.956-133.33 39.979-60.166 96.484-103.56 164.472-126.311 53.98-18.063 110.354-21.415 165.476-9.838 82.83 17.396 153.434 65.363 200.69 136.344 10.702 16.073 25.836 46.006 32.68 64.634 6.703 18.242 13.106 43.275 16.28 63.647 2.098 13.461 2.487 20.483 2.534 45.734.06 31.25-.585 38.592-5.576 63.53-26.78 133.796-136.647 235.614-272.394 252.435-17.246 2.137-47.675 2.935-62.957 1.651zm77.476-197.054c113.33-65.435 206.175-119.33 206.322-119.767.277-.827-411.864-239.171-414.659-239.8-1.374-.31-1.586 31.672-1.586 239.605 0 227.996.097 239.938 1.934 239.45 1.063-.283 94.658-54.052 207.989-119.487z" style="fill:#fff;stroke:#000;stroke-width:5.46616" transform="scale(.26458)"/></svg>
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/icons/stream-bypass@128px.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
public/icons/stream-bypass@16px.png
Normal file
After Width: | Height: | Size: 713 B |
BIN
public/icons/stream-bypass@32px.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
public/icons/stream-bypass@48px.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
public/icons/stream-bypass@96px.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
1
public/icons/stream-bypass_disabled.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="738.248" height="738.248" viewBox="0 0 195.328 195.328"><path d="M108.902 30.525c-53.858 0-97.664 43.806-97.664 97.664 0 53.86 43.806 97.665 97.664 97.665 53.86 0 97.664-43.806 97.664-97.665 0-53.858-43.805-97.664-97.664-97.664zm0 13.434c46.6 0 84.23 37.632 84.23 84.23 0 46.6-37.63 84.23-84.23 84.23-46.599 0-84.232-37.63-84.232-84.23 0-46.598 37.633-84.23 84.232-84.23z" style="color:#000;fill:#000;-inkscape-stroke:none" transform="translate(-11.238 -30.525)"/><path d="M8.986 105.297v96.926l83.94-48.463-7.7-4.446zm10.266 17.781 53.143 30.682-53.143 30.681z" style="color:#000;fill:#000;-inkscape-stroke:none" transform="matrix(1.30853 0 0 1.30853 44.795 -103.534)"/><path d="M345.131 686.085c-122.49-10.29-225.617-86.136-271.115-199.397-9.596-23.888-15.72-47.197-20.05-76.315-2.763-18.58-2.76-64.116.004-82.784 7.5-50.64 23.648-93.74 49.956-133.33 39.979-60.166 96.484-103.56 164.472-126.311 53.98-18.063 110.354-21.415 165.476-9.838 82.83 17.396 153.434 65.363 200.69 136.344 10.702 16.073 25.836 46.006 32.68 64.634 6.703 18.242 13.106 43.275 16.28 63.647 2.098 13.461 2.487 20.483 2.534 45.734.06 31.25-.585 38.592-5.576 63.53-26.78 133.796-136.647 235.614-272.394 252.435-17.246 2.137-47.675 2.935-62.957 1.651zm77.476-197.054c113.33-65.435 206.175-119.33 206.322-119.767.277-.827-411.864-239.171-414.659-239.8-1.374-.31-1.586 31.672-1.586 239.605 0 227.996.097 239.938 1.934 239.45 1.063-.283 94.658-54.052 207.989-119.487z" style="fill:gray;stroke:#000;stroke-width:5.46616" transform="scale(.26458)"/></svg>
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/icons/stream-bypass_disabled@128px.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
public/icons/stream-bypass_disabled@16px.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
public/icons/stream-bypass_disabled@32px.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/icons/stream-bypass_disabled@48px.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
public/icons/stream-bypass_disabled@96px.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
@ -1,18 +0,0 @@
|
|||||||
import {getMatch} from "./match/match";
|
|
||||||
import {storageDelete, storageGet, storageSet} from "./store/store";
|
|
||||||
import {Match} from "./match/matches";
|
|
||||||
|
|
||||||
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).host)) !== undefined) {
|
|
||||||
await storageSet('redirect', match.id)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await storageDelete('redirect')
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
urls: ['<all_urls>'],
|
|
||||||
types: ['main_frame', 'sub_frame']
|
|
||||||
})
|
|
38
src/entries/background/mv2.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import './shared';
|
||||||
|
|
||||||
|
import type { Match } from '~/lib/match';
|
||||||
|
import { storageDelete, storageGet, storageSet } from '~/lib/settings';
|
||||||
|
import { getMatch } from '~/lib/match';
|
||||||
|
|
||||||
|
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||||
|
async (details) => {
|
||||||
|
const referer: { domain: string } | undefined = await storageGet('referer');
|
||||||
|
if (referer === undefined) return;
|
||||||
|
|
||||||
|
details.requestHeaders.push({
|
||||||
|
name: 'Referer',
|
||||||
|
value: `https://${referer.domain}/`
|
||||||
|
});
|
||||||
|
|
||||||
|
await storageDelete('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 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
src/entries/background/mv3.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './shared';
|
5
src/entries/background/shared.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
chrome.runtime.onMessage.addListener(async (message) => {
|
||||||
|
if (message.action == 'ff2mpv') {
|
||||||
|
await chrome.runtime.sendNativeMessage('ff2mpv', { url: message.url });
|
||||||
|
}
|
||||||
|
});
|
50
src/entries/contentScript/main.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import type { Match } from '~/lib/match';
|
||||||
|
import { getMatch } from '~/lib/match';
|
||||||
|
import { Other, Redirect } from '~/lib/settings';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let match: Match;
|
||||||
|
let redirect = false;
|
||||||
|
if ((match = await getMatch(window.location.host)) === null) {
|
||||||
|
if ((match = await Redirect.get()) === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
redirect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const re = document.body.innerHTML.match(match.regex);
|
||||||
|
if (re === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (redirect) {
|
||||||
|
await Redirect.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = await match.match(re);
|
||||||
|
|
||||||
|
// send the url to the ff2mpv (https://github.com/woodruffw/ff2mpv) application
|
||||||
|
if (await Other.getFf2mpv()) {
|
||||||
|
await chrome.runtime.sendMessage({ action: 'ff2mpv', url: url });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match.replace && !url.includes('.m3u8')) {
|
||||||
|
const player = document.createElement('video');
|
||||||
|
player.style.width = '100%';
|
||||||
|
player.style.height = '100%';
|
||||||
|
player.controls = true;
|
||||||
|
player.src = url;
|
||||||
|
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
document.body.append(player);
|
||||||
|
} else {
|
||||||
|
window.location.assign(
|
||||||
|
chrome.runtime.getURL(
|
||||||
|
`src/entries/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}&domain=${
|
||||||
|
window.location.hostname
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
64
src/entries/player/Player.svelte
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { play } from '~/entries/player/player';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let errorMessage: string | null = null;
|
||||||
|
let videoElem: HTMLVideoElement;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
await play(videoElem);
|
||||||
|
videoElem.controls = true;
|
||||||
|
} catch (e) {
|
||||||
|
errorMessage = e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
|
<video id="video" bind:this={videoElem} />
|
||||||
|
{#if errorMessage}
|
||||||
|
<div id="message-container">
|
||||||
|
<p>
|
||||||
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
|
{@html errorMessage}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- eslint-disable -->
|
||||||
|
<style lang="scss" global>
|
||||||
|
body {
|
||||||
|
background-color: #131313;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#message-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
& a,
|
||||||
|
& a:visited {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
16
src/entries/player/player.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Stream Bypass</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="module">
|
||||||
|
import Player from '~/entries/player/Player.svelte';
|
||||||
|
|
||||||
|
new Player({
|
||||||
|
target: document.body
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
42
src/entries/player/player.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { matches } from '~/lib/match';
|
||||||
|
import Hls from 'hls.js';
|
||||||
|
import { storageSet } from '~/lib/settings';
|
||||||
|
|
||||||
|
async function playNative(url: string, videoElem: HTMLVideoElement) {
|
||||||
|
videoElem.src = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function playHls(url: string, videoElem: HTMLVideoElement) {
|
||||||
|
if (videoElem.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
|
videoElem.src = url;
|
||||||
|
} else if (Hls.isSupported()) {
|
||||||
|
const hls = new Hls({
|
||||||
|
enableWorker: false
|
||||||
|
});
|
||||||
|
hls.loadSource(url);
|
||||||
|
hls.attachMedia(videoElem);
|
||||||
|
} else {
|
||||||
|
throw 'Failed to play m3u8 video (hls is not supported). Try again or create a new issue <a href="https://github.com/ByteDream/stream-bypass/issues/new">here</a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function play(videoElem: HTMLVideoElement) {
|
||||||
|
const urlQuery = new URLSearchParams(window.location.search);
|
||||||
|
const id = urlQuery.get('id');
|
||||||
|
const url = decodeURIComponent(urlQuery.get('url'));
|
||||||
|
const domain = urlQuery.get('domain');
|
||||||
|
|
||||||
|
const match = matches[id];
|
||||||
|
if (match === undefined) {
|
||||||
|
throw `Invalid id: ${id}. Please report this <a href="https://github.com/ByteDream/stream-bypass/issues">here</a>`;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
199
src/entries/popup/Popup.svelte
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { matches } from '~/lib/match';
|
||||||
|
import { Hosters, Other } from '~/lib/settings';
|
||||||
|
|
||||||
|
let hostersEnabled: boolean;
|
||||||
|
let hosters = [];
|
||||||
|
(async () => {
|
||||||
|
hostersEnabled = !(await Hosters.getAllDisabled());
|
||||||
|
|
||||||
|
const disabled = await Hosters.getDisabled();
|
||||||
|
hosters = Object.values(matches).map((m) => {
|
||||||
|
m['active'] = disabled.findIndex((p) => p.id == m.id) == -1;
|
||||||
|
m['disabled'] = !hostersEnabled;
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
let ff2mpvEnabled: boolean;
|
||||||
|
(async () => {
|
||||||
|
ff2mpvEnabled = await Other.getFf2mpv();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div>
|
||||||
|
<h3 class="header">Hosters</h3>
|
||||||
|
<div class="buttons super-buttons">
|
||||||
|
<button
|
||||||
|
class:active={hostersEnabled}
|
||||||
|
on:click={async () => {
|
||||||
|
await Hosters.enableAll();
|
||||||
|
hostersEnabled = true;
|
||||||
|
hosters = hosters.map((m) => {
|
||||||
|
m['disabled'] = false;
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}}>On</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class:active={!hostersEnabled}
|
||||||
|
on:click={async () => {
|
||||||
|
await Hosters.disableAll();
|
||||||
|
hostersEnabled = false;
|
||||||
|
hosters = hosters.map((m) => {
|
||||||
|
m['disabled'] = true;
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}}>Off</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<table class="setting-table">
|
||||||
|
{#each hosters as hoster}
|
||||||
|
<tr>
|
||||||
|
<td class="setting-name">
|
||||||
|
<p>{hoster.name}</p>
|
||||||
|
</td>
|
||||||
|
<td class="buttons">
|
||||||
|
<button
|
||||||
|
class:disabled={hoster.disabled}
|
||||||
|
class:active={hoster.active}
|
||||||
|
on:click={async () => {
|
||||||
|
if (hoster.disabled) return;
|
||||||
|
await Hosters.enable(hoster);
|
||||||
|
hoster.active = true;
|
||||||
|
}}>On</button
|
||||||
|
>
|
||||||
|
<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
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class:active={!ff2mpvEnabled}
|
||||||
|
on:click={async () => {
|
||||||
|
await Other.setFf2mpv(false);
|
||||||
|
ff2mpvEnabled = false;
|
||||||
|
}}>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>
|
||||||
|
|
||||||
|
<!-- eslint-disable -->
|
||||||
|
<style lang="scss" global>
|
||||||
|
body {
|
||||||
|
background-color: #2b2a33;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bug-notice {
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
display: block;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 5px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-name {
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
16
src/entries/popup/index.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html style="width: fit-content; height: 500px; overflow-y: hidden" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Stream Bypass</title>
|
||||||
|
</head>
|
||||||
|
<body style="width: fit-content; height: 500px; overflow-y: scroll">
|
||||||
|
<script type="module">
|
||||||
|
import Popup from '~/entries/popup/Popup.svelte';
|
||||||
|
|
||||||
|
new Popup({
|
||||||
|
target: document.body
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
src/entries/popup/main.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import App from './Popup.svelte';
|
||||||
|
|
||||||
|
new App({
|
||||||
|
target: document.getElementById('app')
|
||||||
|
});
|
@ -1,77 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="195.32812mm"
|
|
||||||
height="195.32812mm"
|
|
||||||
viewBox="0 0 195.32812 195.32812"
|
|
||||||
version="1.1"
|
|
||||||
id="svg5"
|
|
||||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
|
||||||
sodipodi:docname="logo-disabled.svg"
|
|
||||||
inkscape:export-filename="disabled_128.png"
|
|
||||||
inkscape:export-xdpi="16.644812"
|
|
||||||
inkscape:export-ydpi="16.644812"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview7"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="0.90509668"
|
|
||||||
inkscape:cx="428.68349"
|
|
||||||
inkscape:cy="362.94465"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1015"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="36"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1" />
|
|
||||||
<defs
|
|
||||||
id="defs2">
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="powerstroke"
|
|
||||||
id="path-effect3140"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
offset_points="3,0"
|
|
||||||
not_jump="false"
|
|
||||||
sort_points="true"
|
|
||||||
interpolator_type="CubicBezierJohan"
|
|
||||||
interpolator_beta="0.2"
|
|
||||||
start_linecap_type="zerowidth"
|
|
||||||
linejoin_type="extrp_arc"
|
|
||||||
miter_limit="4"
|
|
||||||
scale_width="1"
|
|
||||||
end_linecap_type="zerowidth" />
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-11.238281,-30.525391)">
|
|
||||||
<path
|
|
||||||
style="color:#000000;fill:#000000;-inkscape-stroke:none"
|
|
||||||
d="m 108.90234,30.525391 c -53.858772,0 -97.664059,43.805286 -97.664059,97.664059 0,53.85878 43.805287,97.66407 97.664059,97.66407 53.85878,0 97.66407,-43.80529 97.66407,-97.66407 0,-53.858773 -43.80529,-97.664059 -97.66407,-97.664059 z m 0,13.433593 c 46.59895,0 84.23047,37.631526 84.23047,84.230466 0,46.59895 -37.63152,84.23047 -84.23047,84.23047 -46.59894,0 -84.232418,-37.63152 -84.232418,-84.23047 0,-46.59894 37.633478,-84.230466 84.232418,-84.230466 z"
|
|
||||||
id="path2592" />
|
|
||||||
<path
|
|
||||||
style="color:#000000;fill:#000000;-inkscape-stroke:none"
|
|
||||||
d="m 8.9863281,105.29688 v 96.92578 l 83.9394529,-48.46289 -7.699219,-4.44532 z m 10.2656249,17.78125 53.142578,30.68164 -53.142578,30.68164 z"
|
|
||||||
id="path2646"
|
|
||||||
transform="matrix(1.3085265,0,0,1.3085265,56.033246,-73.00885)" />
|
|
||||||
<path
|
|
||||||
style="fill:#808080;stroke:#000000;stroke-width:5.46616"
|
|
||||||
d="M 345.13114,686.08528 C 222.64086,675.79533 119.51392,599.94853 74.016249,486.68834 64.419992,462.79978 58.296197,439.49115 53.966274,410.37335 51.203358,391.79333 51.205528,346.25661 53.970219,327.58931 61.4703,276.94849 77.618278,233.84988 103.92601,194.25835 143.90493,134.09253 200.40964,90.698198 268.39759,67.947934 322.37862,49.884696 378.75198,46.533168 433.87378,58.109989 c 82.82951,17.396066 153.43381,65.362801 200.69115,136.344381 10.701,16.07313 25.83502,46.0053 32.67962,64.63397 6.70243,18.2417 13.10531,43.27443 16.2797,63.64715 2.0974,13.46078 2.48648,20.4825 2.53407,45.73343 0.0589,31.24975 -0.58565,38.59247 -5.57665,63.52913 -26.77909,133.797 -136.64661,235.61462 -272.39375,252.4357 -17.24628,2.13706 -47.67501,2.9353 -62.95678,1.65153 z M 422.60679,489.0315 C 535.93722,423.59631 628.78214,369.70078 628.92885,369.26367 629.20612,368.43746 217.06471,130.09256 214.27041,129.46316 c -1.37426,-0.30954 -1.58595,31.67289 -1.58595,239.60604 0,227.99566 0.0965,239.93763 1.9335,239.44933 1.06342,-0.28267 94.65839,-54.05183 207.98883,-119.48703 z"
|
|
||||||
id="path2606"
|
|
||||||
transform="matrix(0.26458333,0,0,0.26458333,11.238281,30.525391)" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 2.7 KiB |
@ -1,77 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="195.32812mm"
|
|
||||||
height="195.32812mm"
|
|
||||||
viewBox="0 0 195.32812 195.32812"
|
|
||||||
version="1.1"
|
|
||||||
id="svg5"
|
|
||||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
|
||||||
sodipodi:docname="logo.svg"
|
|
||||||
inkscape:export-filename="logo_128.png"
|
|
||||||
inkscape:export-xdpi="16.644812"
|
|
||||||
inkscape:export-ydpi="16.644812"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview7"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="0.90509668"
|
|
||||||
inkscape:cx="198.32136"
|
|
||||||
inkscape:cy="397.19514"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1015"
|
|
||||||
inkscape:window-x="1920"
|
|
||||||
inkscape:window-y="36"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1" />
|
|
||||||
<defs
|
|
||||||
id="defs2">
|
|
||||||
<inkscape:path-effect
|
|
||||||
effect="powerstroke"
|
|
||||||
id="path-effect3140"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1"
|
|
||||||
offset_points="3,0"
|
|
||||||
not_jump="false"
|
|
||||||
sort_points="true"
|
|
||||||
interpolator_type="CubicBezierJohan"
|
|
||||||
interpolator_beta="0.2"
|
|
||||||
start_linecap_type="zerowidth"
|
|
||||||
linejoin_type="extrp_arc"
|
|
||||||
miter_limit="4"
|
|
||||||
scale_width="1"
|
|
||||||
end_linecap_type="zerowidth" />
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-11.238281,-30.525391)">
|
|
||||||
<path
|
|
||||||
style="color:#000000;fill:#000000;-inkscape-stroke:none"
|
|
||||||
d="m 108.90234,30.525391 c -53.858772,0 -97.664059,43.805286 -97.664059,97.664059 0,53.85878 43.805287,97.66407 97.664059,97.66407 53.85878,0 97.66407,-43.80529 97.66407,-97.66407 0,-53.858773 -43.80529,-97.664059 -97.66407,-97.664059 z m 0,13.433593 c 46.59895,0 84.23047,37.631526 84.23047,84.230466 0,46.59895 -37.63152,84.23047 -84.23047,84.23047 -46.59894,0 -84.232418,-37.63152 -84.232418,-84.23047 0,-46.59894 37.633478,-84.230466 84.232418,-84.230466 z"
|
|
||||||
id="path2592" />
|
|
||||||
<path
|
|
||||||
style="color:#000000;fill:#000000;-inkscape-stroke:none"
|
|
||||||
d="m 8.9863281,105.29688 v 96.92578 l 83.9394529,-48.46289 -7.699219,-4.44532 z m 10.2656249,17.78125 53.142578,30.68164 -53.142578,30.68164 z"
|
|
||||||
id="path2646"
|
|
||||||
transform="matrix(1.3085265,0,0,1.3085265,56.033246,-73.00885)" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;stroke:#000000;stroke-width:5.46616"
|
|
||||||
d="M 345.13114,686.08528 C 222.64086,675.79533 119.51392,599.94853 74.016249,486.68834 64.419992,462.79978 58.296197,439.49115 53.966274,410.37335 51.203358,391.79333 51.205528,346.25661 53.970219,327.58931 61.4703,276.94849 77.618278,233.84988 103.92601,194.25835 143.90493,134.09253 200.40964,90.698198 268.39759,67.947933 322.37862,49.884696 378.75198,46.533168 433.87378,58.109989 c 82.82951,17.396066 153.43381,65.362801 200.69115,136.344371 10.701,16.07314 25.83502,46.00531 32.67962,64.63398 6.70243,18.2417 13.10531,43.27443 16.2797,63.64715 2.0974,13.46078 2.48648,20.4825 2.53407,45.73343 0.0589,31.24975 -0.58565,38.59247 -5.57665,63.52913 -26.7791,133.797 -136.64661,235.61462 -272.39375,252.43569 -17.24628,2.13707 -47.67501,2.9353 -62.95678,1.65154 z M 422.60679,489.0315 C 535.93722,423.59631 628.78214,369.70078 628.92885,369.26367 629.20612,368.43746 217.06471,130.09256 214.27041,129.46316 c -1.37426,-0.30954 -1.58595,31.67289 -1.58595,239.60604 0,227.99566 0.0965,239.93763 1.9335,239.44933 1.06342,-0.28267 94.65839,-54.05183 207.98883,-119.48703 z"
|
|
||||||
id="path374"
|
|
||||||
transform="matrix(0.26458333,0,0,0.26458333,11.238281,30.525391)" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 2.3 KiB |
38
src/index.ts
@ -1,38 +0,0 @@
|
|||||||
import {getMatch} from "./match/match";
|
|
||||||
import {storageDelete, storageGet} from "./store/store";
|
|
||||||
import {Match, matches} from "./match/matches";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
let match: Match;
|
|
||||||
let redirect = false;
|
|
||||||
if ((match = await getMatch(window.location.host)) === undefined) {
|
|
||||||
let id: string
|
|
||||||
if ((id = await storageGet('redirect')) !== undefined) {
|
|
||||||
redirect = true
|
|
||||||
match = matches.find(m => m.id === id)
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const re = document.body.innerHTML.match(match.regex)
|
|
||||||
if (re === null) return
|
|
||||||
if (redirect) await storageDelete('redirect')
|
|
||||||
|
|
||||||
const url = await match.match(re)
|
|
||||||
|
|
||||||
if (match.replace && !url.endsWith('.m3u8')) {
|
|
||||||
const player = document.createElement('video')
|
|
||||||
player.style.width = '100%'
|
|
||||||
player.style.height = '100%'
|
|
||||||
player.controls = true
|
|
||||||
player.src = url
|
|
||||||
|
|
||||||
document.body.innerHTML = ''
|
|
||||||
document.body.append(player)
|
|
||||||
} else {
|
|
||||||
window.location.assign(chrome.runtime.getURL(`ui/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}&domain=${window.location.host}`))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
277
src/lib/match.ts
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
import { unpack } from './utils';
|
||||||
|
import { Hosters } from './settings';
|
||||||
|
|
||||||
|
export interface Match {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
domains: string[];
|
||||||
|
replace?: boolean;
|
||||||
|
regex: RegExp;
|
||||||
|
notice?: string;
|
||||||
|
|
||||||
|
match(match: RegExpMatchArray): Promise<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Doodstream: Match = {
|
||||||
|
name: 'Doodstream',
|
||||||
|
id: 'doodstream',
|
||||||
|
domains: [
|
||||||
|
'doodstream.com',
|
||||||
|
'dood.pm',
|
||||||
|
'dood.ws',
|
||||||
|
'dood.wf',
|
||||||
|
'dood.cx',
|
||||||
|
'dood.sh',
|
||||||
|
'dood.watch',
|
||||||
|
'dood.to',
|
||||||
|
'dood.so',
|
||||||
|
'dood.la',
|
||||||
|
'dood.re',
|
||||||
|
'dood.yt',
|
||||||
|
'doods.pro',
|
||||||
|
'ds2play.com',
|
||||||
|
'dooood.com'
|
||||||
|
],
|
||||||
|
replace: true,
|
||||||
|
regex: /(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
||||||
|
headers: {
|
||||||
|
Range: 'bytes=0-'
|
||||||
|
},
|
||||||
|
referrer: `https://${window.location.host}/e/${
|
||||||
|
window.location.pathname.split('/').slice(-1)[0]
|
||||||
|
}`
|
||||||
|
});
|
||||||
|
return `${await response.text()}1234567890${match[2]}${Date.now()}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DropLoad: Match = {
|
||||||
|
name: 'Dropload',
|
||||||
|
id: 'dropload',
|
||||||
|
domains: ['dropload.ui'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const unpacked = await unpack(match[0]);
|
||||||
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Filemoon: Match = {
|
||||||
|
name: 'Filemoon',
|
||||||
|
id: 'filemoon',
|
||||||
|
domains: ['filemoon.sx', 'filemoon.in'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const unpacked = await unpack(match[0]);
|
||||||
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GoodStream: Match = {
|
||||||
|
name: 'Goodstream',
|
||||||
|
id: 'goodstream',
|
||||||
|
domains: ['goodstream.uno'],
|
||||||
|
regex: /(?<=file:\s+").*(?=")/g,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
return match[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Kwik: Match = {
|
||||||
|
name: 'Kwik',
|
||||||
|
id: 'kwik',
|
||||||
|
domains: ['kwik.cx'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const unpacked = await unpack(match[0]);
|
||||||
|
return unpacked.match(/(?<=source=').*(?=')/)[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,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const unpacked = await unpack(match[0]);
|
||||||
|
const url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)[0];
|
||||||
|
return `https:${url}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Mp4Upload: Match = {
|
||||||
|
name: 'Mp4Upload',
|
||||||
|
id: 'mp4upload',
|
||||||
|
domains: ['mp4upload.com'],
|
||||||
|
replace: true,
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const unpacked = await unpack(match[0]);
|
||||||
|
return unpacked.match(/(?<=player.src\(").*(?=")/)[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Newgrounds: Match = {
|
||||||
|
name: 'Newgrounds',
|
||||||
|
id: 'newgrounds',
|
||||||
|
domains: ['newgrounds.com'],
|
||||||
|
regex: /.*/gm,
|
||||||
|
|
||||||
|
match: async () => {
|
||||||
|
const id = window.location.pathname.split('/').slice(-1)[0];
|
||||||
|
const response = await fetch(`https://www.newgrounds.com/portal/video/${id}`, {
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const json = await response.json();
|
||||||
|
return decodeURI(json['sources'][Object.keys(json['sources'])[0]][0]['src']);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Streamtape: Match = {
|
||||||
|
name: 'Streamtape',
|
||||||
|
id: 'streamtape',
|
||||||
|
domains: ['streamtape.com', 'streamtape.net', 'shavetape.cash'],
|
||||||
|
regex: /id=.*(?=')/gm,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
let i = 0;
|
||||||
|
while (i < match.length) {
|
||||||
|
if (match[++i - 1] == match[i]) {
|
||||||
|
return `https://streamtape.com/get_video?${match[i]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the old method as fallback
|
||||||
|
return `https://streamtape.com/get_video?${match.reverse()[0]}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Streamzz: Match = {
|
||||||
|
name: 'Streamzz',
|
||||||
|
id: 'streamzz',
|
||||||
|
domains: ['streamzz.to', 'streamz.ws'],
|
||||||
|
regex: /(?<=\|)\w{2,}/gm,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
return `https://get.${location.hostname.split('.')[0]}.tw/getlink-${
|
||||||
|
match.sort((a, b) => b.length - a.length)[0]
|
||||||
|
}.dll`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SuperVideo: Match = {
|
||||||
|
name: 'Supervideo',
|
||||||
|
id: 'supervideo',
|
||||||
|
domains: ['supervideo.tv'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const unpacked = await unpack(match[0]);
|
||||||
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Upstream: Match = {
|
||||||
|
name: 'Upstream',
|
||||||
|
id: 'upstream',
|
||||||
|
domains: ['upstream.to'],
|
||||||
|
regex: /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
const unpacked = await unpack(match[0]);
|
||||||
|
return unpacked.match(/(?<=file:").*(?=")/)[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Vidmoly: Match = {
|
||||||
|
name: 'Vidmoly',
|
||||||
|
id: 'vidmoly',
|
||||||
|
domains: ['vidmoly.me', 'vidmoly.to'],
|
||||||
|
regex: /(?<=file:").+\.m3u8/gm,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
alert('a');
|
||||||
|
return match[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Vidoza: Match = {
|
||||||
|
name: 'Vidoza',
|
||||||
|
id: 'vidoza',
|
||||||
|
domains: ['vidoza.net'],
|
||||||
|
regex: /(?<=src:\s?").+?(?=")/gm,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
return match[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Voe: Match = {
|
||||||
|
name: 'Voe',
|
||||||
|
id: 'voe',
|
||||||
|
domains: ['voe.sx'],
|
||||||
|
regex: /https?:\/\/\S*m3u8.+(?=['"])/gm,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
return match[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Vupload: Match = {
|
||||||
|
name: 'Vupload',
|
||||||
|
id: 'vupload',
|
||||||
|
domains: ['vupload.com'],
|
||||||
|
regex: /(?<=src:\s?").+?(?=")/gm,
|
||||||
|
|
||||||
|
match: async (match: RegExpMatchArray) => {
|
||||||
|
return match[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const matches = {
|
||||||
|
[Doodstream.id]: Doodstream,
|
||||||
|
[DropLoad.id]: DropLoad,
|
||||||
|
[Filemoon.id]: Filemoon,
|
||||||
|
[GoodStream.id]: GoodStream,
|
||||||
|
[Kwik.id]: Kwik,
|
||||||
|
[Mixdrop.id]: Mixdrop,
|
||||||
|
[Mp4Upload.id]: Mp4Upload,
|
||||||
|
[Newgrounds.id]: Newgrounds,
|
||||||
|
[Streamtape.id]: Streamtape,
|
||||||
|
[Streamzz.id]: Streamzz,
|
||||||
|
[SuperVideo.id]: SuperVideo,
|
||||||
|
[Upstream.id]: Upstream,
|
||||||
|
[Vidmoly.id]: Vidmoly,
|
||||||
|
[Vidoza.id]: Vidoza,
|
||||||
|
[Voe.id]: Voe,
|
||||||
|
[Vupload.id]: Vupload
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getMatch(domain: string): Promise<Match | null> {
|
||||||
|
if (await Hosters.getAllDisabled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const match of Object.values(matches)) {
|
||||||
|
if (
|
||||||
|
match.domains.indexOf(domain) !== -1 &&
|
||||||
|
!(await Hosters.getDisabled().then((d) => d.find((p) => p.id == match.id)))
|
||||||
|
) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
88
src/lib/settings.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import type { Match } from './match';
|
||||||
|
import { matches } from './match';
|
||||||
|
|
||||||
|
export const Hosters = {
|
||||||
|
getDisabled: async () => {
|
||||||
|
const disabled = await storageGet<string[]>('hosters.disabled', []);
|
||||||
|
return disabled.map((id) => matches[id]).filter((m) => m !== undefined);
|
||||||
|
},
|
||||||
|
disable: async (match: Match) => {
|
||||||
|
const disabled = await storageGet('hosters.disabled', []);
|
||||||
|
const index = disabled.indexOf(match.id);
|
||||||
|
if (index === -1) {
|
||||||
|
disabled.push(match.id);
|
||||||
|
await storageSet('hosters.disabled', disabled);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enable: async (match: Match) => {
|
||||||
|
const disabled = await storageGet('hosters.disabled', []);
|
||||||
|
const index = disabled.indexOf(match.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
disabled.splice(index, 1);
|
||||||
|
await storageSet('hosters.disabled', disabled);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAllDisabled: async () => {
|
||||||
|
return await storageGet<boolean>('hosters.allDisabled', false);
|
||||||
|
},
|
||||||
|
disableAll: async () => {
|
||||||
|
await storageSet('hosters.allDisabled', true);
|
||||||
|
},
|
||||||
|
enableAll: async () => {
|
||||||
|
await storageSet('hosters.allDisabled', false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Redirect = {
|
||||||
|
get: async (): Promise<Match | null> => {
|
||||||
|
return matches[await storageGet<string>('redirect')] || null;
|
||||||
|
},
|
||||||
|
set: async (match: Match) => {
|
||||||
|
await storageSet('redirect', match.id);
|
||||||
|
},
|
||||||
|
delete: async () => {
|
||||||
|
await storageDelete('redirect');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Other = {
|
||||||
|
getFf2mpv: async () => {
|
||||||
|
return await storageGet('other.ff2mpv', true);
|
||||||
|
},
|
||||||
|
setFf2mpv: async (enable: boolean) => {
|
||||||
|
await storageSet('other.ff2mpv', enable);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export 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));
|
||||||
|
|
||||||
|
chrome.storage.local.get(key, (entry) => {
|
||||||
|
const value = entry[key];
|
||||||
|
resolve(value === undefined ? defaultValue : value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function storageSet<T>(key: string, value: T) {
|
||||||
|
let resolve: () => void;
|
||||||
|
const promise = new Promise<void>((r) => (resolve = r));
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
[key]: value
|
||||||
|
};
|
||||||
|
chrome.storage.local.set(obj, () => resolve());
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function storageDelete(key: string) {
|
||||||
|
let resolve: () => void;
|
||||||
|
const promise = new Promise<void>((r) => (resolve = r));
|
||||||
|
|
||||||
|
chrome.storage.local.remove(key, () => resolve());
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
94
src/lib/utils.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Adapted from http://matthewfl.com/unPacker.html by matthew@matthewfl.com
|
||||||
|
export async function unpack(packed: string): Promise<string> {
|
||||||
|
const context = `
|
||||||
|
{
|
||||||
|
eval: function (c) {
|
||||||
|
packed = c;
|
||||||
|
},
|
||||||
|
window: {},
|
||||||
|
document: {}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const toExecute = `
|
||||||
|
function() {
|
||||||
|
let packed = "";
|
||||||
|
with(${context}) {
|
||||||
|
${packed}
|
||||||
|
};
|
||||||
|
return packed;
|
||||||
|
}'
|
||||||
|
`;
|
||||||
|
|
||||||
|
const res: string = await runInPageContext(toExecute);
|
||||||
|
return res
|
||||||
|
.replace(/;/g, ';\n')
|
||||||
|
.replace(/{/g, '\n{\n')
|
||||||
|
.replace(/}/g, '\n}\n')
|
||||||
|
.replace(/\n;\n/g, ';\n')
|
||||||
|
.replace(/\n\\n/g, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from: https://github.com/arikw/extension-page-context
|
||||||
|
async function runInPageContext<T>(toExecute: string): Promise<T> {
|
||||||
|
// test that we are running with the allow-scripts permission
|
||||||
|
try {
|
||||||
|
window.sessionStorage;
|
||||||
|
} catch (ignore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returned value container
|
||||||
|
const resultMessageId = crypto.randomUUID();
|
||||||
|
|
||||||
|
// prepare script container
|
||||||
|
const scriptElm = document.createElement('script');
|
||||||
|
scriptElm.setAttribute('type', 'application/javascript');
|
||||||
|
|
||||||
|
const code = `
|
||||||
|
(
|
||||||
|
async function () {
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
id: ${resultMessageId}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
document.documentElement.appendChild(scriptElm);
|
||||||
|
|
||||||
|
// clean up script element
|
||||||
|
scriptElm.remove();
|
||||||
|
|
||||||
|
let resolve: (value: T) => void;
|
||||||
|
let reject: (value: any) => void;
|
||||||
|
const promise = new Promise<T>((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
function onResult(event: MessageEvent) {
|
||||||
|
if (event.data.id === resultMessageId) {
|
||||||
|
window.removeEventListener('message', onResult);
|
||||||
|
if (event.data.error !== undefined) {
|
||||||
|
return reject(JSON.parse(event.data.error));
|
||||||
|
}
|
||||||
|
return resolve(event.data.result !== undefined ? JSON.parse(event.data.result) : undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', onResult);
|
||||||
|
|
||||||
|
return await promise;
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
{
|
|
||||||
"manifest_version": 2,
|
|
||||||
"name": "Stream Bypass",
|
|
||||||
"author": "ByteDream",
|
|
||||||
"description": "A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.",
|
|
||||||
"version": "2.1.6",
|
|
||||||
"homepage_url": "https://github.com/ByteDream/stream-bypass",
|
|
||||||
"browser_specific_settings": {
|
|
||||||
"gecko": {
|
|
||||||
"id": "{55dd42e8-3dd9-455a-b4fe-86664881b10c}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"all_frames": true,
|
|
||||||
"matches": [
|
|
||||||
"<all_urls>"
|
|
||||||
],
|
|
||||||
"js": [
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"run_at": "document_end"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"background": {
|
|
||||||
"scripts": [
|
|
||||||
"background.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"permissions": [
|
|
||||||
"storage",
|
|
||||||
"webRequest",
|
|
||||||
"<all_urls>"
|
|
||||||
],
|
|
||||||
"web_accessible_resources": [
|
|
||||||
"ui/player/*"
|
|
||||||
],
|
|
||||||
"browser_action": {
|
|
||||||
"default_icon": {
|
|
||||||
"48": "icons/logo_48.png",
|
|
||||||
"128": "icons/logo_128.png"
|
|
||||||
},
|
|
||||||
"default_title": "Stream Bypass",
|
|
||||||
"default_popup": "ui/popup/popup.html"
|
|
||||||
},
|
|
||||||
"icons": {
|
|
||||||
"48": "icons/logo_48.png",
|
|
||||||
"128": "icons/logo_128.png"
|
|
||||||
}
|
|
||||||
}
|
|
85
src/manifest.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import pkg from '../package.json';
|
||||||
|
import { matches } from './lib/match';
|
||||||
|
|
||||||
|
const sharedManifest: Partial<chrome.runtime.ManifestBase> = {
|
||||||
|
browser_specific_settings: {
|
||||||
|
gecko: {
|
||||||
|
id: '{55dd42e8-3dd9-455a-b4fe-86664881b10c}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content_scripts: [
|
||||||
|
{
|
||||||
|
all_frames: true,
|
||||||
|
matches: Object.values(matches).flatMap((m) => m.domains.map((d) => `*://${d}/*`)),
|
||||||
|
js: ['src/entries/contentScript/main.ts'],
|
||||||
|
run_at: 'document_end'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
icons: {
|
||||||
|
16: 'icons/stream-bypass@16px.png',
|
||||||
|
32: 'icons/stream-bypass@32px.png',
|
||||||
|
48: 'icons/stream-bypass@48px.png',
|
||||||
|
96: 'icons/stream-bypass@96px.png',
|
||||||
|
128: 'icons/stream-bypass@128px.png'
|
||||||
|
},
|
||||||
|
permissions: ['storage', 'nativeMessaging']
|
||||||
|
};
|
||||||
|
|
||||||
|
const browserAction = {
|
||||||
|
default_icon: {
|
||||||
|
16: 'icons/stream-bypass@16px.png',
|
||||||
|
32: 'icons/stream-bypass@32px.png'
|
||||||
|
},
|
||||||
|
default_popup: 'src/entries/popup/index.html'
|
||||||
|
};
|
||||||
|
|
||||||
|
const ManifestV2 = {
|
||||||
|
...sharedManifest,
|
||||||
|
background: {
|
||||||
|
scripts: ['src/entries/background/mv2.ts'],
|
||||||
|
persistent: true
|
||||||
|
},
|
||||||
|
content_scripts: [{ ...sharedManifest.content_scripts[0], matches: ['<all_urls>'] }],
|
||||||
|
browser_action: browserAction,
|
||||||
|
permissions: [...sharedManifest.permissions, 'webRequest', 'webRequestBlocking', '<all_urls>']
|
||||||
|
};
|
||||||
|
|
||||||
|
const ManifestV3 = {
|
||||||
|
...sharedManifest,
|
||||||
|
action: browserAction,
|
||||||
|
background: {
|
||||||
|
service_worker: 'src/entries/background/mv3.ts'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getManifest(
|
||||||
|
manifestVersion: number
|
||||||
|
): chrome.runtime.ManifestV2 | chrome.runtime.ManifestV3 {
|
||||||
|
const manifest = {
|
||||||
|
author: pkg.author,
|
||||||
|
description: pkg.description,
|
||||||
|
name: pkg.displayName ?? pkg.name,
|
||||||
|
version: pkg.version
|
||||||
|
};
|
||||||
|
|
||||||
|
if (manifestVersion === 2) {
|
||||||
|
return {
|
||||||
|
...manifest,
|
||||||
|
...ManifestV2,
|
||||||
|
manifest_version: manifestVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manifestVersion === 3) {
|
||||||
|
return {
|
||||||
|
...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,
|
||||||
|
manifest_version: manifestVersion
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Missing manifest definition for manifestVersion ${manifestVersion}`);
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
import {Match, matches} from "./matches";
|
|
||||||
import {getAllDisabled, getDisabled} from "../store/store";
|
|
||||||
|
|
||||||
export async function getMatch(host: string): Promise<Match | undefined> {
|
|
||||||
if (await getAllDisabled()) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const match of matches) {
|
|
||||||
if (match.domains.some(v => host.indexOf(v) !== -1) && !((await getDisabled()).some(v => v === match))) {
|
|
||||||
return match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,220 +0,0 @@
|
|||||||
export enum Reliability {
|
|
||||||
HIGH,
|
|
||||||
NORMAL,
|
|
||||||
LOW,
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class Match {
|
|
||||||
name: string
|
|
||||||
id: string
|
|
||||||
reliability: Reliability
|
|
||||||
domains: string[]
|
|
||||||
replace?: boolean
|
|
||||||
regex: RegExp
|
|
||||||
abstract match(match: RegExpMatchArray): Promise<string>
|
|
||||||
|
|
||||||
notice?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
class Doodstream implements Match {
|
|
||||||
name = 'Doodstream'
|
|
||||||
id = 'doodstream'
|
|
||||||
reliability = Reliability.NORMAL
|
|
||||||
domains = [
|
|
||||||
'doodstream.com',
|
|
||||||
'dood.pm',
|
|
||||||
'dood.ws',
|
|
||||||
'dood.wf'
|
|
||||||
]
|
|
||||||
replace = true
|
|
||||||
regex = new RegExp(/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
|
||||||
headers: {
|
|
||||||
'Range': 'bytes=0-'
|
|
||||||
},
|
|
||||||
referrer: `https://${window.location.host}/e/${window.location.pathname.split('/').slice(-1)[0]}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
return `${await response.text()}1234567890${match[2]}${Date.now()}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Filemoon implements Match {
|
|
||||||
name = 'Filemoon'
|
|
||||||
id = 'filemoon'
|
|
||||||
reliability = Reliability.HIGH
|
|
||||||
domains = [
|
|
||||||
'filemoon.sx'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
const start_idx = match.indexOf('moon')
|
|
||||||
|
|
||||||
const prefix = `${match[start_idx]}-${match[start_idx-1]}-${match[start_idx-2]}-${match[start_idx-3]}`
|
|
||||||
const time = match.find(m => m.length === 10 && !isNaN(parseInt(m)))
|
|
||||||
const offset = !isNaN(parseInt(match[start_idx-12])) && parseInt(match[start_idx-12]).toString().length == match[start_idx-12].length ? 0 : -1
|
|
||||||
|
|
||||||
return `https://${prefix}.filemoon.${match[start_idx-4]}/${match[start_idx-5]}/${match[start_idx-6]}/${match[start_idx-7]}/${match[start_idx-8]}/master.m3u8?t=${match[start_idx-11]}${offset != 0 ? `-${match[start_idx-12]}` : ''}&s=${time}&e=${match[start_idx + offset - 12]}&sp=${match[start_idx + offset - 18]}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Mixdrop implements Match {
|
|
||||||
name = 'Mixdrop'
|
|
||||||
id = 'mixdrop'
|
|
||||||
reliability = Reliability.NORMAL
|
|
||||||
domains = [
|
|
||||||
'mixdrop.co'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
const prefix = /(?<=\/\/)[a|s](?=-)/.exec(document.body.innerHTML)[0]
|
|
||||||
const subdomain = match[1].length < match[2].length ? match[1] : match[2]
|
|
||||||
const domain = match.slice().sort((a, b) => b.length - a.length).find(m => /^[a-z]+$/.test(m))
|
|
||||||
const id = match[1].length > match[2].length ? match[1] : match[2]
|
|
||||||
const tld = match.find(m => ['net', 'io', 'to', 'sx', 'com'].indexOf(m) !== -1)
|
|
||||||
const s = match.slice().sort((a, b) => b.length - a.length).slice(1)[0]
|
|
||||||
const e_t = match.find(m => m.length === 10 && !isNaN(parseInt(m)))
|
|
||||||
|
|
||||||
return `https://${prefix}-${subdomain}.${domain}.${tld}/v/${id}.mp4?s=${s}&e=${e_t}&_t=${e_t}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Mp4Upload implements Match {
|
|
||||||
name = 'Mp4Upload'
|
|
||||||
id = 'mp4upload'
|
|
||||||
reliability = Reliability.NORMAL
|
|
||||||
domains = [
|
|
||||||
'mp4upload.com'
|
|
||||||
]
|
|
||||||
replace = true
|
|
||||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
let id = match.slice().reduce((a, b) => a.length >= b.length ? a : b)
|
|
||||||
return `https://www4.mp4upload.com:282/d/${id}/video.mp4`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Newgrounds implements Match {
|
|
||||||
name = 'Newgrounds'
|
|
||||||
id = 'newgrounds'
|
|
||||||
reliability = Reliability.HIGH
|
|
||||||
domains = [
|
|
||||||
'newgrounds.com'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/.*/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
let id = window.location.pathname.split('/').slice(-1)[0]
|
|
||||||
let response = await fetch(`https://www.newgrounds.com/portal/video/${id}`, {
|
|
||||||
headers: {
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
let json = await response.json()
|
|
||||||
return decodeURI(json['sources'][Object.keys(json['sources'])[0]][0]['src'])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Streamtape implements Match {
|
|
||||||
name = 'Streamtape'
|
|
||||||
id = 'streamtape'
|
|
||||||
reliability = Reliability.NORMAL
|
|
||||||
domains = [
|
|
||||||
'streamtape.com'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/id=.*(?=')/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
return `https://streamtape.com/get_video?${match.reverse()[0]}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Streamzz implements Match {
|
|
||||||
name = 'Streamzz'
|
|
||||||
id = 'streamzz'
|
|
||||||
reliability = Reliability.LOW
|
|
||||||
domains = [
|
|
||||||
'streamzz.to',
|
|
||||||
'streamz.ws'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
return `https://get.${document.domain.split('.')[0]}.tw/getlink-${match.sort((a, b) => b.length - a.length)[0]}.dll`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Upstream implements Match {
|
|
||||||
name = 'Upstream'
|
|
||||||
id = 'upstream'
|
|
||||||
reliability = Reliability.NORMAL
|
|
||||||
domains = [
|
|
||||||
'upstream.to'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
return `https://${match[49]}.upstreamcdn.co/hls/${match[148]}/master.m3u8`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Vidoza implements Match {
|
|
||||||
name = 'Vidoza'
|
|
||||||
id = 'vidoza'
|
|
||||||
reliability = Reliability.HIGH
|
|
||||||
domains = [
|
|
||||||
'vidoza.net'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/(?<=src:\s?").+?(?=")/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
return match[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Voe implements Match {
|
|
||||||
name = 'Voe'
|
|
||||||
id = 'voe'
|
|
||||||
reliability = Reliability.HIGH
|
|
||||||
domains = [
|
|
||||||
'voe.sx'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/https?:\/\/\S*m3u8.+(?=')/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
return match[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Vupload implements Match {
|
|
||||||
name = 'Vupload'
|
|
||||||
id = 'vupload'
|
|
||||||
reliability = Reliability.HIGH
|
|
||||||
domains = [
|
|
||||||
'vupload.com'
|
|
||||||
]
|
|
||||||
regex = new RegExp(/(?<=src:\s?").+?(?=")/gm)
|
|
||||||
|
|
||||||
async match(match: RegExpMatchArray): Promise<string> {
|
|
||||||
return match[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const matches = [
|
|
||||||
new Doodstream(),
|
|
||||||
new Filemoon(),
|
|
||||||
new Mixdrop(),
|
|
||||||
new Mp4Upload(),
|
|
||||||
new Newgrounds(),
|
|
||||||
new Streamtape(),
|
|
||||||
new Streamzz(),
|
|
||||||
new Upstream(),
|
|
||||||
new Vidoza(),
|
|
||||||
new Voe(),
|
|
||||||
new Vupload()
|
|
||||||
]
|
|
@ -1,68 +0,0 @@
|
|||||||
import {Match, matches} from "../match/matches";
|
|
||||||
|
|
||||||
export async function storageGet(key: string): Promise<any> {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
chrome.storage.local.get(key, (value) => {
|
|
||||||
resolve(value[key])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function storageSet(key: string, value: any) {
|
|
||||||
const obj = {}
|
|
||||||
obj[key] = value
|
|
||||||
await chrome.storage.local.set(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function storageDelete(key: string) {
|
|
||||||
await chrome.storage.local.remove(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getDisabled(): Promise<Match[]> {
|
|
||||||
const localMatches = []
|
|
||||||
|
|
||||||
for (const disabled of (await storageGet('disabled') as string[]) || []) {
|
|
||||||
let m: Match
|
|
||||||
if ((m = matches.find((v) => v.id === disabled)) !== undefined) {
|
|
||||||
localMatches.push(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return localMatches
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getAllDisabled(): Promise<boolean> {
|
|
||||||
const value = await storageGet('all')
|
|
||||||
return value !== undefined ? String(value).toLowerCase() === 'true' : false
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function enableAll() {
|
|
||||||
await storageSet('all', false)
|
|
||||||
await chrome.browserAction.setIcon({
|
|
||||||
path: null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function disableAll() {
|
|
||||||
await storageSet('all', true)
|
|
||||||
await chrome.browserAction.setIcon({
|
|
||||||
path: {
|
|
||||||
48: chrome.runtime.getURL('icons/disabled_48.png'),
|
|
||||||
128: chrome.runtime.getURL('icons/disabled_128.png')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function enable(match: Match) {
|
|
||||||
const disabled = await storageGet('disabled') || []
|
|
||||||
disabled.splice(disabled.indexOf(match.id))
|
|
||||||
await storageSet('disabled', disabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function disable(match: Match) {
|
|
||||||
const disabled = await storageGet('disabled') as string[] || []
|
|
||||||
if (disabled.indexOf(match.id) !== undefined) {
|
|
||||||
disabled.push(match.id)
|
|
||||||
await storageSet('disabled', disabled)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "esnext",
|
|
||||||
"target": "es2019",
|
|
||||||
"removeComments": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"es5",
|
|
||||||
"scripthost",
|
|
||||||
"es2015.collection",
|
|
||||||
"es2015.promise"
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"../node_modules"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Stream Bypass</title>
|
|
||||||
<link rel="stylesheet" href="player.css">
|
|
||||||
<script src="player.js" defer></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<video id="video"></video>
|
|
||||||
<div id="message-container">
|
|
||||||
<p id="message"></p>
|
|
||||||
<br>
|
|
||||||
<p>Open a new issue <a href="https://github.com/ByteDream/stream-bypass/issues">here</a></p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,34 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #131313;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body, video {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
video {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#message-container {
|
|
||||||
visibility: hidden;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
& * {
|
|
||||||
visibility: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
& a, & a:visited {
|
|
||||||
color: red
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
import {matches} from "../../match/matches";
|
|
||||||
// @ts-ignore
|
|
||||||
import Hls from "hls.js";
|
|
||||||
|
|
||||||
function show_message(message: string) {
|
|
||||||
document.getElementById('message').innerText = message
|
|
||||||
document.getElementById('message-container').style.visibility = 'visible'
|
|
||||||
document.getElementById('video').hidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
async function play_native(url: string) {
|
|
||||||
const video = document.getElementById('video') as HTMLVideoElement
|
|
||||||
video.controls = true
|
|
||||||
video.src = url
|
|
||||||
}
|
|
||||||
|
|
||||||
async function play_hls(url: string) {
|
|
||||||
const video = document.getElementById('video') as HTMLVideoElement
|
|
||||||
video.controls = true
|
|
||||||
|
|
||||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
|
||||||
video.src = url
|
|
||||||
} else if (Hls.isSupported()) {
|
|
||||||
const hls = new Hls({
|
|
||||||
enableWorker: false
|
|
||||||
})
|
|
||||||
hls.loadSource(url)
|
|
||||||
hls.attachMedia(video)
|
|
||||||
} else {
|
|
||||||
show_message('Failed to play m3u8 video (hls is not supported). Try again or create a new issue <a href="https://github.com/ByteDream/stream-bypass/issues/new">here</a>')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const urlQuery = new URLSearchParams(window.location.search)
|
|
||||||
const id = urlQuery.get('id')
|
|
||||||
const url = decodeURIComponent(urlQuery.get('url'))
|
|
||||||
const domain = urlQuery.get('domain')
|
|
||||||
|
|
||||||
const match = matches.find((m) => m.id === id)
|
|
||||||
if (match === undefined) {
|
|
||||||
show_message(`Invalid id: ${id}. Please report this <a href="https://github.com/ByteDream/stream-bypass/issues">here</a>`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
document.title = `Stream Bypass (${domain})`
|
|
||||||
|
|
||||||
new URL(url).pathname.endsWith('.m3u8') ? await play_hls(url) : await play_native(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
@ -1,23 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Stream-Bypass</title>
|
|
||||||
<link rel="stylesheet" href="popup.css">
|
|
||||||
<script src="popup.js" defer></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="container">
|
|
||||||
<div id="all">
|
|
||||||
<div class="buttons">
|
|
||||||
<a>On</a>
|
|
||||||
<a>Off</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<table id="sub-container">
|
|
||||||
</table>
|
|
||||||
<a id="error" href="https://github.com/ByteDream/stream-bypass/issues">Something does not work</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,66 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #2b2a33;
|
|
||||||
font-weight: bold;
|
|
||||||
max-height: 500px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, p {
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
margin: 5px 0;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
border: 1px solid #281515;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: normal;
|
|
||||||
padding: 5px 8px;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: rgba(255, 65, 65, 0.74);
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
background-color: grey;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
&#error {
|
|
||||||
border: none;
|
|
||||||
display: block;
|
|
||||||
font-weight: lighter;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
text-align: center;
|
|
||||||
padding: 10px 0 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
margin: 3px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#all {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 10px 0
|
|
||||||
}
|
|
||||||
|
|
||||||
.low-reliability {
|
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration-color: rgb(255, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.normal-reliability {
|
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration-color: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.high-reliability {
|
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration-color: #00ff00;
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
import {getDisabled, disable, enable, getAllDisabled, enableAll, disableAll} from "../../store/store";
|
|
||||||
import {matches, Reliability} from "../../match/matches";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const disabled = await getDisabled()
|
|
||||||
|
|
||||||
const subContainer = document.getElementById('sub-container')
|
|
||||||
for (const m of matches) {
|
|
||||||
const row = document.createElement('tr')
|
|
||||||
|
|
||||||
const name = document.createElement('td')
|
|
||||||
const nameValue = document.createElement('p')
|
|
||||||
nameValue.innerText = m.name
|
|
||||||
switch (m.reliability) {
|
|
||||||
case Reliability.LOW:
|
|
||||||
nameValue.classList.add('low-reliability')
|
|
||||||
break
|
|
||||||
case Reliability.NORMAL:
|
|
||||||
nameValue.classList.add('normal-reliability')
|
|
||||||
break
|
|
||||||
case Reliability.HIGH:
|
|
||||||
nameValue.classList.add('high-reliability')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttons = document.createElement('td')
|
|
||||||
buttons.classList.add('buttons')
|
|
||||||
const on = document.createElement('a')
|
|
||||||
on.innerText = 'On'
|
|
||||||
const off = document.createElement('a')
|
|
||||||
off.innerText = 'Off'
|
|
||||||
disabled.find((v) => v.id === m.id) === undefined ? on.classList.add('active') : off.classList.add('active')
|
|
||||||
|
|
||||||
on.onclick = async function () {
|
|
||||||
if (!on.classList.contains('disabled')) {
|
|
||||||
await enable(m)
|
|
||||||
on.classList.add('active')
|
|
||||||
off.classList.remove('active')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
off.onclick = async function () {
|
|
||||||
if (!off.classList.contains('disabled')) {
|
|
||||||
await disable(m)
|
|
||||||
on.classList.remove('active')
|
|
||||||
off.classList.add('active')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
name.append(nameValue)
|
|
||||||
buttons.append(on, off)
|
|
||||||
row.append(name, buttons)
|
|
||||||
subContainer.append(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
const allOnButton = document.getElementById('all').getElementsByTagName('a')[0]
|
|
||||||
const allOffButton = document.getElementById('all').getElementsByTagName('a')[1]
|
|
||||||
|
|
||||||
if (await getAllDisabled()) {
|
|
||||||
const allBtns = document.getElementById('sub-container').getElementsByTagName('a')
|
|
||||||
for (let i = 0; i < allBtns.length; i++) {
|
|
||||||
allBtns[i].classList.add('disabled')
|
|
||||||
}
|
|
||||||
allOffButton.classList.add('active')
|
|
||||||
} else {
|
|
||||||
allOnButton.classList.add('active')
|
|
||||||
}
|
|
||||||
|
|
||||||
allOnButton.onclick = async () => {
|
|
||||||
if (!allOnButton.classList.contains('active')) {
|
|
||||||
allOnButton.classList.add('active')
|
|
||||||
allOffButton.classList.remove('active')
|
|
||||||
|
|
||||||
const allBtns = document.getElementById('sub-container').getElementsByTagName('a')
|
|
||||||
for (let i = 0; i < allBtns.length; i++) {
|
|
||||||
allBtns[i].classList.remove('disabled')
|
|
||||||
}
|
|
||||||
|
|
||||||
await enableAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allOffButton.onclick = async () => {
|
|
||||||
if (!allOffButton.classList.contains('active')) {
|
|
||||||
allOffButton.classList.add('active')
|
|
||||||
allOnButton.classList.remove('active')
|
|
||||||
|
|
||||||
const allBtns = document.getElementById('sub-container').getElementsByTagName('a')
|
|
||||||
for (let i = 0; i < allBtns.length; i++) {
|
|
||||||
allBtns[i].classList.add('disabled')
|
|
||||||
}
|
|
||||||
|
|
||||||
await disableAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
3
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/// <reference types="svelte" />
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
/// <reference types="@samrum/vite-plugin-web-extension/client" />
|
7
svelte.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import sveltePreprocess from 'svelte-preprocess';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||||
|
// for more information about preprocessors
|
||||||
|
preprocess: sveltePreprocess()
|
||||||
|
};
|
107
tasks/build.ts
@ -1,107 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const rollup = require('rollup')
|
|
||||||
const rollupPluginCommonJs = require('@rollup/plugin-commonjs')
|
|
||||||
const rollupPluginNodeResolve = require('@rollup/plugin-node-resolve').default
|
|
||||||
const rollupPluginReplace = require('@rollup/plugin-replace')
|
|
||||||
const rollupPluginTypescript = require('@rollup/plugin-typescript')
|
|
||||||
const sass = require('node-sass')
|
|
||||||
const sassPluginNodeImport = require('node-sass-package-importer')
|
|
||||||
const typescript = require('typescript')
|
|
||||||
|
|
||||||
async function buildManifest() {
|
|
||||||
const manifest = JSON.parse(fs.readFileSync('src/manifest.json'))
|
|
||||||
|
|
||||||
manifest['version'] = process.env.npm_package_version
|
|
||||||
|
|
||||||
fs.writeFileSync('src/manifest.json', JSON.stringify(manifest, null, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildMisc() {
|
|
||||||
const files = {
|
|
||||||
'src/manifest.json': 'build/manifest.json',
|
|
||||||
|
|
||||||
'src/icons/logo_48.png': 'build/icons/logo_48.png',
|
|
||||||
'src/icons/logo_128.png': 'build/icons/logo_128.png',
|
|
||||||
'src/icons/disabled_48.png': 'build/icons/disabled_48.png',
|
|
||||||
'src/icons/disabled_128.png': 'build/icons/disabled_128.png',
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [src, dst] of Object.entries(files)) {
|
|
||||||
fs.mkdirSync(path.dirname(dst), {recursive: true})
|
|
||||||
fs.copyFileSync(src, dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildHtml() {
|
|
||||||
const files = {
|
|
||||||
'src/ui/popup/popup.html': 'build/ui/popup/popup.html',
|
|
||||||
'src/ui/player/player.html': 'build/ui/player/player.html'
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [src, dst] of Object.entries(files)) {
|
|
||||||
fs.mkdirSync(path.dirname(dst), {recursive: true})
|
|
||||||
fs.copyFileSync(src, dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildCss() {
|
|
||||||
const files = {
|
|
||||||
'src/ui/popup/popup.scss': 'build/ui/popup/popup.css',
|
|
||||||
'src/ui/player/player.scss': 'build/ui/player/player.css'
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [src, dst] of Object.entries(files)) {
|
|
||||||
const compiled = sass.renderSync({
|
|
||||||
file: src,
|
|
||||||
importer: sassPluginNodeImport()
|
|
||||||
})
|
|
||||||
fs.mkdirSync(path.dirname(dst), {recursive: true})
|
|
||||||
fs.writeFileSync(dst, compiled.css)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildJs() {
|
|
||||||
const files = {
|
|
||||||
'src/ui/popup/popup.ts': 'build/ui/popup/popup.js',
|
|
||||||
'src/ui/player/player.ts': 'build/ui/player/player.js',
|
|
||||||
|
|
||||||
'src/index.ts': 'build/index.js',
|
|
||||||
'src/background.ts': 'build/background.js'
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [src, dst] of Object.entries(files)) {
|
|
||||||
const bundle = await rollup.rollup({
|
|
||||||
input: src,
|
|
||||||
plugins: [
|
|
||||||
rollupPluginNodeResolve({
|
|
||||||
browser: true
|
|
||||||
}),
|
|
||||||
rollupPluginReplace({
|
|
||||||
'process.env.NODE_ENV': JSON.stringify('production')
|
|
||||||
}),
|
|
||||||
rollupPluginTypescript({
|
|
||||||
typescript,
|
|
||||||
tsconfig: 'src/tsconfig.json'
|
|
||||||
}),
|
|
||||||
rollupPluginCommonJs({
|
|
||||||
extensions: ['.js', '.ts']
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
await bundle.write({
|
|
||||||
file: dst,
|
|
||||||
strict: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function build() {
|
|
||||||
await buildManifest()
|
|
||||||
await buildMisc()
|
|
||||||
await buildHtml()
|
|
||||||
await buildCss()
|
|
||||||
await buildJs()
|
|
||||||
}
|
|
||||||
|
|
||||||
build()
|
|
18
tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"target": "es2019",
|
||||||
|
"module": "esnext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["src/*"]
|
||||||
|
},
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||||
|
}
|
38
vite.config.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { defineConfig, loadEnv } from 'vite';
|
||||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
import webExtension from '@samrum/vite-plugin-web-extension';
|
||||||
|
import path from 'path';
|
||||||
|
import { getManifest } from './src/manifest';
|
||||||
|
import { matches } from './src/lib/match';
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(({ mode }) => {
|
||||||
|
const env = loadEnv(mode, process.cwd(), '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [
|
||||||
|
svelte(),
|
||||||
|
webExtension({
|
||||||
|
manifest: getManifest(Number(env.MANIFEST_VERSION)),
|
||||||
|
additionalInputs: {
|
||||||
|
html: [
|
||||||
|
{
|
||||||
|
fileName: 'src/entries/player/player.html',
|
||||||
|
webAccessible: {
|
||||||
|
matches:
|
||||||
|
Number(env.MANIFEST_VERSION) === 3
|
||||||
|
? Object.values(matches).flatMap((m) => m.domains.map((d) => `*://${d}/*`))
|
||||||
|
: ['<all_urls>']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'~': path.resolve(__dirname, './src')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|