Compare commits
133 Commits
Author | SHA1 | Date | |
---|---|---|---|
5214228a72 | |||
7000d5a08b | |||
a21f799e7d | |||
1fb85313dd | |||
6e7410b088 | |||
0054f5da0e | |||
48dc192964 | |||
7c45cacd36 | |||
00514e4e81 | |||
4c1d0d0b63 | |||
a920832945 | |||
7d8d8b6614 | |||
b4052834f2 | |||
9900c33243 | |||
98709bc934 | |||
f4cbdd3258 | |||
fa41a8de8e | |||
ce8bc855b9 | |||
03202b2a12 | |||
9f0e1b59ce | |||
d928d25e09 | |||
266771aa13 | |||
d56672d90f | |||
a9ea5fe4b2 | |||
bb3f5384d6 | |||
6989587161 | |||
dd9bf71a5c | |||
6da0050df4 | |||
1a7c22ec0e | |||
175862b098 | |||
fd5a532d0f | |||
8c43eedb23 | |||
e027c2e09e | |||
f9a0656d4d | |||
382d8b1268 | |||
5b8639ce6a | |||
841c824590 | |||
2055a3ea81 | |||
0262d1853c | |||
81da6600e6 | |||
817f5b82f9 | |||
9a17fb0d9b | |||
17f8aab216 | |||
44d4c9cbcf | |||
b34531b982 | |||
e699d3885c | |||
396038a803 | |||
bd64d4ed0b | |||
a207c336b0 | |||
2460657f2a | |||
698ed5ac3c | |||
dc42220f09 | |||
e146649bbf | |||
424e34190c | |||
c5f4f8b246 | |||
e2b8d884af | |||
b07d0b4be6 | |||
2d0441997c | |||
53e040db46 | |||
6d1ff3fbea | |||
4cf76eb62a | |||
672b920f31 | |||
1e166b5ecc | |||
213b996755 | |||
de0d8d5a41 | |||
1f34f74e11 | |||
60a2247b02 | |||
88814fcc83 | |||
f908e8249b | |||
8d57dfbc57 | |||
3aa84ba709 | |||
9ecfbd44e9 | |||
b1bea7687c | |||
c3148a76a3 | |||
210aa5c263 | |||
a55c455fcc | |||
923787bedb | |||
8990e25a72 | |||
9b4bcc6c64 | |||
2e8cce10b5 | |||
9d4b52c107 | |||
806320a0de | |||
774d3b52d7 | |||
31787bb3e3 | |||
e64feb5130 | |||
aa51b5d95f | |||
27c03c0b5b | |||
f48adc3db3 | |||
feaf843584 | |||
0b6021cf52 | |||
8b375cae2c | |||
c9acb929cc | |||
7a69ac83e0 | |||
a9a8609cb8 | |||
e0d4f8747e | |||
f8213772c8 | |||
bce27f2beb | |||
7bfae612f9 | |||
106f4ffda3 | |||
b83d287a42 | |||
a39418e506 | |||
0fc06a78a7 | |||
90bed8b774 | |||
ac31945372 | |||
1683bf2035 | |||
af54a28737 | |||
0f8d323c3c | |||
4c1bfbde62 | |||
9889cf79a2 | |||
230eb074e9 | |||
2a9c00c3a8 | |||
16cf6688ec | |||
4edc3d5f84 | |||
e5552910b2 | |||
2db8381df7 | |||
55eac3c010 | |||
d93a5ea784 | |||
e41bc2b8ef | |||
052c0d1e78 | |||
1e7b9b3d8d | |||
fbb9a1d67b | |||
126aeee1f7 | |||
0916f1c637 | |||
99d4577d1d | |||
9929c48761 | |||
2e13586ebb | |||
6583b0c15c | |||
038f708b36 | |||
f0f8bc9189 | |||
b45c1410f2 | |||
4907448aa6 | |||
4f08fee7b4 | |||
3624317116 |
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
|
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.idea/
|
||||
dist/
|
||||
release/
|
||||
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" } }]
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk --no-cache add python3 nodejs npm
|
||||
|
||||
RUN npm install -g typescript sass
|
||||
|
||||
COPY [".", "."]
|
||||
|
||||
CMD ["python3", "build.py", "-b", "-c"]
|
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 ByteDream
|
||||
Copyright (c) 2022-NOW ByteDream
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
200
README.md
@ -2,94 +2,170 @@
|
||||
|
||||
A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/ByteDream/stream-bypass/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/ByteDream/stream-bypass?label=Version&style=flat-square" alt="Version">
|
||||
</a>
|
||||
<a href="https://addons.mozilla.org/de/firefox/addon/stream-bypass/">
|
||||
<img src="https://img.shields.io/amo/users/stream-bypass?label=Firefox%20Store%20Downloads&style=flat-square" alt="Firefox Addon Store">
|
||||
</a>
|
||||
<a href="https://addons.mozilla.org/de/firefox/addon/stream-bypass/">
|
||||
<img src="https://img.shields.io/amo/stars/stream-bypass?label=Firefox%20Store%20Stars&style=flat-square" alt="Firefox Addon Stars">
|
||||
</a>
|
||||
<a href="https://github.com/ByteDream/stream-bypass/releases/latest">
|
||||
<img src="https://img.shields.io/github/downloads/ByteDream/stream-bypass/total?label=GitHub%20Downloads&style=flat-square" alt="GitHub Downloads">
|
||||
</a>
|
||||
<a href="https://discord.gg/gUWwekeNNg">
|
||||
<img src="https://img.shields.io/discord/915659846836162561?label=Discord&style=flat-square" alt="Discord">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#-introduction">Introduction 📝</a>
|
||||
•
|
||||
<a href="#-installation">Installation 📥</a>
|
||||
•
|
||||
<a href="#-supported-sites">Supported Sites 📜</a>
|
||||
•
|
||||
<a href="#%EF%B8%8F-building">Building 🛠️</a>
|
||||
•
|
||||
<a href="#%EF%B8%8F-settings">Settings ⚙️</a>
|
||||
•
|
||||
<a href="#-license">License ⚖</a>
|
||||
</p>
|
||||
|
||||
## 📝 Introduction
|
||||
|
||||
This addon replaces the video player from this sides with the native player build-in into the browser or redirects directly to the source video.
|
||||
This has the advantage, that no advertising or popups are shown when trying to interact with the video (playing, skipping, ...) or some sites are showing them even if you do nothing.
|
||||
Additionally this enables you to download the video by right-clicking it and just choose the download option.
|
||||
|
||||
Supported streaming providers (for a complete list of all supported websites, see [here](SUPPORTED)):
|
||||
- [streamtape.com](https://streamtape.com/)
|
||||
- [vidoza.net](https://vidoza.net/)
|
||||
Additionally, this enables you to download the video by right-clicking it and just choose the download option.
|
||||
|
||||
<details id="example">
|
||||
<summary><b>How it's working</b></summary>
|
||||
<summary><b>How it's working:</b></summary>
|
||||
<img src="example.gif" alt="">
|
||||
</details>
|
||||
|
||||
The addon was tested on
|
||||
- Firefox (92.0)
|
||||
- Chromium (92.0)
|
||||
- Opera (78.0)
|
||||
## 📥 Installation
|
||||
|
||||
## Installing
|
||||
### Official browser stores
|
||||
|
||||
### Firefox
|
||||
The best way to install the extension are the official browser extension stores:
|
||||
|
||||
Install the `.xpi` (firefox addon) file from the [latest release](https://github.com/ByteDream/vivosx-source-redirector/releases/latest).
|
||||
- [Firefox Addon Store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/)
|
||||
|
||||
### Chromium / Google Chrome
|
||||
### Manual installation
|
||||
|
||||
1. Download the `stream-bypass-<version>.zip` file from the [latest release](https://github.com/ByteDream/vivosx-source-redirector/releases/latest) and unzip it (with [7zip](https://www.7-zip.org/) or something like that).
|
||||
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.
|
||||
- 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)
|
||||
- Go into your browser and type `about:debugging#/runtime/this-firefox` in the address bar
|
||||
- Click the `Load Temporary Add-on...` button and choose the `manifest.json` file in the unzipped directory
|
||||
- Chromium / Google Chrome
|
||||
> As nearly every browser other than Firefox is based on Chromium, this should be the same for most of them
|
||||
- Download `enhance-crunchyroll-<version>-mv3.zip` from the [latest release](https://github.com/ByteDream/stream-bypass/releases/latest) and unzip it (with [7zip](https://www.7-zip.org/) or something like that)
|
||||
- Go into your browser and type `chrome://extensions` in the address bar
|
||||
- Turn on the developer mode by checking the switch in the top right corner
|
||||
- Click `Load unpacked` and choose the unzipped directory
|
||||
|
||||
### Opera
|
||||
## 📜 Supported sites
|
||||
|
||||
1. Download the `stream-bypass-<version>.zip` file from the [latest release](https://github.com/ByteDream/vivosx-source-redirector/releases/latest) and unzip it (with [7zip](https://www.7-zip.org/) or something like that).
|
||||
2. Go into your browser and type `opera://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.
|
||||
| Site | Supported | Note |
|
||||
| --------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔️ | |
|
||||
| [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)) |
|
||||
| [mixdrop.co](https://mixdrop.co) | ✔ ️ | |
|
||||
| [mp4upload.com](https://mp4upload.com) | ✔ | |
|
||||
| [newgrounds.com](https://newgrounds.com) | ✔ | |
|
||||
| [streamtape.com](https://streamtape.com) | ✔ | |
|
||||
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | |
|
||||
| [upstream.to](https://upstream.to) | ✔ | |
|
||||
| [videovard.sx](https://videovard.sx) | ❌ | Reverse engineering the site costs too much time |
|
||||
| [vidoza.net](https://vidoza.net) | ✔ | |
|
||||
| [vidstream.pro](https://vidstream.pro) | ❌ | Reverse engineering the site costs too much time ([#5](https://github.com/ByteDream/stream-bypass/issues/5)) |
|
||||
| [voe.sx](https://voe.sx) | ✔ | |
|
||||
| [vupload.com](https://vupload.com) | ✔ | |
|
||||
| [kwik.cx](https://kwik.cx) | ✔ | |
|
||||
| [dropload.io](https://dropload.io) | ✔ | |
|
||||
| [supervideo.tv](https://supervideo.tv) | ✔ | |
|
||||
| [goodstream.uno](https://goodstream.uno) | ✔ | |
|
||||
|
||||
## Compiling
|
||||
- ✔️: Everything ok.
|
||||
- ⚠: Included in the addon but will probably not work. See `Note` in this case, an explanation why will stand there in the most cases.
|
||||
- ❌: Not included / supported by the addon. This can have various reasons. See `Note` for an explanation.
|
||||
|
||||
If you want to use / install the addon from source, you have to compile the `typescript` and `sass` files yourself.
|
||||
- Compile it [manual](#manual).
|
||||
- Compile it using [docker](#docker).
|
||||
Some sites put much effort in obfuscating their code / how they receive the video stream so that it simply cost too much time for me to reverse engineer it and find out how to bypass the native video player of the site.
|
||||
|
||||
### Manual
|
||||
<details>
|
||||
<summary>Hall of dead sites</summary>
|
||||
<ul>
|
||||
<li><a href="https://evoload.io">evoload.io</a> - Down</li>
|
||||
<li><a href="https://vidlox.me">vidlox.me</a> - Reachable but empty</li>
|
||||
<li><a href="https://vivo.sx">vivo.sx</a> - Down</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
For compiling everything bare bones, you need [typescript](https://www.typescriptlang.org/) and [sass](https://sass-lang.com/) installed.
|
||||
- Compile typescript
|
||||
## 🛠️ Building
|
||||
|
||||
If you want to build the addon from source and not using the [installation](#installation) way, follow the instructions.
|
||||
|
||||
Requirements:
|
||||
|
||||
- `npm` installed.
|
||||
- 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:
|
||||
|
||||
```shell
|
||||
# install all dependencies
|
||||
$ npm install
|
||||
|
||||
# build the extension source to a build/ directory
|
||||
$ npm run build
|
||||
|
||||
# same as build + create a bundle zipfile at dist/
|
||||
$ npm run bundle
|
||||
```
|
||||
$ tsc -p src
|
||||
```
|
||||
- Compile sass (replace `<path to sass file>` with every `.sass` file in the `src` directory)
|
||||
```
|
||||
$ sass --no-source-map <path to sass file>
|
||||
```
|
||||
The compiled output will be in the `src` directory.
|
||||
|
||||
If you want to keep it a little cleaner, you additionally need [python3](https://www.python.org).
|
||||
- Compile everything with one line
|
||||
```
|
||||
$ python3 build.py -b -c
|
||||
```
|
||||
The compiled output will remain in a (new created if not existing) `build` directory.
|
||||
|
||||
### Docker
|
||||
|
||||
For this, you need [docker](https://www.docker.com/) to be installed.
|
||||
- Build the docker image
|
||||
```
|
||||
$ docker build -t stream-bypass .
|
||||
```
|
||||
- Compile
|
||||
```
|
||||
$ docker rum --rm -v build:/build stream-bypass
|
||||
```
|
||||
The compiled output will remain in a (new created if not existing) `build` directory.
|
||||
|
||||
##### 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 [installing](#installing).
|
||||
When using firefox, use the following
|
||||
If you want to use the addon in Chromium or any browser which is based on it (almost every other, Google Chrome, Opera, ...), follow the steps in [installation](#-installation).
|
||||
When using firefox, use the following:
|
||||
|
||||
1. Type `about:debugging` in the browser's address bar.
|
||||
2. Select 'This Firefox' tab (maybe named different, depending on your language).
|
||||
3. Under `Temporary Extensions`, click `Load Temporary Add-on`.
|
||||
4. Choose any file in the directory where the compiled sources are.
|
||||
|
||||
## License
|
||||
## ⚙️ 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
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.
|
||||
|
105
build.py
@ -1,105 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
|
||||
def load_matches():
|
||||
matched = []
|
||||
|
||||
indexed = False
|
||||
pattern = re.compile(r"(?<=\[')\S*(?=',)")
|
||||
for line in open('src/match.ts', 'r'):
|
||||
if not indexed:
|
||||
if 'constmatches=[' in line.replace(' ', ''):
|
||||
indexed = True
|
||||
else:
|
||||
match = pattern.findall(line)
|
||||
if match:
|
||||
matched.append(match[0])
|
||||
else:
|
||||
break
|
||||
|
||||
return matched
|
||||
|
||||
|
||||
def write_manifest():
|
||||
matches = load_matches()
|
||||
manifest = json.load(open('src/manifest.json', 'r'))
|
||||
|
||||
for content_script in manifest['content_scripts']:
|
||||
content_script['matches'] = [f'*://{match}/*' for match in matches]
|
||||
|
||||
json.dump(manifest, open('src/manifest.json', 'w'), indent=2)
|
||||
|
||||
|
||||
def write_supported():
|
||||
open('SUPPORTED', 'w').writelines([f'{match}\n' for match in load_matches()])
|
||||
|
||||
|
||||
def copy_built():
|
||||
if not shutil.which('tsc'):
|
||||
sys.stderr.write('The typescript compiler `tsc` could not be found')
|
||||
sys.exit(1)
|
||||
elif not shutil.which('sass'):
|
||||
sys.stderr.write('The sass compiler `sass` could not be found')
|
||||
sys.exit(1)
|
||||
|
||||
write_manifest()
|
||||
|
||||
subprocess.call(['tsc', '-p', 'src'])
|
||||
|
||||
build_path = Path('build')
|
||||
if build_path.is_dir():
|
||||
for file in build_path.rglob('*'):
|
||||
if file.is_dir():
|
||||
shutil.rmtree(str(file))
|
||||
else:
|
||||
file.unlink()
|
||||
else:
|
||||
build_path.mkdir()
|
||||
for file in Path('src').rglob('*'):
|
||||
build_file = build_path.joinpath(str(file)[4:])
|
||||
if file.is_dir():
|
||||
build_file.mkdir(parents=True)
|
||||
elif file.suffix == '.sass':
|
||||
css_file = str(file)[:-4] + 'css'
|
||||
subprocess.call(['sass', '--no-source-map', file, css_file])
|
||||
shutil.copy(css_file, str(build_path.joinpath(css_file[4:])))
|
||||
elif file.name == 'tsconfig.json':
|
||||
continue
|
||||
elif file.suffix != '.ts':
|
||||
shutil.copy(str(file), str(build_file))
|
||||
|
||||
|
||||
def clean_build():
|
||||
for file in Path('src').rglob('*'):
|
||||
if file.suffix in ['.js', '.css', '.map']:
|
||||
file.unlink()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-m', '--manifest', action='store_true', help='Builds the manifest.json file for addon information in ./src')
|
||||
parser.add_argument('-s', '--supported', action='store_true', help='Builds the SUPPORTED file with all supported domains in the current directory')
|
||||
parser.add_argument('-b', '--build', action='store_true', help='Creates a ./build folder and builds all typescript / sass files')
|
||||
parser.add_argument('-c', '--clean', action='store_true', help='Cleans the ./src folder from .js, .css and .map files')
|
||||
|
||||
parsed = parser.parse_args()
|
||||
|
||||
if parsed.manifest:
|
||||
write_manifest()
|
||||
if parsed.supported:
|
||||
write_supported()
|
||||
if parsed.build:
|
||||
copy_built()
|
||||
if parsed.clean:
|
||||
clean_build()
|
||||
|
||||
if not parsed.manifest and not parsed.supported and not parsed.build and not parsed.clean:
|
||||
print('\n'.join(load_matches()))
|
7637
package-lock.json
generated
Normal file
52
package.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "stream-bypass",
|
||||
"version": "3.0.0",
|
||||
"displayName": "Stream Bypass",
|
||||
"author": "ByteDream",
|
||||
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"watch": "vite build --watch --mode development --minify false",
|
||||
"dev": "vite",
|
||||
"serve:firefox": "web-ext run --start-url \"about:debugging#/runtime/this-firefox\" --source-dir ./dist/",
|
||||
"serve:chrome": "web-ext run -t chromium --start-url \"https://example.com\" --source-dir ./dist/",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"lint": "prettier --check --plugin prettier-plugin-svelte . && eslint .",
|
||||
"format": "prettier --write --plugin prettier-plugin-svelte .",
|
||||
"release:firefox": "MANIFEST_VERSION=2 vite build --outDir release/firefox",
|
||||
"release:chrome": "MANIFEST_VERSION=3 vite build --outDir release/chrome"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ByteDream/stream-bypass.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ByteDream/stream-bypass/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@samrum/vite-plugin-web-extension": "^5.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^2.1.1",
|
||||
"@tsconfig/svelte": "^4.0.1",
|
||||
"@types/chrome": "^0.0.228",
|
||||
"@types/webextension-polyfill": "^0.10.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-svelte": "^2.35.0",
|
||||
"hls.js": "^1.4.12",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-svelte": "^3.1.0",
|
||||
"sass": "^1.69.5",
|
||||
"svelte": "^3.58.0",
|
||||
"svelte-check": "^3.2.0",
|
||||
"svelte-preprocess": "^5.0.3",
|
||||
"tslib": "^2.5.0",
|
||||
"typescript": "^5.0.4",
|
||||
"vite": "~4.3.3",
|
||||
"web-ext": "^7.6.2",
|
||||
"webextension-polyfill": "^0.10.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 |
24
src/entries/background/main.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { Match } from '~/lib/match';
|
||||
import { storageDelete, storageGet, storageSet } from '~/lib/settings';
|
||||
import { getMatch } from '~/lib/match';
|
||||
|
||||
chrome.runtime.onMessage.addListener(async (message) => {
|
||||
if (message.action == 'ff2mpv') {
|
||||
await chrome.runtime.sendNativeMessage('ff2mpv', { url: message.url });
|
||||
}
|
||||
});
|
||||
|
||||
chrome.webRequest.onBeforeRedirect.addListener(
|
||||
async (details) => {
|
||||
// check if redirects origins from a previous redirect
|
||||
if ((await storageGet('redirect')) === undefined) {
|
||||
let match: Match;
|
||||
if ((match = await getMatch(new URL(details.url).hostname)) !== undefined) {
|
||||
await storageSet('redirect', match.id);
|
||||
}
|
||||
} else {
|
||||
await storageDelete('redirect');
|
||||
}
|
||||
},
|
||||
{ urls: ['<all_urls>'], types: ['main_frame', 'sub_frame'] }
|
||||
);
|
51
src/entries/contentScript/main.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import type { Match } from '~/lib/match';
|
||||
import { getMatch } from '~/lib/match';
|
||||
import { Other, Redirect } from '~/lib/settings';
|
||||
import browser from 'webextension-polyfill';
|
||||
|
||||
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 browser.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(
|
||||
browser.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>
|
39
src/entries/player/player.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { matches } from '~/lib/match';
|
||||
import Hls from 'hls.js';
|
||||
|
||||
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})`;
|
||||
|
||||
if (new URL(url).pathname.endsWith('.m3u8')) {
|
||||
await playHls(url, videoElem);
|
||||
} else {
|
||||
await playNative(url, videoElem);
|
||||
}
|
||||
}
|
218
src/entries/popup/Popup.svelte
Normal file
@ -0,0 +1,218 @@
|
||||
<script lang="ts">
|
||||
import { matches, Reliability } 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
|
||||
class:reliability-low={hoster.reliability === Reliability.LOW}
|
||||
class:reliability-normal={hoster.reliability === Reliability.NORMAL}
|
||||
class:reliability-high={hoster.reliability === Reliability.HIGH}
|
||||
>
|
||||
{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;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
.reliability-low {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: red;
|
||||
}
|
||||
.reliability-normal {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: yellow;
|
||||
}
|
||||
.reliability-high {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #00ff00;
|
||||
}
|
||||
</style>
|
16
src/entries/popup/index.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Stream Bypass</title>
|
||||
</head>
|
||||
<body style="width: fit-content; height: 500px">
|
||||
<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')
|
||||
});
|
Before Width: | Height: | Size: 16 KiB |
24
src/index.ts
@ -1,24 +0,0 @@
|
||||
// @ts-ignore
|
||||
chrome.storage.local.get(['all', 'disabled'], function (result) {
|
||||
let keys = Object.keys(result)
|
||||
if (keys.indexOf('all') !== -1 && !result['all']) {
|
||||
return
|
||||
}
|
||||
// @ts-ignore
|
||||
for (let match of matches) {
|
||||
if (window.location.href.indexOf(match[0]) !== -1) {
|
||||
if (keys.indexOf('disabled') === -1 || result['disabled'].indexOf(match[0]) === -1) {
|
||||
let regex = match[1] as RegExp
|
||||
let matchClass = match[2] as Match
|
||||
|
||||
let re
|
||||
if (regex === null) {
|
||||
location.assign(matchClass === null ? document.body.innerHTML : matchClass.match(new RegExp('').exec(document.body.innerHTML)))
|
||||
} else if ((re = regex.exec(document.body.innerHTML)) !== null) {
|
||||
location.assign(matchClass === null ? re[0] : matchClass.match(re))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
285
src/lib/match.ts
Normal file
@ -0,0 +1,285 @@
|
||||
import { unpack } from '~/lib/utils';
|
||||
import { Hosters } from '~/lib/settings';
|
||||
|
||||
export enum Reliability {
|
||||
HIGH,
|
||||
NORMAL,
|
||||
LOW
|
||||
}
|
||||
|
||||
export interface Match {
|
||||
name: string;
|
||||
id: string;
|
||||
reliability: Reliability;
|
||||
domains: string[];
|
||||
replace?: boolean;
|
||||
regex: RegExp;
|
||||
notice?: string;
|
||||
|
||||
match(match: RegExpMatchArray): Promise<string>;
|
||||
}
|
||||
|
||||
export const Doodstream: Match = {
|
||||
name: 'Doodstream',
|
||||
id: 'doodstream',
|
||||
reliability: Reliability.NORMAL,
|
||||
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',
|
||||
'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',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.NORMAL,
|
||||
domains: ['goodstream.uno'],
|
||||
regex: /(?<=file:\s+").*(?=")/g,
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Kwik: Match = {
|
||||
name: 'Kwik',
|
||||
id: 'kwik',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.LOW,
|
||||
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',
|
||||
reliability: Reliability.HIGH,
|
||||
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',
|
||||
reliability: Reliability.HIGH,
|
||||
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 Vidoza: Match = {
|
||||
name: 'Vidoza',
|
||||
id: 'vidoza',
|
||||
reliability: Reliability.HIGH,
|
||||
domains: ['vidoza.net'],
|
||||
regex: /(?<=src:\s?").+?(?=")/gm,
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Voe: Match = {
|
||||
name: 'Voe',
|
||||
id: 'voe',
|
||||
reliability: Reliability.HIGH,
|
||||
domains: ['voe.sx'],
|
||||
regex: /https?:\/\/\S*m3u8.+(?=')/gm,
|
||||
|
||||
match: async (match: RegExpMatchArray) => {
|
||||
return match[0];
|
||||
}
|
||||
};
|
||||
|
||||
export const Vupload: Match = {
|
||||
name: 'Vupload',
|
||||
id: 'vupload',
|
||||
reliability: Reliability.HIGH,
|
||||
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,
|
||||
[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;
|
||||
}
|
73
src/lib/settings.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import browser from 'webextension-polyfill';
|
||||
import type { Match } from '~/lib/match';
|
||||
import { matches } from '~/lib/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> {
|
||||
const entry = await browser.storage.local.get(key);
|
||||
const value = entry[key];
|
||||
return value === undefined ? defaultValue : value;
|
||||
}
|
||||
|
||||
export async function storageSet<T>(key: string, value: T) {
|
||||
const obj = {
|
||||
[key]: value
|
||||
};
|
||||
await browser.storage.local.set(obj);
|
||||
}
|
||||
|
||||
export async function storageDelete(key: string) {
|
||||
await browser.storage.local.remove(key);
|
||||
}
|
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,34 +0,0 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Stream Bypass",
|
||||
"author": "ByteDream",
|
||||
"description": "",
|
||||
"version": "1.0.0",
|
||||
"homepage_url": "https://github.com/ByteDream/stream-bypass",
|
||||
"content_scripts": [
|
||||
{
|
||||
"all_frames": true,
|
||||
"matches": [
|
||||
"*://streamtape.com/*",
|
||||
"*://vidoza.net/*",
|
||||
"*://vivo.sx/*",
|
||||
"*://vupload.com/*"
|
||||
],
|
||||
"js": [
|
||||
"match.js",
|
||||
"index.js"
|
||||
]
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
"storage"
|
||||
],
|
||||
"browser_action": {
|
||||
"default_icon": "icons/stream-bypass.png",
|
||||
"default_title": "Stream Bypass",
|
||||
"default_popup": "popup/popup.html"
|
||||
},
|
||||
"icons": {
|
||||
"128": "icons/stream-bypass.png"
|
||||
}
|
||||
}
|
80
src/manifest.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import pkg from '../package.json';
|
||||
|
||||
const sharedManifest: Partial<chrome.runtime.ManifestBase> = {
|
||||
browser_specific_settings: {
|
||||
gecko: {
|
||||
id: '{55dd42e8-3dd9-455a-b4fe-86664881b10c}'
|
||||
}
|
||||
},
|
||||
content_scripts: [
|
||||
{
|
||||
all_frames: true,
|
||||
matches: ['<all_urls>'],
|
||||
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', 'webRequest', 'nativeMessaging', '<all_urls>']
|
||||
};
|
||||
|
||||
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/main.ts'],
|
||||
persistent: true
|
||||
},
|
||||
browser_action: browserAction,
|
||||
permissions: [...sharedManifest.permissions]
|
||||
};
|
||||
|
||||
const ManifestV3 = {
|
||||
...sharedManifest,
|
||||
action: browserAction,
|
||||
background: {
|
||||
service_worker: 'src/entries/background/main.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,
|
||||
...ManifestV3,
|
||||
manifest_version: manifestVersion
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Missing manifest definition for manifestVersion ${manifestVersion}`);
|
||||
}
|
43
src/match.ts
@ -1,43 +0,0 @@
|
||||
interface Match {
|
||||
match(match: RegExpMatchArray): string
|
||||
}
|
||||
|
||||
class Streamtape implements Match {
|
||||
match(match: RegExpMatchArray): string {
|
||||
return `https://streamtape.com/get_video?${match[0]}`
|
||||
}
|
||||
}
|
||||
|
||||
class Vivo implements Match {
|
||||
match(match: RegExpMatchArray): string {
|
||||
return this.rot47(decodeURIComponent(match[1]))
|
||||
}
|
||||
|
||||
// decrypts a string with the rot47 algorithm (https://en.wikipedia.org/wiki/ROT13#Variants)
|
||||
rot47(encoded: string): string {
|
||||
const s = []
|
||||
for(let i = 0; i < encoded.length; i++) {
|
||||
const j = encoded.charCodeAt(i)
|
||||
if((j >= 33) && (j <= 126)) {
|
||||
s[i] = String.fromCharCode(33+((j+ 14)%94))
|
||||
} else {
|
||||
s[i] = String.fromCharCode(j)
|
||||
}
|
||||
}
|
||||
return s.join('')
|
||||
}
|
||||
}
|
||||
|
||||
class Vupload implements Match {
|
||||
match(match: RegExpMatchArray): string {
|
||||
return `https://www3.megaupload.to/${match[0]}/v.mp4`
|
||||
}
|
||||
}
|
||||
|
||||
// every match HAS to be on an separate line
|
||||
const matches = [
|
||||
['streamtape.com', new RegExp(/id=\S*(?=')/gm), new Streamtape()],
|
||||
['vidoza.net', new RegExp(/(?<=src:(\s*)?")\S*(?=")/gm), null],
|
||||
['vivo.sx', new RegExp(/source:\s*'(\S+)'/gm), new Vivo()],
|
||||
['vupload.com', new RegExp(/(?<=class\|)\w*/gm), new Vupload()]
|
||||
]
|
@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<link rel="stylesheet" href="popup.css">
|
||||
<script src="../match.js"></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>
|
||||
</div>
|
||||
<script src="./popup.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,35 +0,0 @@
|
||||
body
|
||||
background-color: #2b2a33
|
||||
font-weight: bold
|
||||
max-height: 500px
|
||||
|
||||
|
||||
a, p
|
||||
color: white
|
||||
font-size: 16px
|
||||
margin: 5px 0
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
hr
|
||||
margin: 3px 0
|
||||
|
||||
|
||||
#all
|
||||
display: flex
|
||||
justify-content: center
|
||||
margin: 10px 0
|
@ -1,103 +0,0 @@
|
||||
function enableAll(enable: boolean) {
|
||||
// @ts-ignore
|
||||
chrome.storage.local.set({'all': enable})
|
||||
|
||||
// @ts-ignore
|
||||
for (let button of document.getElementById('sub-container').getElementsByTagName('a')) {
|
||||
enable ? button.classList.remove('disabled') : button.classList.add('disabled')
|
||||
}
|
||||
}
|
||||
|
||||
function enableOne(website: string, enable: boolean) {
|
||||
// @ts-ignore
|
||||
chrome.storage.local.get(['disabled'], function (result) {
|
||||
let disabled: string[] = Object.keys(result).length === 0 ? [] : result['disabled']
|
||||
if (enable && disabled.indexOf(website) !== -1) {
|
||||
disabled.splice(disabled.indexOf(website), 1)
|
||||
} else if (!enable && disabled.indexOf(website) === -1) {
|
||||
disabled.push(website)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
chrome.storage.local.set({'disabled': disabled})
|
||||
})
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
chrome.storage.local.get(['all', 'disabled'], function (result) {
|
||||
let allDisabled = result['all'] !== undefined && !result['all']
|
||||
let disabled = new Map()
|
||||
|
||||
if (allDisabled) {
|
||||
// @ts-ignore
|
||||
for (let match of matches) {
|
||||
disabled.set(match[0], false)
|
||||
}
|
||||
} else {
|
||||
if (Object.keys(result).indexOf('disabled') !== -1) {
|
||||
for (let disable of result['disabled']) {
|
||||
disabled.set(disable, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let subContainer = document.getElementById('sub-container')
|
||||
// @ts-ignore
|
||||
for (let match of matches) {
|
||||
let row = document.createElement('tr')
|
||||
|
||||
let name = document.createElement('td')
|
||||
let nameValue = document.createElement('p')
|
||||
nameValue.innerText = match[0]
|
||||
let buttons = document.createElement('td')
|
||||
buttons.classList.add('buttons')
|
||||
let on = document.createElement('a')
|
||||
on.innerText = 'On'
|
||||
let off = document.createElement('a')
|
||||
off.innerText = 'Off'
|
||||
disabled.has(match[0]) ? off.classList.add('active') : on.classList.add('active')
|
||||
if (allDisabled) {
|
||||
on.classList.add('disabled')
|
||||
off.classList.add('disabled')
|
||||
}
|
||||
|
||||
on.onclick = function () {
|
||||
if (!on.classList.contains('disabled')) {
|
||||
enableOne(match[0], true)
|
||||
on.classList.add('active')
|
||||
off.classList.remove('active')
|
||||
}
|
||||
}
|
||||
off.onclick = function () {
|
||||
if (!off.classList.contains('disabled')) {
|
||||
enableOne(match[0], false)
|
||||
on.classList.remove('active')
|
||||
off.classList.add('active')
|
||||
}
|
||||
}
|
||||
|
||||
name.append(nameValue)
|
||||
buttons.append(on, off)
|
||||
row.append(name, buttons)
|
||||
subContainer.append(row)
|
||||
}
|
||||
|
||||
let allButtons = document.getElementById('all').getElementsByTagName('a')
|
||||
allButtons[0].onclick = function () {
|
||||
if (!allButtons[0].classList.contains('disabled')) {
|
||||
enableAll(true)
|
||||
allButtons[0].classList.add('active')
|
||||
allButtons[1].classList.remove('active')
|
||||
}
|
||||
}
|
||||
allButtons[1].onclick = function () {
|
||||
if (!allButtons[1].classList.contains('disabled')) {
|
||||
enableAll(false)
|
||||
allButtons[0].classList.remove('active')
|
||||
allButtons[1].classList.add('active')
|
||||
}
|
||||
}
|
||||
allDisabled ? allButtons[1].classList.add('active') : allButtons[0].classList.add('active')
|
||||
})
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es5",
|
||||
"scripthost",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
}
|
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()
|
||||
};
|
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"]
|
||||
}
|
35
vite.config.ts
Normal file
@ -0,0 +1,35 @@
|
||||
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';
|
||||
|
||||
// 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: ['<all_urls>'],
|
||||
excludeEntryFile: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': path.resolve(__dirname, './src')
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|