217 Commits
v1.0.0 ... main

Author SHA1 Message Date
f9a0197f7f update readme 2025-11-08 23:24:25 +01:00
d8bb70890a update build instructions 2025-11-08 22:26:18 +01:00
45900b2e86 do not upload sources to releases 2025-11-08 22:21:10 +01:00
3058847f31 upload artifacts when publishing 2025-11-08 22:20:35 +01:00
082f30722c do not lint on tag push 2025-11-08 21:52:46 +01:00
39a6fffd50 update version 2025-11-08 20:00:37 +01:00
6109cd5543 fix vidmoly 2025-11-08 19:57:36 +01:00
de38196a77 fix player not accessible when using mv3 2025-11-08 19:56:46 +01:00
d2b6086155 fix player background color 2025-11-08 19:55:54 +01:00
66c638425d fix publish action 2025-11-08 18:38:37 +01:00
b2da03f9a1 fix chrome build artifact 2025-11-08 18:33:19 +01:00
76a4217aab fix readme formatting 2025-11-08 18:29:30 +01:00
cac464cc65 update actions 2025-11-08 18:29:30 +01:00
7bd8f01a1b update readme 2025-11-08 18:29:30 +01:00
b7c5e020b6 update prettier ignored files list 2025-11-08 18:29:30 +01:00
fdd1ca350e update 2025-11-08 18:29:28 +01:00
607326e6d6 Update dependencies and version 2025-05-08 02:02:57 +02:00
47f6de2de8 Fix voe 2025-05-08 01:59:29 +02:00
d159fb0aa4 Add mixdrop domain 2025-05-06 22:21:09 +02:00
290733a85a Add supervideo domain 2025-05-06 22:20:04 +02:00
84eb32ab61 Fix dropload domain 2025-05-06 22:18:19 +02:00
927a4e3ae3 Update dependencies and version 2025-04-17 02:43:13 +02:00
5926177920 Update voe.sx match regex 2025-04-17 02:37:32 +02:00
9a29316641 Fix voe.sx 2025-04-17 02:26:59 +02:00
c300125f64 Set javascript target to es2021 2025-04-17 02:26:41 +02:00
59f2ffec57 Allow arbitrary properties in Match interface 2025-04-17 02:10:18 +02:00
6dff691c25 Use other notation for match function 2025-04-17 02:04:45 +02:00
c4085f9ac8 Update version 2025-04-04 02:13:20 +02:00
422702b16f Add import order prettier plugin 2025-04-04 02:10:51 +02:00
c3dad3681c Fix player media type url query 2025-04-04 02:05:04 +02:00
6eb3259ec5 Remove duplicate supported websites 2025-04-04 01:11:41 +02:00
68e445a738 Update supported websites 2025-04-04 00:58:14 +02:00
e864bc6100 Update dependencies and version 2025-04-04 00:45:03 +02:00
3ac73ac3b4 Update vite config 2025-04-04 00:44:51 +02:00
6aaf960c28 Holy fuck why does vite have a problem with this 2025-04-04 00:43:57 +02:00
8a13dea681 Format 2025-04-04 00:01:21 +02:00
8d575241fe Fix lastPathSegment util 2025-04-04 00:00:38 +02:00
50f400b8b9 Use enum to specify media type 2025-04-03 23:56:18 +02:00
f6fcfd354a Add more utils 2025-04-03 23:48:53 +02:00
c081127d35 Add loadx.ws 2025-04-03 23:43:49 +02:00
416fceba88 Update match function to manually specify media type 2025-04-03 23:43:39 +02:00
a9cf03c176 Fix luluvdo.com 2025-04-03 23:14:31 +02:00
8852318483 Update voe.sx to catch every redirect url 2025-04-03 22:55:24 +02:00
7067aaf4a0 Add luluvdo.com to supported websites table 2025-03-14 18:52:08 +01:00
bf723e2ed6 Update dependencies and version 2025-03-14 18:41:05 +01:00
9c80362a1c Update ci actions 2025-03-14 18:31:29 +01:00
ae738ff32f Add luluvdo.com site 2025-03-14 18:20:08 +01:00
16d4f2956a Add dood.work domain (#23) 2025-03-14 17:43:50 +01:00
bytedream
f6c6102436 Merge pull request #24 from muleyo/main
Added Voe.sx Domain
2025-03-13 16:08:39 +01:00
muleyo
f1525817b1 Added Voe.sx Domain
A domain for Voe.sx was missing
2025-03-10 01:42:23 +01:00
1e1d8cdfa6 Fix tick character 2024-12-19 22:50:59 +01:00
47eed2d12b Update supported websites 2024-12-15 19:08:50 +01:00
67e03eafaa Update dependencies and version 2024-12-15 18:43:06 +01:00
db0ccd9b56 Fix vidoza.net 2024-12-15 18:05:25 +01:00
fd3eda9c61 Fix vidmoly.to 2024-12-15 17:31:13 +01:00
e522916585 Fix filemoon.to 2024-12-15 17:14:46 +01:00
b9514284da Fix voe.sx 2024-12-15 16:16:38 +01:00
06beaa6ebe Update README 2024-07-29 02:35:43 +02:00
6a56c37dd2 Update package-lock.json 2024-07-29 02:31:30 +02:00
018b381732 Update README 2024-07-29 00:00:57 +02:00
696ec22471 Update dependencies and version 2024-07-28 23:43:56 +02:00
e379113aa4 Add firefox android support 2024-07-28 23:33:54 +02:00
84605ceb30 Make nativeMessaging an optional permission 2024-07-28 23:31:25 +02:00
67c492db47 Update popup UI 2024-07-28 23:04:04 +02:00
ae2f196b06 Lint 2024-07-28 18:08:05 +02:00
298e9308ce Add streama2z.com (#19) 2024-07-28 17:54:41 +02:00
3cd4c6b6b7 Fix doodstream.com 2024-07-28 17:54:02 +02:00
6286acf85c Make video replace strategy more aggressive 2024-07-28 17:33:54 +02:00
94eee79218 Fix voe.sx 2024-07-28 16:35:58 +02:00
2f186bda07 Add d000d.com (#18) 2024-07-28 14:46:36 +02:00
1c9f95cebc Fix linting errors 2024-07-14 21:08:47 +02:00
c57cd03407 Fix vidmoly & add it to README 2024-05-20 15:40:38 +02:00
64cf565da3 Update dependencies and version 2024-05-20 15:37:46 +02:00
bf8a7eb602 Remove reliability 2024-05-20 14:11:09 +02:00
c643a39e2d Add new doodstream domain 2024-04-24 01:18:01 +02:00
fcfbc41fdb Fix voe.sx 2024-04-24 00:46:32 +02:00
1251f079f5 Remove discord link 2023-12-10 19:51:35 +01:00
ccfc77167b Add vidmoly.me 2023-12-08 15:50:19 +01:00
a10066458f Add chrome web store link 2023-11-20 12:20:32 +01:00
6a8c705b06 Fix mv2 chrome namespace apis 2023-11-17 16:25:40 +01:00
76a5bac7fd Fix chromium popup height 2023-11-17 16:25:29 +01:00
3aca863a4b Fix invalid release name 2023-11-17 15:06:17 +01:00
fe14edc0ef Make mv3 compatible 2023-11-17 15:06:09 +01:00
fffe23638c Update build instructions 2023-11-11 22:05:39 +01:00
5214228a72 Add linting and build pipeline 2023-11-11 21:48:53 +01:00
7000d5a08b Lint 2023-11-11 21:48:44 +01:00
a21f799e7d Update install instructions 2023-11-11 21:35:24 +01:00
1fb85313dd Use more reliable streamtape match function 2023-11-11 21:31:23 +01:00
6e7410b088 Rewrite 2023-11-11 21:31:20 +01:00
Bharathi
0054f5da0e Update few domains (#15)
* Update matches.ts

* Update matches.ts

* Update matches.ts
2023-08-08 18:58:50 +02:00
48dc192964 Update version 2023-08-08 14:25:48 +02:00
sdaqo
7c45cacd36 add ff2mpv setting + new hosters (#14)
* add Kwik, use unpacker to improve reliabilty

* use packer for filemoon

* use packer for upstream

* add ff2mpv setting + functionality

* Update README.md

* get rid of @types/webextension-polyfill

* Revert "Update README.md"

This reverts commit affb600096.

* Update README.md

* Update info link for ff2mpv

* add kwik to hosters in readme

* removes console.logs

* Delete package-lock.json

* add package-lock.json to .gitignore

* unpack without using eval

* Merge main branch into here

* Add Dropload Hoster

* Add Supervideo Hoster

* Add GoodStream Hoster

* Add hosters to readme and delete console.logs

* Delete package-lock.json

* Fix ff2mpv info url

* Update readme

---------

Co-authored-by: bytedream <bytedream@protonmail.com>
2023-08-08 13:50:18 +02:00
00514e4e81 Update license year 2023-04-29 00:13:39 +02:00
4c1d0d0b63 Update dependencies 2023-04-29 00:12:00 +02:00
sdaqo
a920832945 Add Kwik and improve reliabilty for several other hosters (#12)
* add Kwik, use unpacker to improve reliabilty

* use packer for filemoon

* use packer for upstream

* Update README.md

* Revert "Update README.md"

This reverts commit affb600096.

* add kwik to hosters in readme

* unpack without using eval
2023-04-28 18:19:01 +02:00
ByteDream
7d8d8b6614 Merge pull request #9 from bachig26/master
Update matches.ts
2023-01-19 13:27:04 +01:00
Bharathi
b4052834f2 Update matches.ts
- add dood.re
2023-01-19 05:02:40 +01:00
ByteDream
9900c33243 Merge pull request #7 from bachig26/patch-1
added more domains
2023-01-11 08:10:40 +01:00
Bharathi
98709bc934 added more domains
- additional domains to mixdrop and doodstream
2023-01-11 01:07:48 +01:00
f4cbdd3258 Version 2.1.7 2022-11-10 16:33:48 +01:00
fa41a8de8e Fix voe regex 2022-10-24 23:10:27 +02:00
ce8bc855b9 Update dead sites 2022-10-23 16:56:05 +02:00
03202b2a12 Fix issues link 2022-10-01 12:10:19 +02:00
9f0e1b59ce Update version to 2.1.6 2022-09-30 20:30:42 +02:00
d928d25e09 Update supported websites 2022-09-30 20:29:15 +02:00
266771aa13 Update issues url 2022-09-30 20:26:43 +02:00
d56672d90f Fix mp4upload 2022-09-30 16:02:00 +02:00
a9ea5fe4b2 Add filemoon.sx 2022-09-30 14:59:42 +02:00
bb3f5384d6 Fix url media matching 2022-09-30 14:57:23 +02:00
6989587161 Remove error message when video is not loaded properly 2022-09-12 22:01:50 +02:00
dd9bf71a5c Update dependency versions and bump version to 2.1.5 2022-09-10 18:15:53 +02:00
6da0050df4 Fix false-positive error message 2022-09-10 18:10:48 +02:00
1a7c22ec0e Move from sass to scss for stylesheets 2022-09-10 17:37:12 +02:00
175862b098 Enable error message on non hls streams 2022-08-23 22:07:58 +02:00
fd5a532d0f Change streamzz reliability to low 2022-08-23 21:53:38 +02:00
8c43eedb23 Change doodstream reliability to normal 2022-08-23 21:43:24 +02:00
e027c2e09e Version 2.1.4 2022-08-04 17:06:10 +02:00
f9a0656d4d Optimize redirect functionality 2022-08-04 17:05:50 +02:00
382d8b1268 Add dood.wf as domain for doodstream 2022-08-04 17:03:03 +02:00
5b8639ce6a web development :))))))))))))))))))))))))))) 2022-07-23 17:40:16 +02:00
841c824590 Fix native player error message 2022-07-23 17:28:47 +02:00
2055a3ea81 Update version to 2.1.2 2022-07-23 17:19:31 +02:00
0262d1853c Change error message layout and link color when video cannot be loaded 2022-07-22 13:55:28 +02:00
81da6600e6 Fix error message if video cannot be loaded 2022-07-22 13:55:03 +02:00
817f5b82f9 Remove unused import 2022-07-22 01:49:54 +02:00
9a17fb0d9b Fix domain sometimes not shown in window title when using native player 2022-07-22 01:41:50 +02:00
17f8aab216 Fix mixdrop not working 2022-07-22 01:34:34 +02:00
44d4c9cbcf Update supported websites 2022-07-20 11:56:08 +02:00
b34531b982 Bump version to 2.1.1 2022-07-20 11:55:57 +02:00
e699d3885c Change mp4upload reliability 2022-07-19 11:53:14 +02:00
396038a803 Fix replace video player height 2022-07-19 11:28:11 +02:00
bd64d4ed0b Add doodstream domain 2022-07-19 11:27:03 +02:00
a207c336b0 Add mp4upload support 2022-07-19 11:09:09 +02:00
2460657f2a Remove doodstream comment 2022-07-19 11:06:56 +02:00
698ed5ac3c Add support for doodstream 2022-07-19 11:03:16 +02:00
dc42220f09 Fix streamtape 2022-07-19 10:07:21 +02:00
e146649bbf Use window.location instead of location 2022-07-16 02:34:22 +02:00
424e34190c Rework title of built-in player 2022-07-16 02:32:23 +02:00
c5f4f8b246 Version to 2.1.0 2022-07-13 18:39:26 +02:00
e2b8d884af Fix streamtape regex 2022-07-13 18:21:03 +02:00
b07d0b4be6 Update supported sites table 2022-07-13 18:04:55 +02:00
2d0441997c Add doodstream stubs 2022-07-13 18:04:44 +02:00
53e040db46 Fix addon cannot be disabled via ui 2022-07-13 01:00:33 +02:00
6d1ff3fbea Fix player not working in non firefox browsers 2022-07-13 00:46:40 +02:00
4cf76eb62a Add functions to bypass websites that are redirected to (looking at you voe) 2022-07-13 00:45:52 +02:00
672b920f31 Rename match.ts to matches.ts 2022-07-12 22:51:37 +02:00
1e166b5ecc Add new voe domain (un-block-voe.net) 2022-07-04 12:27:14 +02:00
213b996755 Add new voe domain (voeun-block.net) 2022-06-26 14:08:56 +02:00
ByteDream
de0d8d5a41 Fix typo 2022-06-20 16:22:34 +02:00
ByteDream
1f34f74e11 Fix typo 2022-06-20 10:49:34 +02:00
ByteDream
60a2247b02 Merge pull request #2 from ByteDream/v2
Update to v2
2022-06-20 10:45:23 +02:00
88814fcc83 Fck I've deleted the wrong file 2022-06-20 10:39:55 +02:00
f908e8249b Remove background script from build file 2022-06-18 02:37:05 +02:00
8d57dfbc57 Update copyright year 2022-06-18 02:33:04 +02:00
3aa84ba709 Change Firefox Addon ... names to Firefox Store ... 2022-06-18 02:31:12 +02:00
9ecfbd44e9 Remove background script OwO forgot that 2022-06-18 02:22:38 +02:00
b1bea7687c Change reliability for vidlox 2022-06-18 02:21:34 +02:00
c3148a76a3 Fix typo 2022-06-18 01:50:48 +02:00
210aa5c263 Update supported websites 2022-06-18 01:40:39 +02:00
a55c455fcc Refactor, complete past changes & change manifest back to version 2 2022-06-18 01:29:47 +02:00
923787bedb Update icons 2022-06-18 01:29:19 +02:00
8990e25a72 Redirect to custom video player on every url 2022-06-18 01:13:54 +02:00
9b4bcc6c64 Update to manifest version 3 2022-06-17 00:23:29 +02:00
2e8cce10b5 Add streamz.ws 2022-06-16 23:43:48 +02:00
9d4b52c107 Add vidoza 2022-06-16 23:42:43 +02:00
806320a0de Fix streamzz and remove vidoza 2022-06-16 22:28:28 +02:00
774d3b52d7 Update readme 2022-06-16 19:47:11 +02:00
31787bb3e3 Use web-ext instead of custom bundle & add code check 2022-06-16 19:44:24 +02:00
e64feb5130 Remove readme build 2022-06-16 19:23:30 +02:00
aa51b5d95f Remove old and not working websites 2022-06-16 19:23:13 +02:00
27c03c0b5b Center hls error message 2022-06-16 19:17:44 +02:00
f48adc3db3 Exclude comment matches when matching domain matches 2022-06-16 16:09:35 +02:00
feaf843584 Fix package 2022-06-15 10:53:06 +02:00
0b6021cf52 FIx popup 2022-06-15 10:52:50 +02:00
8b375cae2c Fix build script 2022-06-15 10:52:20 +02:00
c9acb929cc Add readme build 2022-06-15 09:13:45 +02:00
7a69ac83e0 I hate nodejs. 2022-06-14 23:39:26 +02:00
a9a8609cb8 Lots of updates :o 2022-06-14 20:50:51 +02:00
e0d4f8747e Update .gitignore 2022-06-12 17:36:11 +02:00
f8213772c8 Fixed error report message 2022-03-04 20:59:34 +01:00
ByteDream
bce27f2beb Updated firefox download notice :)))) 2022-02-04 16:10:06 +01:00
7bfae612f9 Updated version 2022-02-04 15:46:44 +01:00
106f4ffda3 Re-added content_security_policy to fix hls playing 2022-02-04 15:40:16 +01:00
b83d287a42 Added newgrounds.com 2022-02-04 15:40:00 +01:00
a39418e506 Added prefix wildcards to url matching 2022-02-04 15:39:22 +01:00
0fc06a78a7 Updated version 2022-02-02 17:42:34 +01:00
90bed8b774 Removed content_security_policy 2022-02-02 09:50:07 +01:00
ac31945372 Removed upstream.com 2022-02-01 22:07:51 +01:00
1683bf2035 Fixed readme build 2022-02-01 22:06:44 +01:00
af54a28737 Hls error style changes & typo fixes 2022-02-01 21:57:12 +01:00
0f8d323c3c Added comment passing on extracting matching sites 2022-02-01 21:55:55 +01:00
4c1bfbde62 Updated vupload.com regex (mp4 => hls) 2022-02-01 21:00:20 +01:00
9889cf79a2 Added badges 2022-02-01 20:49:13 +01:00
230eb074e9 Added gitignore 2022-02-01 20:36:31 +01:00
ByteDream
2a9c00c3a8 Typo fix 2022-01-10 16:10:48 +01:00
ByteDream
16cf6688ec Updated direct download links using smartrelease :3 2022-01-10 15:16:49 +01:00
4edc3d5f84 Update v1.4.2 2022-01-06 17:32:44 +01:00
e5552910b2 Hls player works now; fixed vupload.com regex 2022-01-06 17:32:30 +01:00
2db8381df7 Added link to issues 2022-01-06 17:30:47 +01:00
ByteDream
55eac3c010 Firefox addon store link reactivation 2021-11-23 12:30:24 +01:00
d93a5ea784 Static 3rd party library version 2021-11-18 20:34:22 +01:00
ByteDream
e41bc2b8ef Update README.md 2021-11-18 17:18:21 +01:00
052c0d1e78 firefox addon fix 2021-11-06 13:40:06 +01:00
1e7b9b3d8d Fixed empty supported website 2021-10-23 23:07:27 +02:00
fbb9a1d67b Added website support completion for README.md 2021-10-23 23:03:30 +02:00
126aeee1f7 Added automatically browser recognizing for README.md manipulation 2021-10-23 22:20:56 +02:00
0916f1c637 2 new websites + indicator for redirect reliability 2021-10-23 17:02:09 +02:00
99d4577d1d Removed deprecated comment 2021-10-23 00:15:26 +02:00
9929c48761 Added native m3u8 / hls streaming 2021-10-23 00:13:53 +02:00
2e13586ebb Added mcloud.to 2021-10-22 23:40:21 +02:00
6583b0c15c Updated version 2021-09-13 17:14:30 +02:00
038f708b36 Fixed vivo.sx / vivo.st regex and unwanted return 2021-09-13 17:13:49 +02:00
f0f8bc9189 Added id 2021-09-13 01:57:41 +02:00
b45c1410f2 Added 7 new sites and general refactoring 2021-09-13 01:36:00 +02:00
4907448aa6 Add overflow 2021-09-13 01:14:00 +02:00
ByteDream
4f08fee7b4 Typo fix 2021-09-02 09:38:25 +02:00
ByteDream
3624317116 Update README.md 2021-09-01 22:18:20 +02:00
81 changed files with 10360 additions and 456 deletions

40
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: build
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build Firefox (mv2)
run: npm run build:firefox
- name: Build Chrome (mv3)
run: npm run build
- name: Upload Firefox (mv2)
uses: actions/upload-artifact@v5
with:
name: stream-bypass-mv2
path: .output/firefox-mv2
- name: Upload Chrome (mv3)
uses: actions/upload-artifact@v5
with:
name: stream-bypass-mv3
path: .output/chrome-mv3

28
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: lint
on:
push:
branches:
- '**'
tags-ignore:
- '*'
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint

85
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,85 @@
name: publish
on:
push:
tags:
- v*
workflow_dispatch:
inputs:
dry_run:
type: boolean
description: Dry run
default: true
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Zip extensions
run: |
npm run zip:firefox
npm run zip
- name: Resolve zip extension paths
id: paths
run: |
MV2_PATH=$(find .output -type f -name "*-mv2.zip")
MV2_NAME=$(basename $MV2_PATH)
MV3_PATH=$(find .output -type f -name "*-mv3.zip")
MV3_NAME=$(basename $MV3_PATH)
echo "mv2_path=$MV2_PATH" >> "$GITHUB_OUTPUT"
echo "mv2_name=$MV2_NAME" >> "$GITHUB_OUTPUT"
echo "mv3_path=$MV3_PATH" >> "$GITHUB_OUTPUT"
echo "mv3_name=$MV3_NAME" >> "$GITHUB_OUTPUT"
- name: Upload mv2 as artifact
uses: actions/upload-artifact@v5
with:
name: ${{ steps.paths.outputs.mv2_name }}
path: ${{ steps.paths.outputs.mv2_path }}
- name: Upload mv3 as artifact
uses: actions/upload-artifact@v5
with:
name: ${{ steps.paths.outputs.mv3_name }}
path: ${{ steps.paths.outputs.mv3_path }}
- name: Upload to latest release
if: github.event.inputs.dry_run != 'true'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: .output/*mv*.zip
tag: ${{ github.ref }}
overwrite: true
file_glob: true
- name: Submit to stores
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }}
FIREFOX_JWT_ISSUER: ${{ secrets.FIREFOX_JWT_ISSUER }}
FIREFOX_JWT_SECRET: ${{ secrets.FIREFOX_JWT_SECRET }}
CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
run: |
npx wxt submit \
--firefox-zip .output/*-mv2.zip --firefox-sources-zip .output/*-sources.zip \
--chrome-zip .output/*-mv3.zip

27
.gitignore vendored Normal file
View File

@@ -0,0 +1,27 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.output
stats.html
stats-*.json
.wxt
web-ext.config.ts
# Editor directories and files
.zed
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

11
.prettierignore Normal file
View File

@@ -0,0 +1,11 @@
.DS_Store
.idea
.vscode
.zed
node_modules
package-lock.json
.output
.wxt
.github

18
.prettierrc.cjs Normal file
View File

@@ -0,0 +1,18 @@
// @ts-check
/** @type {import("prettier").Config} */
module.exports = {
useTabs: true,
singleQuote: true,
trailingComma: 'none',
printWidth: 120,
tabWidth: 4,
plugins: ['prettier-plugin-svelte', '@ianvs/prettier-plugin-sort-imports'],
/* prettier-plugin-svelte */
overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }],
/* @ianvs/prettier-plugin-sort-imports */
importOrder: ['^~/(.*)$', '^./(.*)$', ''],
importOrderParserPlugins: ['typescript'],
importOrderTypeScriptVersion: '5.0.0',
importOrderCaseSensitive: false
};

View File

@@ -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"]

View File

@@ -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

215
README.md
View File

@@ -2,94 +2,189 @@
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%20Users&style=flat-square" alt="Firefox Addon Store">
</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 Store">
</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>
</p>
<p align="center">
<a href="#-introduction">Introduction 📝</a>
<a href="#-installation">Installation 📥</a>
<a href="#-features">Features ✨</a>
<a href="#-supported-websites">Supported Websites 📜</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 works:</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/) (Firefox for Android is supported too!)
- [Chrome Web Store](https://chromewebstore.google.com/detail/ddfpfjomnakfckhmilacnbokdaknamdb)
### Chromium / Google Chrome
<details>
<summary><h3 id="manual-installation1">Manual installation</h3></summary>
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 (mv2)
- Download `stream-bypass-<version>-mv2.zip` from the [latest release](https://github.com/bytedream/stream-bypass/releases/latest) and unzip it (e.g. with [7zip](https://www.7-zip.org/))
- 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 (mv3)
> As nearly every browser other than Firefox is based on Chromium, this should be the same for most of them
- Download `stream-bypass-<version>-mv3.zip` from the [latest release](https://github.com/bytedream/stream-bypass/releases/latest) and unzip it (e.g. [7zip](https://www.7-zip.org/))
- 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
</details>
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.
## ✨ Features
## Compiling
- ✔: Supported.
- ✖: Not supported.
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).
| Feature | Firefox (mv2) | Chrome (mv3) | Firefox for Android (mv2) |
| --------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------ | ------------------------- |
| Replace site-speicifc video player with browser native video player | ✔ | ✔ | ✔ |
| Support websites that are accessed via a redirect | ✔ | ✖ | ✔ |
| Open video in mpv (with [ff2mpv](https://github.com/bytedream/stream-bypass/tree/master#ff2mpv-use-mpv-to-directly-play-streams)) | ✔ | ✔ | ✖ |
### Manual
## 📜 Supported websites
For compiling everything bare bones, you need [typescript](https://www.typescriptlang.org/) and [sass](https://sass-lang.com/) installed.
- Compile typescript
```
$ 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.
- ✔: Everything ok.
- ⚠: Works with limitations.
- ✖: Not supported.
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.
| Site | Firefox & Firefox for Android (mv2) | Chrome & Chromium based (mv2) |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| [dropload.io](https://dropload.io) | ✔ | ✔ |
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔ | ⚠ (redirect probably required) |
| [filemoon.to](https://filemoon.to) | ✔ | ✔ |
| [goodstream.uno](https://goodstream.uno) | ✔ | ✔ |
| [kwik.cx](https://kwik.cx) | ✔ | ✔ |
| [loadx.ws](https://loadx.ws) | ✔ | ✖ (background request always required) |
| [luluvdo.com](https://luluvdo.com) | ✔ | ✖ (background request always required) |
| [mixdrop.co](https://mixdrop.co) | ✔ | ✔ |
| [mp4upload.com](https://mp4upload.com) | ✔ | ✔ |
| [newgrounds.com](https://newgrounds.com) | ✔ | ✔ |
| [streama2z.com](https://streama2z.com) | ✔ | ✖ (redirect always required) |
| [streamtape.com](https://streamtape.com) | ⚠ (correct video url can't always be extract, retrying/reloading the page might fix it) | ⚠ (correct video url can't always be extract, retrying/reloading the page might fix it) |
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | ✔ |
| [supervideo.tv](https://supervideo.tv) | ✔ | ✔ |
| [upstream.to](https://upstream.to) | ✔ | ✔ |
| [vidmoly.to](https://vidmoly.me) | ✔ | ✔ |
| [vidoza.net](https://vidoza.net) | ✔ | ✔ |
| [voe.sx](https://voe.sx) | ✔ | ✖ (redirect always required) |
| [vupload.com](https://vupload.com) | ✔ | ✔ |
### Docker
_This table might not be 100% accurate, it isn't actively monitored if the addon works for every website!_
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.
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.
## 🛠️ Building
If you want to build the addon from source and not using the way described in [installation](#-installation), 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 i
# build the extension and start it in a new firefox instance
$ npm run dev:firefox
# build the extension with optimizations to the .output/firefox-mv2 directory
$ npm run build:firefox
```
You can omit the `:firefox` suffix, then it's built for Chrome.
##### 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, follow the steps in the [manual 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
> You reach the settings by pressing the tree dots (⋮) in the top right corner of the extension popup.
### Hosts
You can enable or disabled for which hosts the extension should redirect.
### ff2mpv
[ff2mpv](https://github.com/woodruffw/ff2mpv) allows you to play streams directly in [mpv](https://mpv.io/) instead of the browser.
You can enable or disable this behavior.
<details>
<summary><i>Steps to get it set up</i></summary>
- 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://ddfpfjomnakfckhmilacnbokdaknamdb/` to `allowed_origins`:
```
"allowed_origins": [
"chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/",
"chrome-extension://ddfpfjomnakfckhmilacnbokdaknamdb/"
]
```
</details>
## ⚖ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.

View File

@@ -1,4 +0,0 @@
streamtape.com
vidoza.net
vivo.sx
vupload.com

105
build.py
View File

@@ -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()))

31
eslint.config.mjs Normal file
View File

@@ -0,0 +1,31 @@
import { fileURLToPath } from 'node:url';
import { includeIgnoreFile } from '@eslint/compat';
import js from '@eslint/js';
import prettier from 'eslint-config-prettier';
import svelte from 'eslint-plugin-svelte';
import { defineConfig } from 'eslint/config';
import ts from 'typescript-eslint';
const gitignorePath = fileURLToPath(new URL('.gitignore', import.meta.url));
export default defineConfig(
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs[('flat/recommended', 'flat/prettier')],
prettier,
includeIgnoreFile(gitignorePath),
{
files: ['**/*.svelte'],
languageOptions: {
parserOptions: {
parser: ts.parser
}
}
},
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'no-undef': 'off'
}
}
);

8355
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

54
package.json Normal file
View File

@@ -0,0 +1,54 @@
{
"name": "stream-bypass",
"version": "4.0.0",
"displayName": "Stream Bypass",
"author": "bytedream",
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/bytedream/stream-bypass.git"
},
"bugs": {
"url": "https://github.com/bytedream/stream-bypass/issues"
},
"type": "module",
"scripts": {
"dev": "wxt",
"dev:firefox": "wxt -b firefox",
"build": "wxt build",
"build:firefox": "wxt build -b firefox",
"zip": "wxt zip",
"zip:firefox": "wxt zip -b firefox",
"check": "svelte-check --tsconfig ./tsconfig.json",
"postinstall": "wxt prepare",
"format": "prettier --write .",
"lint": "prettier --check . && eslint ."
},
"devDependencies": {
"@eslint/compat": "^1.4.1",
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
"@steeze-ui/heroicons": "^2.4.2",
"@steeze-ui/svelte-icon": "^1.6.2",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@tailwindcss/vite": "^4.1.16",
"@tsconfig/svelte": "^5.0.5",
"@wxt-dev/module-svelte": "^2.0.4",
"eslint": "^9.38.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.12.5",
"hls.js": "^1.6.13",
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"sass": "^1.93.2",
"svelte": "^5.43.2",
"svelte-check": "^4.3.3",
"tailwindcss": "^4.1.16",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.2",
"vite": "^7.1.12",
"web-ext": "^9.1.0",
"wxt": "^0.20.11"
}
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

21
src/assets/base.css Normal file
View File

@@ -0,0 +1,21 @@
@import 'tailwindcss';
@theme {
--color-linux-mint-green: #35a854;
--default-font-family: Arial, Helvetica, sans-serif;
}
@layer base {
body {
background-color: #030712;
color: var(--color-gray-300);
margin: 0;
padding: 0;
}
a {
text-decoration: none;
}
}

View File

@@ -0,0 +1,30 @@
import { UrlReferer } from '@/lib/settings';
export default defineBackground(() => {
browser.runtime.onMessage.addListener(async (message) => {
if (message.action == 'ff2mpv') {
await browser.runtime.sendNativeMessage('ff2mpv', { url: message.url });
}
// the following listener is only available in mv2
if (import.meta.env.MANIFEST_VERSION === 3) return;
browser.webRequest.onBeforeSendHeaders.addListener(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
async (details) => {
const referer = await UrlReferer.get(new URL(details.url).hostname);
if (!referer) return;
details.requestHeaders!.push({
name: 'Referer',
value: `https://${referer}/`
});
return { requestHeaders: details.requestHeaders };
},
{ urls: ['<all_urls>'], types: ['xmlhttprequest'] },
['blocking', 'requestHeaders']
);
});
});

View File

@@ -0,0 +1,78 @@
import { getHost, hosts, type Host, type HostMatch } from '@/lib/host';
import { FF2MPVSettings } from '@/lib/settings';
export default defineContentScript({
matches: [
...Object.values(hosts).flatMap((h) => h.domains.map((d) => `*://${d}/*`)),
// only mv2 allows to match all urls
...(import.meta.env.MANIFEST_VERSION === 2 ? ['<all_urls>'] : [])
],
allFrames: true,
runAt: 'document_end',
main
});
async function main() {
let host: Host | null;
if ((host = await getHost(window.location.host)) === null) {
return;
}
let re = null;
for (const regex of host.regex) {
if ((re = document.body.innerHTML.match(regex)) !== null) {
break;
}
}
if (re === null) {
return;
}
let hostMatch: HostMatch | null;
try {
hostMatch = await host.match(re);
} catch {
hostMatch = null;
}
if (!hostMatch || !hostMatch.url) return;
// send the url to the ff2mpv (https://github.com/woodruffw/ff2mpv) application
if (await FF2MPVSettings.getEnabled()) {
await browser.runtime.sendMessage({ action: 'ff2mpv', url: hostMatch.url });
}
if (host.replace && hostMatch.type != 'hls') {
// this destroys all intervals that may spawn popups or events
let intervalId = window.setInterval(() => {}, 0);
while (intervalId--) {
clearInterval(intervalId);
}
let timeoutId = window.setTimeout(() => {}, 0);
while (timeoutId--) {
clearTimeout(timeoutId);
}
// clear completed document
document.documentElement.innerHTML = '';
document.body.style.backgroundColor = '#131313';
// video player
const player = document.createElement('video');
player.style.width = '100%';
player.style.height = '100%';
player.controls = true;
player.src = hostMatch.url;
// add video player to document body
document.body.style.margin = '0';
document.body.append(player);
} else {
window.location.assign(
browser.runtime.getURL(
`/player.html?id=${host.id}&url=${encodeURIComponent(hostMatch.url)}&domain=${window.location.hostname}&type=${hostMatch.type}`
)
);
}
}

View File

@@ -0,0 +1,27 @@
<script lang="ts">
import { onMount } from 'svelte';
import { play } from './player';
let errorMessage: string | null = $state(null);
let videoElem: HTMLVideoElement;
onMount(async () => {
try {
await play(videoElem);
videoElem.controls = true;
} catch (e) {
errorMessage = e as string;
}
});
</script>
<!-- svelte-ignore a11y_media_has_caption -->
<video class="absolute top-0 left-0 w-full h-full m-0" bind:this={videoElem}></video>
{#if errorMessage}
<div class="h-full flex items-center justify-center text-center">
<p>
{errorMessage}&nbsp;<a class="underline" href="https://github.com/bytedream/stream-bypass/issues">here</a>.
</p>
</div>
{/if}

View File

@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Stream Bypass</title>
<style>
html,
body,
video {
width: 100%;
height: 100%;
margin: 0;
}
</style>
</head>
<body style="background-color: #131313; overflow: hidden">
<script type="module">
import { mount } from 'svelte';
import Player from './Player.svelte';
mount(Player, {
target: document.body
});
</script>
</body>
</html>

View File

@@ -0,0 +1,75 @@
import Hls from 'hls.js';
import { listenMessages, MessageType, sendMessage } from '@/lib/communication';
import { HostMatchType, hosts } from '@/lib/host';
import { UrlReferer } from '@/lib/settings';
async function playNative(url: string, domain: string, videoElem: HTMLVideoElement) {
// multiple hosts need to have a correct referer set
await UrlReferer.addTemporary(new URL(url).hostname, domain);
videoElem.src = url;
}
async function playHls(url: string, domain: string, videoElem: HTMLVideoElement) {
if (videoElem.canPlayType('application/vnd.apple.mpegurl')) {
videoElem.src = url;
} else if (Hls.isSupported()) {
const hls = new Hls({
enableWorker: false,
xhrSetup: async (xhr: XMLHttpRequest, url: string) => {
// multiple hosts need to have a correct referer set
await UrlReferer.addTemporary(new URL(url).hostname, domain);
xhr.open('GET', url);
}
});
hls.loadSource(url);
hls.attachMedia(videoElem);
} else {
throw 'Failed to play m3u8 video (hls is not supported). Try again or create a new issue';
}
}
export async function play(videoElem: HTMLVideoElement) {
const urlQuery = new URLSearchParams(window.location.search);
const id = urlQuery.get('id') as string;
const url = decodeURIComponent(urlQuery.get('url') as string);
const domain = urlQuery.get('domain') as string;
const type = urlQuery.get('type') as HostMatchType;
const host = hosts.find((host) => host.id === id);
if (!host) {
throw `Invalid id: ${id}. Please report this`;
}
document.title = `Stream Bypass (${domain})`;
initCommunication(id, url, domain);
switch (type) {
case HostMatchType.NATIVE:
await playNative(url, domain, videoElem);
break;
case HostMatchType.HLS:
await playHls(url, domain, videoElem);
break;
}
}
function initCommunication(id: string, url: string, domain: string) {
const notifyActiveMatch = () =>
sendMessage(MessageType.NotifyActiveMatch, {
id: id,
url: url,
domain: domain
});
// if an extension popup is open, it will be notified that a match/player is now active
notifyActiveMatch();
// if an extension popup is opened, the listener will recognize it's active match request and send the match/player
// data
const cancel = listenMessages((type) => {
if (type !== MessageType.RequestActiveMatch) return;
notifyActiveMatch();
});
window.onbeforeunload = cancel;
}

View File

@@ -0,0 +1,33 @@
<script lang="ts">
import '@/assets/base.css';
import { fly } from 'svelte/transition';
import Main from '@/entrypoints/popup/pages/main/Main.svelte';
import Settings from '@/entrypoints/popup/pages/settings/Settings.svelte';
import { isMobile } from '@/entrypoints/popup/state.js';
/* state init */
browser.runtime.getPlatformInfo().then((info) => ($isMobile = info.os === 'android'));
/* types */
type Page = 'main' | 'settings';
/* states */
let activePage = $state<Page>('main');
</script>
<div class="flex w-[350px] overflow-hidden" class:w-screen={$isMobile}>
{#if activePage === 'main'}
<div transition:fly={{ x: -300, duration: 150 }} class="min-w-full w-full h-[300px] flex-1 flex flex-col">
<Main onSettingsOpenRequest={() => (activePage = 'settings')} />
</div>
{:else if activePage === 'settings'}
<div
transition:fly={{ x: 300, duration: 150 }}
class="min-w-full w-full h-[300px] flex-1 flex flex-col"
class:h-screen={$isMobile}
>
<Settings onSettingsCloseRequest={() => (activePage = 'main')} />
</div>
{/if}
</div>

View File

@@ -0,0 +1 @@
<div class="w-full border-b-[1px] border-gray-400"></div>

View File

@@ -0,0 +1,57 @@
<script lang="ts">
/* types */
interface Props {
checked: boolean;
onChecked?: (checked: boolean) => void | boolean | Promise<void> | Promise<boolean>;
size?: 'sm' | 'md';
}
/* states */
let { checked = $bindable(), onChecked, size = 'md' }: Props = $props();
let internalChecked = $state($state.snapshot(checked));
/* callbacks */
async function onInputChange() {
internalChecked = !internalChecked;
let approved = false;
if (!onChecked) {
approved = true;
} else {
const ret = onChecked(!internalChecked);
if (typeof ret === 'boolean') {
approved = ret;
} else if (typeof ret === 'object' && 'then' in ret && typeof ret.then === 'function') {
const promiseRet = await ret;
if (typeof promiseRet === 'undefined') approved = true;
else approved = promiseRet;
}
}
if (approved) {
internalChecked = !internalChecked;
checked = internalChecked;
}
}
</script>
<label class="flex items-center cursor-pointer">
<div class="relative">
<input type="checkbox" class="peer sr-only" bind:checked={internalChecked} onchange={onInputChange} />
<div
class="block rounded-full box bg-red-700 peer-checked:bg-linux-mint-green"
class:w-8={size === 'sm'}
class:h-4={size === 'sm'}
class:w-10={size === 'md'}
class:h-5={size === 'md'}
></div>
<div
class="absolute flex items-center justify-center transition bg-white rounded-full dot left-0 top-0 peer-checked:translate-x-full"
class:w-4={size === 'sm'}
class:h-4={size === 'sm'}
class:w-5={size === 'md'}
class:h-5={size === 'md'}
></div>
</div>
</label>

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stream Bypass</title>
<meta name="manifest.type" content="browser_action" />
</head>
<body>
<div id="app" class="contents h-full"></div>
<script type="module" src="./main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,8 @@
import { mount } from 'svelte';
import App from './App.svelte';
const app = mount(App, {
target: document.getElementById('app')!
});
export default app;

View File

@@ -0,0 +1,3 @@
<div class="h-full flex items-center justify-center">
<p class="text-[1.05rem]">Extension disabled</p>
</div>

View File

@@ -0,0 +1,69 @@
<script lang="ts">
import { Clipboard, InformationCircle } from '@steeze-ui/heroicons';
import { Icon } from '@steeze-ui/svelte-icon';
import { isMobile } from '@/entrypoints/popup/state';
/* types */
interface Props {
url: string;
domain: string;
}
type UrlType = 'url' | 'curl';
/* states */
let { url, domain }: Props = $props();
let urlOutputType: UrlType = $state('url');
let urlOutput = $derived(getUrl(urlOutputType));
/* functions */
function getUrl(type: UrlType) {
switch (type) {
case 'url':
return url;
case 'curl':
return `curl -H "Referer: https://${domain}/" "${encodeURI(url)}"`;
}
}
/* callbacks */
function copyUrl() {
navigator.clipboard.writeText(urlOutput);
}
</script>
<div>
<div class="flex gap-2 items-center pb-1">
<p class="mt-0.5 text-sm">Show video as</p>
<select
class="w-fit text-xs border text-slate-200 border-gray-500 rounded cursor-pointer pt-1 pb-0.5 pl-1"
bind:value={urlOutputType}
>
<option value="url">URL</option>
<option value="curl">cURL</option>
</select>
{#if urlOutputType === 'url'}
<div class="relative group h-4 flex justify-center items-center">
<button class="text-sm peer"><Icon src={InformationCircle} size="1rem" /></button>
<span
class="z-10 absolute w-58 bottom-5/4 p-1 bg-gray-800 text-xs invisible opacity-0 group-hover:visible group-hover:opacity-100 peer-focus:visible peer-focus:opacity-100 transition-[opacity]"
>You may have to send the referer header <code class="select-text">Referer: {domain}</code> when accessing
the url</span
>
</div>
{/if}
</div>
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<div class="relative group w-full" tabindex={$isMobile ? 0 : undefined}>
<pre
class="w-full h-20 overflow-y-scroll text-[0.8rem] wrap-anywhere text-wrap select-text rounded bg-gray-900 py-[0.25rem] px-1.5">{urlOutput}</pre>
<div
class="absolute top-2 right-2 transition-opacity duration-100 opacity-0 group-hover:opacity-100 group-focus:opacity-100 h-full"
>
<button class="cursor-pointer" title="Copy to clipboard" onclick={copyUrl}>
<Icon src={Clipboard} size="1rem" />
</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,46 @@
<script lang="ts">
import Toggle from '@/entrypoints/popup/components/Toggle.svelte';
import { HostSettings } from '@/lib/settings';
/* types */
interface Props {
allHostsDisabled: boolean;
onSettingsClick: () => void;
}
/* states */
let { allHostsDisabled = $bindable(), onSettingsClick }: Props = $props();
/* effects */
$effect(() => {
HostSettings.setAllHostsDisabled(allHostsDisabled);
browser.browserAction.setIcon({
path: allHostsDisabled
? {
16: 'icon/stream-bypass_disabled@16px.png',
32: 'icon/stream-bypass_disabled@32px.png',
48: 'icon/stream-bypass_disabled@48px.png',
128: 'icon/stream-bypass_disabled@128px.png'
}
: {
16: 'icon/stream-bypass@16px.png',
32: 'icon/stream-bypass@32px.png',
48: 'icon/stream-bypass@48px.png',
128: 'icon/stream-bypass@128px.png'
}
});
});
</script>
<div class="flex justify-between items-center p-2">
<div class="flex items-baseline gap-2">
<h1>stream-bypass</h1>
<span class="text-xs text-gray-400">v{import.meta.env.VERSION}</span>
</div>
<div class="flex items-center gap-2">
{#key allHostsDisabled}
<Toggle bind:checked={() => !allHostsDisabled, (v) => (allHostsDisabled = !v)} />
{/key}
<button class="font-bold cursor-pointer" onclick={() => onSettingsClick()}>⋮</button>
</div>
</div>

View File

@@ -0,0 +1,65 @@
<script lang="ts">
import '@/assets/base.css';
import { onDestroy } from 'svelte';
import Divider from '@/entrypoints/popup/components/Divider.svelte';
import AllDisabled from '@/entrypoints/popup/pages/main/AllDisabled.svelte';
import CopyMatch from '@/entrypoints/popup/pages/main/CopyMatch.svelte';
import Header from '@/entrypoints/popup/pages/main/Header.svelte';
import Match from '@/entrypoints/popup/pages/main/Match.svelte';
import NoMatch from '@/entrypoints/popup/pages/main/NoMatch.svelte';
import { listenMessages, MessageType, sendMessageToActiveTab } from '@/lib/communication';
import { hosts, type Host } from '@/lib/host';
import { HostSettings } from '@/lib/settings';
/* types */
interface Props {
onSettingsOpenRequest: () => void;
}
/* states */
let { onSettingsOpenRequest }: Props = $props();
let currentMatch = $state<{ host: Host; url: string; domain: string } | null>(null);
let allHostsDisabled = $state(false);
HostSettings.getAllHostsDisabled().then((val) => (allHostsDisabled = val));
/* lifecycle */
const cancel = listenMessages((type, data) => {
if (type !== MessageType.NotifyActiveMatch) return;
currentMatch = {
host: hosts.find((host) => host.id === data.id)!,
url: data.url,
domain: data.domain
};
});
sendMessageToActiveTab(MessageType.RequestActiveMatch, undefined);
onDestroy(cancel);
</script>
<div class="w-full">
<Header bind:allHostsDisabled onSettingsClick={onSettingsOpenRequest} />
<Divider />
</div>
<div class="px-2 h-full">
{#if allHostsDisabled}
<AllDisabled />
{:else if !currentMatch}
<NoMatch />
{:else}
<div class="flex flex-col justify-between h-full pb-2">
<Match host={currentMatch.host} domain={currentMatch.domain} />
<div class="divider border-dashed"></div>
<div class="mt-2">
<CopyMatch url={currentMatch.url} domain={currentMatch.domain} />
</div>
</div>
{/if}
</div>
<style>
* {
@apply select-none;
}
</style>

View File

@@ -0,0 +1,20 @@
<script lang="ts">
import { type Host } from '@/lib/host';
/* types */
interface Props {
host: Host;
domain: string;
}
/* states */
const { host, domain }: Props = $props();
</script>
<div class="flex flex-col items-center justify-center w-full h-full">
<p class="text-lg">Match found:</p>
<div class="[&>*]:select-text">
<span class="underline text-green-400 text-2xl font-bold">{host.name}</span>
<span class="text-xs text-slate-300">({domain})</span>
</div>
</div>

View File

@@ -0,0 +1,13 @@
<div class="relative h-full">
<div class="h-full flex items-center justify-center">
<p class="text-[1.05rem]">No supported video found on this site</p>
</div>
<div class="absolute bottom-0.5">
<p class="text-xs text-gray-400">
Suggestions or bugs can be submitted <a
class="underline"
href="https://github.com/bytedream/stream-bypass/issues">here</a
>
</p>
</div>
</div>

View File

@@ -0,0 +1,35 @@
<script lang="ts">
import { InformationCircle } from '@steeze-ui/heroicons';
import { Icon } from '@steeze-ui/svelte-icon';
import Toggle from '@/entrypoints/popup/components/Toggle.svelte';
import { FF2MPVSettings } from '@/lib/settings';
/* states */
let enabled = $state(false);
FF2MPVSettings.getEnabled().then((val) => (enabled = val));
/* callbacks */
function onEnableChange(enabled: boolean) {
if (!enabled) return true;
return browser.permissions.request({ permissions: ['nativeMessaging'] });
}
</script>
<div class="flex items-center gap-2">
<div class="relative mr-3">
<span>Communication enabled</span>
<a
class="absolute -top-1 -right-4 text-sm"
href="https://github.com/bytedream/stream-bypass/tree/main?tab=readme-ov-file#ff2mpv-use-mpv-to-directly-play-streams"
target="_blank"><Icon src={InformationCircle} size="1rem" /></a
>
</div>
{#key enabled}
<Toggle
bind:checked={() => enabled, (v) => FF2MPVSettings.setEnabled(v)}
onChecked={onEnableChange}
size="sm"
/>
{/key}
</div>

View File

@@ -0,0 +1,16 @@
<script lang="ts">
/* types */
interface Props {
onBackClick: () => void;
}
/* states */
let { onBackClick }: Props = $props();
</script>
<div class="flex justify-between items-center p-2">
<div class="flex items-baseline gap-2">
<button class="cursor-pointer" onclick={() => onBackClick()}>←</button>
<h1>Settings</h1>
</div>
</div>

View File

@@ -0,0 +1,41 @@
<script lang="ts">
import Toggle from '@/entrypoints/popup/components/Toggle.svelte';
import { hosts } from '@/lib/host';
import { HostSettings } from '@/lib/settings';
/* states */
let disabledHostIds = $state<Array<string>>([]);
HostSettings.getDisabledHosts().then((val) => (disabledHostIds = val));
</script>
<div class="grid grid-cols-[35%_43%_22%] gap-y-0.75">
<p class="font-bold">Host</p>
<p class="font-bold">Domains</p>
<p class="font-bold">Enabled</p>
{#each hosts as host (host.id)}
{@const domainList = host.domains.join(', ')}
<p>{host.name}</p>
<div>
<label for={host.id}>
<input id={host.id} type="checkbox" class="peer hidden" checked />
<p
title={domainList}
class="cursor-pointer overflow-hidden peer-checked:text-ellipsis peer-checked:text-nowrap"
>
{domainList}
</p>
</label>
</div>
<div class="mt-[.2rem]">
{#key disabledHostIds}
<Toggle
bind:checked={
() => !disabledHostIds.includes(host.id),
(v) => (v ? HostSettings.removeDisabledHost(host) : HostSettings.addDisabledHost(host))
}
size="sm"
></Toggle>
{/key}
</div>
{/each}
</div>

View File

@@ -0,0 +1,60 @@
<script lang="ts">
import Divider from '@/entrypoints/popup/components/Divider.svelte';
import Ff2mpv from '@/entrypoints/popup/pages/settings/Ff2mpv.svelte';
import Header from '@/entrypoints/popup/pages/settings/Header.svelte';
import HostsTable from '@/entrypoints/popup/pages/settings/HostsTable.svelte';
import { isMobile } from '@/entrypoints/popup/state';
/* types */
interface Props {
onSettingsCloseRequest: () => void;
}
/* states */
let { onSettingsCloseRequest }: Props = $props();
</script>
<div class="w-full">
<Header onBackClick={onSettingsCloseRequest} />
<Divider />
</div>
<div class="flex flex-col gap-y-1 pt-1 h-full mx-2 my-1 overflow-y-scroll">
<details class="details" open>
<summary>Hosts</summary>
<HostsTable />
</details>
{#if !$isMobile}
<details class="details">
<summary>ff2mpv</summary>
<Ff2mpv />
</details>
{/if}
</div>
<style>
* {
@apply select-none;
}
.details {
/* using normal css instead of tailwind in the following blocks.
for some reason tailwind fails to resolve many references */
&::before,
&::after {
content: '';
display: block;
width: 100%;
border-top: 1px solid var(--color-gray-600);
margin: 0.25rem 0;
}
& > summary {
cursor: pointer;
}
&[open] > summary {
margin-bottom: 0.5rem;
}
}
</style>

View File

@@ -0,0 +1,3 @@
import { writable } from 'svelte/store';
export const isMobile = writable(false);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -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
}
}
})

36
src/lib/communication.ts Normal file
View File

@@ -0,0 +1,36 @@
export enum MessageType {
RequestActiveMatch,
NotifyActiveMatch
}
export type MessageData<T extends MessageType> = {
[MessageType.RequestActiveMatch]: undefined;
[MessageType.NotifyActiveMatch]: {
id: string;
url: string;
domain: string;
};
}[T];
export async function sendMessage<T extends MessageType>(message: T, data: MessageData<T>) {
await browser.runtime.sendMessage({ type: message, data: data });
}
export async function sendMessageToActiveTab<T extends MessageType>(message: T, data: MessageData<T>) {
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
await browser.tabs.sendMessage(tabs[0].id!, { type: message, data: data }).catch(() => {});
}
export function listenMessages(listener: (type: MessageType, data: any) => void): () => void {
const callback = (callbackData: { type: MessageType; data: any }) => {
const { type, data } = callbackData;
listener(type, data);
};
browser.runtime.onMessage.addListener(callback);
return () => {
browser.runtime.onMessage.removeListener(callback);
};
}

View File

@@ -0,0 +1,41 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
name: 'Doodstream',
id: 'doodstream',
domains: [
'doodstream.com',
'dood.pm',
'dood.ws',
'dood.wf',
'dood.cx',
'dood.sh',
'dood.watch',
'dood.work',
'dood.to',
'dood.so',
'dood.la',
'dood.li',
'dood.re',
'dood.yt',
'doods.pro',
'ds2play.com',
'dooood.com',
'd000d.com'
],
replace: true,
regex: [/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s],
match: async function (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 {
type: HostMatchType.HLS,
url: `${await response.text()}1234567890${match[2]}${Date.now()}`
};
}
} satisfies Host;

17
src/lib/host/dropload.ts Normal file
View File

@@ -0,0 +1,17 @@
import { HostMatchType, type Host } from '@/lib/host';
import { unpack } from '@/utils/content';
export default {
name: 'Dropload',
id: 'dropload',
domains: ['dropload.io'],
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
match: async function (match: RegExpMatchArray) {
const unpacked = await unpack(match[0]);
return {
type: HostMatchType.HLS,
url: unpacked.match(/(?<=file:").*(?=")/)![0]
};
}
} satisfies Host;

24
src/lib/host/filemoon.ts Normal file
View File

@@ -0,0 +1,24 @@
import { HostMatchType, type Host } from '@/lib/host';
import { HostSettings } from '@/lib/settings';
import { unpack } from '@/utils/content';
export default {
name: 'Filemoon',
id: 'filemoon',
domains: ['filemoon.sx', 'filemoon.to', 'filemoon.in'],
regex: [/(?<=<iframe\s*src=")\S*(?=")/s, /eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
replace: true,
match: async function (match: RegExpMatchArray) {
if (window.location.host.startsWith('filemoon')) {
await HostSettings.addTemporaryHostDomain(this, new URL(match[0]).host);
return null;
}
const unpacked = await unpack(match[0]);
return {
type: HostMatchType.HLS,
url: unpacked.match(/(?<=file:")\S*(?=")/)![0]
};
}
} satisfies Host;

View File

@@ -0,0 +1,15 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
name: 'Goodstream',
id: 'goodstream',
domains: ['goodstream.uno'],
regex: [/(?<=file:\s+").*(?=")/g],
match: async function (match: RegExpMatchArray) {
return {
type: HostMatchType.HLS,
url: match[0]
};
}
} satisfies Host;

78
src/lib/host/index.ts Normal file
View File

@@ -0,0 +1,78 @@
import Doodstream from './doodstream';
import Dropload from './dropload';
import Filemoon from './filemoon';
import Goodstream from './goodstream';
import Kwik from './kwik';
import Loadx from './loadx';
import Luluvdo from './luluvdo';
import Mixdrop from './mixdrop';
import Mp4Upload from './mp4upload';
import Newgrounds from './newgrounds';
import StreamA2z from './streama2z';
import Streamtape from './streamtape';
import Streamzz from './streamzz';
import SuperVideo from './supervideo';
import Upstream from './upstream';
import Vidmoly from './vidmoly';
import Vidoza from './vidoza';
import Voe from './voe';
import Vupload from './vupload';
import { HostSettings } from '@/lib/settings';
export enum HostMatchType {
NATIVE = 'native',
HLS = 'hls'
}
export interface HostMatch {
type: HostMatchType;
/** If null, it's interpreted that a url should be present but isn't, probably because the website broke */
url: string | null;
}
export interface Host {
name: string;
id: string;
domains: string[];
replace?: boolean;
regex: RegExp[];
notice?: string;
match(match: RegExpMatchArray): Promise<HostMatch | null>;
}
export const hosts = [
Doodstream,
Dropload,
Filemoon,
Goodstream,
Kwik,
Loadx,
Luluvdo,
Mixdrop,
Mp4Upload,
Newgrounds,
StreamA2z,
Streamtape,
Streamzz,
SuperVideo,
Upstream,
Vidmoly,
Vidoza,
Voe,
Vupload
];
export async function getHost(domain: string) {
if (await HostSettings.getAllHostsDisabled()) return null;
const disabledIds = await HostSettings.getDisabledHosts();
for (const host of hosts) {
if (host.domains.includes(domain)) {
if (!disabledIds.includes(host.id)) return host;
else return null;
}
}
return HostSettings.checkTemporaryHostDomain(domain);
}

17
src/lib/host/kwik.ts Normal file
View File

@@ -0,0 +1,17 @@
import { HostMatchType, type Host } from '@/lib/host';
import { unpack } from '@/utils/content';
export default {
name: 'Kwik',
id: 'kwik',
domains: ['kwik.cx'],
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
match: async function (match: RegExpMatchArray) {
const unpacked = await unpack(match[0]);
return {
type: HostMatchType.HLS,
url: unpacked.match(/(?<=source=').*(?=')/)![0]
};
}
} satisfies Host;

27
src/lib/host/loadx.ts Normal file
View File

@@ -0,0 +1,27 @@
import { HostMatchType, type Host } from '@/lib/host';
import { lastPathSegment } from '@/utils/extract';
export default {
name: 'LoadX',
id: 'loadx',
domains: ['loadx.ws'],
regex: [/./gm],
match: async () => {
const hash = encodeURIComponent(lastPathSegment(window.location.href));
const response = await fetch(`https://${window.location.host}/player/index.php?data=${hash}&do=getVideo`, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
const responseJson = await response.json();
const videoSource: string = responseJson['videoSource'];
return {
type: HostMatchType.HLS,
url: videoSource.replace('\\/', '/')
};
}
} satisfies Host;

32
src/lib/host/luluvdo.ts Normal file
View File

@@ -0,0 +1,32 @@
import { HostMatchType, type Host } from '@/lib/host';
import { unpack } from '@/utils/content';
import { lastPathSegment } from '@/utils/extract';
export default {
name: 'Luluvdo',
id: 'luluvdo',
domains: ['luluvdo.com'],
regex: [/./gm],
match: async () => {
const requestBody = new FormData();
requestBody.set('op', 'embed');
requestBody.set('file_code', lastPathSegment(window.location.href));
const response = await fetch(`https://${window.location.host}/dl`, {
method: 'POST',
body: requestBody,
referrer: window.location.href
});
const responseText = await response.text();
const evalMatch = responseText.match(/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms)!;
// sometimes is packed, sometimes it's not. looks like someone forgets to obfuscate the code when pushing to
// production
const unpacked = evalMatch ? await unpack(evalMatch[0]) : responseText;
return {
type: HostMatchType.HLS,
url: unpacked.match(/(?<=file:").*(?=")/)![0]
};
}
} satisfies Host;

18
src/lib/host/mixdrop.ts Normal file
View File

@@ -0,0 +1,18 @@
import { HostMatchType, type Host } from '@/lib/host';
import { unpack } from '@/utils/content';
export default {
name: 'Mixdrop',
id: 'mixdrop',
domains: ['mixdrop.bz', 'mixdrop.ch', 'mixdrop.co', 'mixdrop.gl', 'mixdrop.my', 'mixdrop.to'],
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
match: async function (match: RegExpMatchArray) {
const unpacked = await unpack(match[0]);
const url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)![0];
return {
type: HostMatchType.NATIVE,
url: `https:${url}`
};
}
} satisfies Host;

18
src/lib/host/mp4upload.ts Normal file
View File

@@ -0,0 +1,18 @@
import { HostMatchType, type Host } from '@/lib/host';
import { unpack } from '@/utils/content';
export default {
name: 'Mp4Upload',
id: 'mp4upload',
domains: ['mp4upload.com'],
replace: true,
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
match: async function (match: RegExpMatchArray) {
const unpacked = await unpack(match[0]);
return {
type: HostMatchType.NATIVE,
url: unpacked.match(/(?<=player.src\(").*(?=")/)![0]
};
}
} satisfies Host;

View File

@@ -0,0 +1,22 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
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 {
type: HostMatchType.HLS,
url: decodeURI(json['sources'][Object.keys(json['sources'])[0]][0]['src'])
};
}
} satisfies Host;

20
src/lib/host/streama2z.ts Normal file
View File

@@ -0,0 +1,20 @@
import { HostMatchType, type Host } from '@/lib/host';
import { HostSettings } from '@/lib/settings';
export default {
name: 'Stream2Az',
id: 'stream2az',
domains: ['streama2z.com', 'streama2z.xyz'],
regex: [/https?:\/\/\S*m3u8.+(?=['"])/gm],
match: async function (match: RegExpMatchArray) {
if (this.domains.indexOf(window.location.hostname) !== -1) {
await HostSettings.addTemporaryHostDomain(this, window.location.hostname);
return null;
}
return {
type: HostMatchType.HLS,
url: match[0]
};
}
} satisfies Host;

View File

@@ -0,0 +1,26 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
name: 'Streamtape',
id: 'streamtape',
domains: ['streamtape.com', 'streamtape.net', 'shavetape.cash'],
regex: [/id=.*(?=')/gm],
match: async function (match: RegExpMatchArray) {
let i = 0;
while (i < match.length) {
if (match[++i - 1] == match[i]) {
return {
type: HostMatchType.HLS,
url: `https://streamtape.com/get_video?${match[i]}`
};
}
}
return {
type: HostMatchType.HLS,
// use the old method as fallback
url: `https://streamtape.com/get_video?${match.reverse()[0]}`
};
}
} satisfies Host;

17
src/lib/host/streamzz.ts Normal file
View File

@@ -0,0 +1,17 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
name: 'Streamzz',
id: 'streamzz',
domains: ['streamzz.to', 'streamz.ws'],
regex: [/(?<=\|)\w{2,}/gm],
match: async function (match: RegExpMatchArray) {
return {
type: HostMatchType.HLS,
url: `https://get.${location.hostname.split('.')[0]}.tw/getlink-${
match.sort((a, b) => b.length - a.length)[0]
}.dll`
};
}
} satisfies Host;

View File

@@ -0,0 +1,17 @@
import { HostMatchType, type Host } from '@/lib/host';
import { unpack } from '@/utils/content';
export default {
name: 'Supervideo',
id: 'supervideo',
domains: ['supervideo.cc', 'supervideo.tv'],
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
match: async function (match: RegExpMatchArray) {
const unpacked = await unpack(match[0]);
return {
type: HostMatchType.HLS,
url: unpacked.match(/(?<=file:").*(?=")/)![0]
};
}
} satisfies Host;

17
src/lib/host/upstream.ts Normal file
View File

@@ -0,0 +1,17 @@
import { HostMatchType, type Host } from '@/lib/host';
import { unpack } from '@/utils/content';
export default {
name: 'Upstream',
id: 'upstream',
domains: ['upstream.to'],
regex: [/eval\(function\(p,a,c,k,e,d\).*?(?=<\/script>)/gms],
match: async function (match: RegExpMatchArray) {
const unpacked = await unpack(match[0]);
return {
type: HostMatchType.HLS,
url: unpacked.match(/(?<=file:").*(?=")/)![0]
};
}
} satisfies Host;

15
src/lib/host/vidmoly.ts Normal file
View File

@@ -0,0 +1,15 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
name: 'Vidmoly',
id: 'vidmoly',
domains: ['vidmoly.me', 'vidmoly.net', 'vidmoly.to'],
regex: [/(?<=file:").+\.m3u8.*(?=")/gm],
match: async function (match: RegExpMatchArray) {
return {
type: HostMatchType.HLS,
url: match[0]
};
}
} satisfies Host;

16
src/lib/host/vidoza.ts Normal file
View File

@@ -0,0 +1,16 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
name: 'Vidoza',
id: 'vidoza',
domains: ['vidoza.net', 'videzz.net'],
regex: [/(?<=src:\s?").+?(?=")/gm],
replace: true,
match: async function (match: RegExpMatchArray) {
return {
type: HostMatchType.HLS,
url: match[0]
};
}
} satisfies Host;

74
src/lib/host/voe.ts Normal file
View File

@@ -0,0 +1,74 @@
import { HostMatchType, type Host } from '@/lib/host';
import { HostSettings } from '@/lib/settings';
function rot13(encrypted: string) {
let decrypted = '';
for (let i = 0; i < encrypted.length; i++) {
let char = encrypted.charCodeAt(i);
if (char >= 65 && char <= 90) {
char = ((char - 65 + 13) % 26) + 65;
} else if (char >= 97 && char <= 122) {
char = ((char - 97 + 13) % 26) + 97;
}
decrypted += String.fromCharCode(char);
}
return decrypted;
}
function removeSpecialSequences(input: string) {
return input
.replaceAll(/@\$/g, '')
.replaceAll(/\^\^/g, '')
.replaceAll(/~@/g, '')
.replaceAll(/%\?/g, '')
.replaceAll(/\*~/g, '')
.replaceAll(/!!/g, '')
.replaceAll(/#&/g, '');
}
function shiftString(input: string) {
let shifted = '';
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
const shiftedChar = char - 3;
shifted += String.fromCharCode(shiftedChar);
}
return shifted;
}
export default {
name: 'Voe',
id: 'voe',
domains: ['voe.sx'],
regex: [
// voe.sx
/(?<=window\.location\.href\s=\s')\S*(?=')/gm,
// whatever site voe.sx redirects to
/(?<=<script type="application\/json">).*(?=<\/script>)/m
],
match: async function (match: RegExpMatchArray) {
if (window.location.host === 'voe.sx') {
await HostSettings.addTemporaryHostDomain(this, new URL(match[0]).host);
return null;
} else {
let json = match[0];
json = JSON.parse(json);
let deobfuscated = json[0];
deobfuscated = rot13(deobfuscated);
deobfuscated = removeSpecialSequences(deobfuscated);
deobfuscated = atob(deobfuscated);
deobfuscated = shiftString(deobfuscated);
deobfuscated = deobfuscated.split('').reverse().join('');
deobfuscated = atob(deobfuscated);
const payload = JSON.parse(deobfuscated);
return {
type: HostMatchType.HLS,
url: payload['source']
};
}
}
} satisfies Host;

15
src/lib/host/vupload.ts Normal file
View File

@@ -0,0 +1,15 @@
import { HostMatchType, type Host } from '@/lib/host';
export default {
name: 'Vupload',
id: 'vupload',
domains: ['vupload.com'],
regex: [/(?<=src:\s?").+?(?=")/gm],
match: async function (match: RegExpMatchArray) {
return {
type: HostMatchType.HLS,
url: match[0]
};
}
} satisfies Host;

88
src/lib/settings.ts Normal file
View File

@@ -0,0 +1,88 @@
import { storage } from '#imports';
import { hosts, type Host } from '@/lib/host';
export class HostSettings {
/* disabled hosts */
private static disabledHosts = storage.defineItem<string[]>('local:disabledHosts', { fallback: [] });
private static allHostsDisabled = storage.defineItem<boolean>('local:allHostsDisabled', { fallback: false });
static async addDisabledHost(host: Host) {
const ids = await this.disabledHosts.getValue();
const index = ids.indexOf(host.id);
if (index === -1) {
ids.push(host.id);
await this.disabledHosts.setValue(ids);
}
}
static async removeDisabledHost(host: Host) {
const ids = await this.disabledHosts.getValue();
const index = ids.indexOf(host.id);
if (index !== -1) {
ids.splice(index, 1);
await this.disabledHosts.setValue(ids);
}
}
static async getDisabledHosts() {
return await this.disabledHosts.getValue();
}
static async getAllHostsDisabled() {
return await this.allHostsDisabled.getValue();
}
static async setAllHostsDisabled(disabled: boolean) {
await this.allHostsDisabled.setValue(disabled);
}
/* tmp */
private static temporaryHostDomain = storage.defineItem<Record<string, string>>('local:temporaryHostDomain', {
fallback: {}
});
static async addTemporaryHostDomain(host: Host, domain: string) {
const temporaryHostDomains = await this.temporaryHostDomain.getValue();
temporaryHostDomains[domain] = host.id;
await this.temporaryHostDomain.setValue(temporaryHostDomains);
console.log(await this.temporaryHostDomain.getValue());
}
static async checkTemporaryHostDomain(domain: string) {
const temporaryHostDomains = await this.temporaryHostDomain.getValue();
const hostId = temporaryHostDomains[domain];
return hostId ? (hosts.find((host) => host.id === hostId) ?? null) : null;
}
}
export class FF2MPVSettings {
private static ff2mpvEnabled = storage.defineItem<boolean>('local:ff2mpv', { fallback: false });
static async getEnabled() {
return await this.ff2mpvEnabled.getValue();
}
static async setEnabled(enabled: boolean) {
console.log('set', enabled);
await this.ff2mpvEnabled.setValue(enabled);
}
}
export class UrlReferer {
private static temporaryUrlReferer = storage.defineItem<Record<string, string>>('local:temporaryUrlReferer', {
fallback: {}
});
static async addTemporary(hostname: string, referer: string) {
const tmpUrlReferer = await this.temporaryUrlReferer.getValue();
tmpUrlReferer[hostname] = referer;
await this.temporaryUrlReferer.setValue(tmpUrlReferer);
}
static async get(hostname: string) {
const tmpUrlReferer = await this.temporaryUrlReferer.getValue();
return tmpUrlReferer[hostname] ?? null;
}
}

View File

@@ -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"
}
}

View File

@@ -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()]
]

View File

@@ -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>

View File

@@ -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

View File

@@ -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')
})

View File

@@ -1,15 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2015",
"lib": [
"dom",
"es5",
"scripthost",
"es2015.collection"
]
},
"exclude": [
"node_modules"
],
}

92
src/utils/content.ts Normal file
View File

@@ -0,0 +1,92 @@
// 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 = (await runInPageContext(toExecute)) as string;
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 | null> {
// test that we are running with the allow-scripts permission
try {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
window.sessionStorage;
} catch {
return null;
}
// returned value container
const resultMessageId = crypto.randomUUID();
// prepare script container
const scriptElm = document.createElement('script');
scriptElm.setAttribute('type', 'application/javascript');
// inject the script
scriptElm.textContent = `
(
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, '*');
}
)();
`;
// 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;
}

6
src/utils/extract.ts Normal file
View File

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

6
tsconfig.json Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": "./.wxt/tsconfig.json",
"compilerOptions": {
"useDefineForClassFields": true
}
}

1
wxt-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="svelte" />

58
wxt.config.ts Normal file
View File

@@ -0,0 +1,58 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'wxt';
import { version } from './package.json';
export default defineConfig({
srcDir: 'src',
modules: ['@wxt-dev/module-svelte'],
manifest: ({ browser, manifestVersion }) => ({
name: manifestVersion === 2 ? 'Stream Bypass' : 'Stream Bypass Lite',
icons: {
16: 'icon/stream-bypass@16px.png',
32: 'icon/stream-bypass@32px.png',
48: 'icon/stream-bypass@48px.png',
128: 'icon/stream-bypass@128px.png'
},
browser_specific_settings:
browser === 'firefox'
? {
gecko: {
id: '{55dd42e8-3dd9-455a-b4fe-86664881b10c}',
data_collection_permissions: {
required: ['none']
}
},
gecko_android: {}
}
: undefined,
permissions: ['storage', ...(manifestVersion === 2 ? ['webRequest', 'webRequestBlocking', '<all_urls>'] : [])],
optional_permissions: ['nativeMessaging'],
web_accessible_resources: [
{
resources: ['player.html'],
// TODO: Replace this with all hosts domains if target manifest version is 3.
// This isn't working atm because importing '@/lib/host' fails. Ahhhh I love the whole fucking JS/TS
// environment. Maybe I'm also overlooking something and the fix is easy, but that itsn't working out of the
// box is once again terrible DX
matches: ['<all_urls>']
}
]
}),
zip: {
artifactTemplate: '{{name}}-{{version}}-{{manifestVersion}}.zip'
},
vite: () => ({
define: {
'import.meta.env.VERSION': JSON.stringify(version)
},
plugins: [tailwindcss()]
}),
svelte: {
vite: {
preprocess: vitePreprocess({})
}
}
});