48 Commits

Author SHA1 Message Date
48dc192964 Update version 2023-08-08 14:25:48 +02:00
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
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
7d8d8b6614 Merge pull request #9 from bachig26/master
Update matches.ts
2023-01-19 13:27:04 +01:00
b4052834f2 Update matches.ts
- add dood.re
2023-01-19 05:02:40 +01:00
9900c33243 Merge pull request #7 from bachig26/patch-1
added more domains
2023-01-11 08:10:40 +01:00
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
19 changed files with 527 additions and 251 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 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

View File

@ -27,7 +27,9 @@ A multi-browser addon / extension for multiple streaming providers which redirec
<a href="#-supported-sites">Supported Sites 📜</a>
<a href="#%EF%B8%8F-building">Building </a>
<a href="#%EF%B8%8F-building">Building 🛠</a>
<a href="#%EF%B8%8F-settings">Settings ⚙️</a>
<a href="#-license">License ⚖</a>
</p>
@ -68,21 +70,25 @@ Install the addon directly from the [firefox addon store](https://addons.mozilla
## 📜 Supported sites
| Site | Supported | Note |
|-----------------------------------------------------------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | | URL can be extracted but not played (see [here](https://github.com/ByteDream/stream-bypass/blob/674527ed9c1aa35a30fed135c46017571dd3ce31/src/match/matches.ts#L18) for more information) |
| [evoload.io](https://evoload.io) | ✔ | |
|-----------------------------------------------------------------------|-----------|--------------------------------------------------------------------------------------------------------------|
| [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) | | URL can be extracted but not. Probably the same issue as with doodstream |
| [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 |
| [vidlox.me](https://vidlox.me) | ⚠ | Website down / Timeout |
| [vidoza.net](https://vidoza.net) | ✔ | |
| [vivo.sx](https://vivo.sx) | ⚠️ | Website down / Timeout |
| [voe.sx](https://voe.sx) / [voeunblk.com](https://voeunblk.com) | ✔ | |
| [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) | ✔ | |
- ✔️: 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.
@ -90,7 +96,16 @@ Install the addon directly from the [firefox addon store](https://addons.mozilla
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
<details>
<summary>Hall of dead sites</summary>
<ul>
<li><a href="https://evoload.io">evoload.io</a> - Down</li>
<li><a href="https://vidlox.me">vidlox.me</a> - Reachable but empty</li>
<li><a href="https://vivo.sx">vivo.sx</a> - Down</li>
</ul>
</details>
## 🛠️ Building
If you want to build the addon from source and not using the [installation](#installation) way, follow the instructions.
@ -119,6 +134,36 @@ When using firefox, use the following:
3. Under `Temporary Extensions`, click `Load Temporary Add-on`.
4. Choose any file in the directory where the compiled sources are.
## ⚙️ Settings
### <ins>ff2mpv: use mpv to directly play streams</ins>
ff2mpv is located at this repository: https://github.com/woodruffw/ff2mpv
Steps to get it set up:
- In the [Usage](https://github.com/woodruffw/ff2mpv#usage) section of the ff2mpv repository pick the installation instruction for your operating system (Linux/Windows/macOS; you do not need the browser addon).
- Scroll down to `Install manually`
- Follow instructions for Firefox/Chrome
- Edit the `ff2mpv.json` you created:
- Firefox: Add `{55dd42e8-3dd9-455a-b4fe-86664881b10c}` to `allowed_extensions` ->
```
"allowed_extensions": [
"ff2mpv@yossarian.net",
"{55dd42e8-3dd9-455a-b4fe-86664881b10c}"
]
```
- Chrome/Chromium:
- Go To: Settings -> Extensions
- Click on `Details` of the Stream Bypass extension and copy the ID
- Add `chrome-extension://your-id-here/` to `allowed_origins` ->
```
"allowed_origins": [
"chrome-extension://ephjcajbkgplkjmelpglennepbpmdpjg/",
"chrome-extension://your-id-her/"
]
```
## ⚖ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.

View File

@ -1,6 +1,6 @@
{
"name": "stream-bypass",
"version": "2.1.0",
"version": "2.2.0",
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
"main": "src/index.ts",
"scripts": {
@ -20,22 +20,21 @@
},
"homepage": "https://github.com/ByteDream/stream-bypass#readme",
"devDependencies": {
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-replace": "^4.0.0",
"@rollup/plugin-typescript": "^8.3.3",
"@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.1",
"@types/chrome": "^0.0.190",
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.1.0",
"@types/chrome": "^0.0.234",
"@types/node-sass": "^4.11.2",
"@types/yazl": "^2.4.2",
"@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.1",
"eslint": "^8.17.0",
"hls.js": "^1.1.5",
"node-sass": "^7.0.1",
"node-sass-package-importer": "^5.3.2",
"rollup": "^2.75.6",
"rollup": "^3.21.0",
"sass": "^1.62.1",
"tippy.js": "^6.3.7",
"typescript": "^4.7.3",
"typescript": "^5.0.4",
"web-ext": "^7.0.0"
}
}

View File

@ -1,7 +1,14 @@
import {getMatch} from "./match/match";
import {storageGet, storageSet} from "./store/store";
import {storageDelete, storageGet, storageSet} from "./store/store";
import {Match} from "./match/matches";
chrome.runtime.onMessage.addListener((message, sender) => {
if (message.action == "ff2mpv") {
chrome.runtime.sendNativeMessage("ff2mpv", {url: message.url})
.catch((error) => {console.error(error)})
}
})
chrome.webRequest.onBeforeRedirect.addListener(async details => {
// check if redirects origins from a previous redirect
if (await storageGet('redirect') === undefined) {
@ -9,6 +16,8 @@ chrome.webRequest.onBeforeRedirect.addListener(async details => {
if ((match = await getMatch(new URL(details.url).host)) !== undefined) {
await storageSet('redirect', match.id)
}
} else {
await storageDelete('redirect')
}
}, {
urls: ['<all_urls>'],

View File

@ -1,26 +1,42 @@
import {getMatch} from "./match/match";
import {storageDelete, storageGet} from "./store/store";
import {storageDelete, storageGet, getSetting} from "./store/store";
import {Match, matches} from "./match/matches";
async function main() {
let match: Match;
let redirect = false;
if ((match = await getMatch(window.location.host)) === undefined) {
let id: string
if ((id = await storageGet('redirect')) !== undefined) {
redirect = true
match = matches.find(m => m.id === id)
await storageDelete('redirect')
} else {
return
}
}
const re = document.body.innerHTML.match(match.regex)
if (re === null) {
return
}
if (re === null) return
if (redirect) await storageDelete('redirect')
const url = await match.match(re)
location.assign(chrome.runtime.getURL(`ui/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}`))
if (await getSetting("ff2mpv")) {
chrome.runtime.sendMessage({action: "ff2mpv", url: url})
}
if (match.replace && !url.includes('.m3u8')) {
const player = document.createElement('video')
player.style.width = '100%'
player.style.height = '100%'
player.controls = true
player.src = url
document.body.innerHTML = ''
document.body.append(player)
} else {
window.location.assign(chrome.runtime.getURL(`ui/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}&domain=${window.location.host}`))
}
}
main()

View File

@ -3,7 +3,7 @@
"name": "Stream Bypass",
"author": "ByteDream",
"description": "A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.",
"version": "2.1.0",
"version": "2.2.0",
"homepage_url": "https://github.com/ByteDream/stream-bypass",
"browser_specific_settings": {
"gecko": {
@ -30,6 +30,7 @@
"permissions": [
"storage",
"webRequest",
"nativeMessaging",
"<all_urls>"
],
"web_accessible_resources": [

View File

@ -1,5 +1,7 @@
import {unPack} from "./unpack";
export enum Reliability {
HIGH = 1,
HIGH,
NORMAL,
LOW,
}
@ -9,24 +11,31 @@ export abstract class Match {
id: string
reliability: Reliability
domains: string[]
replace?: boolean
regex: RegExp
abstract match(match: RegExpMatchArray): Promise<string>
notice?: string
}
// DOES NOT WORK.
// The url can be extracted (sometimes??? wtf) without problems but to receive the actual video, custom request
// headers must be set. And because the javascript and browser ecosystem is so fucked up, there is no good way to
// do this with media which can be natively played with the browser, like here.
class Doodstream implements Match {
name = 'Doodstream'
id = 'doodstream'
reliability = Reliability.HIGH
reliability = Reliability.NORMAL
domains = [
'doodstream.com',
'dood.pm'
'dood.pm',
'dood.ws',
'dood.wf',
'dood.cx',
'dood.sh',
'dood.watch',
'dood.to',
'dood.so',
'dood.la',
'dood.re'
]
replace = true
regex = new RegExp(/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s)
async match(match: RegExpMatchArray): Promise<string> {
@ -41,27 +50,20 @@ class Doodstream implements Match {
}
}
class Evoload implements Match {
name = 'Evoload'
id = 'evoload'
reliability = Reliability.NORMAL
class Filemoon implements Match {
name = 'Filemoon'
id = 'filemoon'
reliability = Reliability.HIGH
domains = [
'evoload.io'
'filemoon.sx',
'filemoon.in'
]
regex = new RegExp(/.*/gm)
regex = new RegExp(/eval\(function\(p,a,c,k,e,d\).*?(?=\<\/script\>)/gms)
async match(match: RegExpMatchArray): Promise<string> {
const code = window.location.pathname.split('/').slice(-1)[0]
const response = await fetch('https://evoload.io/SecurePlayer', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({code: code})
})
const json = await response.json()
return json['stream']['src']
let unpacked = await unPack(match[0])
let url = unpacked.match(/(?<=file:").*(?=")/)[0]
return url
}
}
@ -70,28 +72,37 @@ class Mixdrop implements Match {
id = 'mixdrop'
reliability = Reliability.HIGH
domains = [
'mixdrop.co'
'mixdrop.co',
'mixdrop.to',
'mixdrop.ch',
'mixdrop.bz',
'mixdrop.gl'
]
regex = new RegExp(/(?<=\|)\w{2,}/gm)
regex = new RegExp(/eval\(function\(p,a,c,k,e,d\).*?(?=\<\/script\>)/gms)
async match(match: RegExpMatchArray): Promise<string> {
return `https://a-${match[1]}.${match[4]}.${match[5]}/v/${match[2]}.${match[6]}?s=${match[12]}&e=${match[13]}`
let unpacked = await unPack(match[0])
let url = unpacked.match(/(?<=MDCore.wurl=").*(?=")/)[0]
return `https:${url}`
}
}
/*class Mp4Upload implements Match {
class Mp4Upload implements Match {
name = 'Mp4Upload'
id = 'mp4upload'
reliability = Reliability.LOW
reliability = Reliability.HIGH
domains = [
'mp4upload.com'
]
regex = new RegExp(/(?<=\|)\w{2,}/gm)
replace = true
regex = new RegExp(/eval\(function\(p,a,c,k,e,d\).*?(?=\<\/script\>)/gms)
async match(match: RegExpMatchArray): Promise<string> {
return `https://${match[34]}.mp4upload.com:${match[89]}/d/${match[88]}/video.mp4`
let unpacked = await unPack(match[0])
let url = unpacked.match(/(?<=player.src\(").*(?=")/)[0]
return url
}
}*/
}
class Newgrounds implements Match {
name = 'Newgrounds'
@ -121,17 +132,17 @@ class Streamtape implements Match {
domains = [
'streamtape.com'
]
regex = new RegExp(/\/get_video\S*(?=')/gm)
regex = new RegExp(/id=.*(?=')/gm)
async match(match: RegExpMatchArray): Promise<string> {
return `https://streamtape.com${match[0]}`
return `https://streamtape.com/get_video?${match.reverse()[0]}`
}
}
class Streamzz implements Match {
name = 'Streamzz'
id = 'streamzz'
reliability = Reliability.NORMAL
reliability = Reliability.LOW
domains = [
'streamzz.to',
'streamz.ws'
@ -146,28 +157,16 @@ class Streamzz implements Match {
class Upstream implements Match {
name = 'Upstream'
id = 'upstream'
reliability = Reliability.NORMAL
reliability = Reliability.HIGH
domains = [
'upstream.to'
]
regex = new RegExp(/(?<=\|)\w{2,}/gm)
regex = new RegExp(/eval\(function\(p,a,c,k,e,d\).*?(?=\<\/script\>)/gms)
async match(match: RegExpMatchArray): Promise<string> {
return `https://${match[49]}.upstreamcdn.co/hls/${match[148]}/master.m3u8`
}
}
class Vidlox implements Match {
name = 'Vidlox'
id = 'vidlox'
reliability = Reliability.LOW
domains = [
'vidlox.me'
]
regex = new RegExp(/(?<=\[")\S+?(?=")/gm)
async match(match: RegExpMatchArray): Promise<string> {
return match[0]
let unpacked = await unPack(match[0])
let url = unpacked.match(/(?<=file:").*(?=")/)[0]
return url
}
}
@ -185,34 +184,6 @@ class Vidoza implements Match {
}
}
class Vivo implements Match {
name = 'Vivo'
id = 'vivo'
reliability = Reliability.LOW
domains = [
'vivo.sx'
]
regex = new RegExp(/(?<=source:\s')(\S+)(?=')/gms)
async match(match: RegExpMatchArray): Promise<string> {
return this.rot47(decodeURIComponent(match[0]))
}
// 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 Voe implements Match {
name = 'Voe'
id = 'voe'
@ -220,7 +191,7 @@ class Voe implements Match {
domains = [
'voe.sx'
]
regex = new RegExp(/https?:\/\/\S*m3u8(?=")/gm)
regex = new RegExp(/https?:\/\/\S*m3u8.+(?=')/gm)
async match(match: RegExpMatchArray): Promise<string> {
return match[0]
@ -241,16 +212,83 @@ class Vupload implements Match {
}
}
class Kwik implements Match {
name = 'Kwik'
id = 'kwik'
reliability = Reliability.HIGH
domains = [
'kwik.cx'
]
regex = new RegExp(/eval\(function\(p,a,c,k,e,d\).*?(?=\<\/script\>)/gms)
async match(match: RegExpMatchArray): Promise<string> {
console.log(match[0]);
let unpacked = await unPack(match[0])
let url = unpacked.match(/(?<=source=').*(?=')/)[0]
return url
}
}
class DropLoad implements Match {
name = 'Dropload'
id = 'dropload'
reliability = Reliability.HIGH
domains = [
'dropload.io'
]
regex = new RegExp(/eval\(function\(p,a,c,k,e,d\).*?(?=\<\/script\>)/gms)
async match(match: RegExpMatchArray): Promise<string> {
let unpacked = await unPack(match[0])
let url = unpacked.match(/(?<=file:").*(?=")/)[0]
return url
}
}
class SuperVideo implements Match {
name = 'Supervideo'
id = 'supervideo'
reliability = Reliability.HIGH
domains = [
'supervideo.tv'
]
regex = new RegExp(/eval\(function\(p,a,c,k,e,d\).*?(?=\<\/script\>)/gms)
async match(match: RegExpMatchArray): Promise<string> {
let unpacked = await unPack(match[0])
let url = unpacked.match(/(?<=file:").*(?=")/)[0]
return url
}
}
class GoodStream implements Match {
name = 'Goodstream'
id = 'goodstream'
reliability = Reliability.NORMAL
domains = [
'goodstream.uno'
]
regex = new RegExp(/(?<=file:\s+").*(?=")/g)
async match(match: RegExpMatchArray): Promise<string> {
return match[0]
}
}
export const matches = [
new Evoload(),
new Doodstream(),
new Filemoon(),
new Mixdrop(),
new Mp4Upload(),
new Newgrounds(),
new Streamtape(),
new Streamzz(),
new Upstream(),
new Vidlox(),
new Vidoza(),
new Vivo(),
new Voe(),
new Vupload()
new Vupload(),
new Kwik(),
new DropLoad(),
new SuperVideo(),
new GoodStream()
]

84
src/match/unpack.ts Normal file
View File

@ -0,0 +1,84 @@
function runInPageContext(to_execute: string) {
// Adapted from: https://github.com/arikw/extension-page-context
const doc = document;
// test that we are running with the allow-scripts permission
try { window.sessionStorage; } catch (ignore) { return null; }
// returned value container
const resultMessageId = parseInt('' + Math.floor((Math.random() * 100) + 1) + ((new Date()).getTime()));
// prepare script container
let scriptElm = doc.createElement('script');
scriptElm.setAttribute("type", "application/javascript");
const code = `
(
async function () {
const response = {
id: ${resultMessageId}
};
try {
response.result = JSON.stringify(await (${to_execute})() ); // run script
} catch(err) {
response.error = JSON.stringify(err);
}
window.postMessage(response, '*');
}
)();
`;
// inject the script
scriptElm.textContent = code;
// run the script
doc.documentElement.appendChild(scriptElm);
// clean up script element
scriptElm.remove();
// create a "flat" promise
let resolve, reject;
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
// resolve on result
function onResult(event) {
const data = Object(event.data);
if (data.id === resultMessageId) {
window.removeEventListener('message', onResult);
if (data.error !== undefined) {
return reject(JSON.parse(data.error));
}
return resolve((data.result !== undefined) ? JSON.parse(data.result) : undefined);
}
}
window.addEventListener('message', onResult);
return promise;
}
export const unPack = async (packed: String): Promise<string> => {
// Adapted from http://matthewfl.com/unPacker.html by matthew@matthewfl.com
let context = `
{
eval: function (c) {
packed = c;
},
window: {},
document: {}
}
`
const to_execute = `function() { let packed = ""; with(${context}) { ` + packed + '}; return packed; }'
const res = await runInPageContext(to_execute);
return (res+"").replace(/;/g, ";\n").replace(/{/g, "\n{\n").replace(/}/g, "\n}\n").replace(/\n;\n/g, ";\n").replace(/\n\\n/g, "\n");
}

27
src/store/settings.ts Normal file
View File

@ -0,0 +1,27 @@
import { storageSet, storageGet } from "./store"
export class Setting {
name: string
info_url?: string
constructor(name: string, info_url?: string) {
this.name = name
this.info_url = info_url
}
async enable() {
await storageSet(this.name, true)
}
async disable() {
await storageSet(this.name, false)
}
async get_status() {
return await storageGet(this.name)
}
}
export const Settings = [
new Setting("ff2mpv", "https://github.com/ByteDream/stream-bypass/tree/master#ff2mpv-use-mpv-to-directly-play-streams")
]

View File

@ -66,3 +66,5 @@ export async function disable(match: Match) {
await storageSet('disabled', disabled)
}
}
export const getSetting = storageGet

View File

@ -2,14 +2,16 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HLS</title>
<title>Stream Bypass</title>
<link rel="stylesheet" href="player.css">
<script src="player.js" defer></script>
</head>
<body>
<video id="video"></video>
<div id="message-container">
<p id="message" hidden></p>
<p id="message"></p>
<br>
<p>Open a new issue <a href="https://github.com/ByteDream/stream-bypass/issues">here</a></p>
</div>
</body>
</html>

View File

@ -1,22 +0,0 @@
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
height: 100%
#message
color: white
text-align: center

34
src/ui/player/player.scss Normal file
View File

@ -0,0 +1,34 @@
body {
background-color: #131313;
}
html, body, video {
width: 100%;
height: 100%;
margin: 0;
}
video {
position: absolute;
top: 0;
left: 0;
}
#message-container {
visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
color: white;
text-align: center;
height: 100%;
& * {
visibility: inherit;
}
& a, & a:visited {
color: red
}
}

View File

@ -1,20 +1,20 @@
import {Match, matches, Reliability} from "../../match/matches";
import {matches} from "../../match/matches";
// @ts-ignore
import Hls from "hls.js";
function show_message(message: string) {
document.getElementById('message').innerText = message
document.getElementById('message-container').hidden = false
document.getElementById('message-container').style.visibility = 'visible'
document.getElementById('video').hidden = true
}
async function play_native(url: string, match: Match) {
async function play_native(url: string) {
const video = document.getElementById('video') as HTMLVideoElement
video.controls = true
video.src = url
}
async function play_hls(url: string, match: Match) {
async function play_hls(url: string) {
const video = document.getElementById('video') as HTMLVideoElement
video.controls = true
@ -26,36 +26,6 @@ async function play_hls(url: string, match: Match) {
})
hls.loadSource(url)
hls.attachMedia(video)
const loaded = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(false)
}, match.reliability * 3000)
hls.on(Hls.Events.MANIFEST_PARSED, () => {
resolve(true)
})
})
if (!loaded) {
let message: string
switch (match.reliability) {
case Reliability.LOW:
message = `The reliability for this domain is low, errors like this are common.
Try to choose another streaming provider (if existent) or deactivate the addon for this provider (${match.name}) and try again`
break
case Reliability.NORMAL:
message = `The reliability for this domain is normal, errors like this can occur but are not very common. Try to refresh the page`
break
case Reliability.HIGH:
message = `The reliability for this domains is high, errors like this are very unlikely to happen.
Try to refresh the page and if the error still exists you might want to open a new issue <a href="https://github.com/ByteDream/stream-bypass/issues/new">here</a>.
When you're using <a href="https://www.torproject.org/">Tor</a> such errors have a slight chance to occur more often,
so if this is the case just try to reload the page and see if it's working then`
break
}
show_message(`Could not load HLS video. ${message}`)
}
} else {
show_message('Failed to play m3u8 video (hls is not supported). Try again or create a new issue <a href="https://github.com/ByteDream/stream-bypass/issues/new">here</a>')
}
@ -64,16 +34,17 @@ async function play_hls(url: string, match: Match) {
async function main() {
const urlQuery = new URLSearchParams(window.location.search)
const id = urlQuery.get('id')
const url = urlQuery.get('url')
const url = decodeURIComponent(urlQuery.get('url'))
const domain = urlQuery.get('domain')
const match = matches.find((m) => m.id === id)
if (match === undefined) {
show_message(`Invalid id: ${id}. Please report this <a href="https://github.com/ByteDream/stream-bypass/issues/new">here</a>`)
show_message(`Invalid id: ${id}. Please report this <a href="https://github.com/ByteDream/stream-bypass/issues">here</a>`)
return
}
document.title = match.name
document.title = `Stream Bypass (${domain})`
url.endsWith('.m3u8') ? await play_hls(url, match) : await play_native(url, match)
new URL(url).pathname.endsWith('.m3u8') ? await play_hls(url) : await play_native(url)
}
main()

View File

@ -13,11 +13,19 @@
<a>On</a>
<a>Off</a>
</div>
</div>
<hr>
<p>Hosters</p>
<table id="sub-container">
</table>
<a id="error" href="https://github.com/ByteDream/stream-bypass/issues/new">Something does not work</a>
<hr class="upper-hr">
<p>Settings</p>
<table id="settings-container">
</table>
</div>
<hr>
<table id="sub-container">
</table>
<a id="error" href="https://github.com/ByteDream/stream-bypass/issues">Something does not work</a>
</div>
</body>
</html>

View File

@ -1,56 +0,0 @@
body
background-color: #2b2a33
font-weight: bold
max-height: 500px
overflow-x: hidden
overflow-y: auto
a, p
color: white
font-size: 16px
margin: 5px 0
cursor: default
a
border: 1px solid #281515
cursor: pointer
font-weight: normal
padding: 5px 8px
&.active
background-color: rgba(255, 65, 65, 0.74)
cursor: default
&.disabled
background-color: grey
cursor: not-allowed
&#error
border: none
display: block
font-weight: lighter
font-size: 0.8rem
text-align: center
padding: 10px 0 5px 0
hr
margin: 3px 0
#all
display: flex
justify-content: center
margin: 10px 0
.low-reliability
text-decoration: underline
text-decoration-color: rgb(255, 0, 0)
.normal-reliability
text-decoration: underline
text-decoration-color: yellow
.high-reliability
text-decoration: underline
text-decoration-color: #00ff00

76
src/ui/popup/popup.scss Normal file
View File

@ -0,0 +1,76 @@
body {
background-color: #2b2a33;
font-weight: bold;
max-height: 500px;
overflow-x: hidden;
overflow-y: auto;
}
a, p {
color: white;
font-size: 16px;
margin: 5px 0;
cursor: default;
}
a {
border: 1px solid #281515;
cursor: pointer;
font-weight: normal;
padding: 5px 8px;
&.active {
background-color: rgba(255, 65, 65, 0.74);
cursor: default;
}
&.disabled {
background-color: grey;
cursor: not-allowed;
}
&#error {
border: none;
display: block;
font-weight: lighter;
font-size: 0.8rem;
text-align: center;
padding: 10px 0 5px 0;
}
}
hr {
margin: 3px 0;
&.upper-hr {
width: 100%;
}
}
#all {
display: flex;
align-items: center;
flex-direction: column;
margin: 10px 0
}
.buttons {
margin-bottom: 10px;
}
.low-reliability {
text-decoration: underline;
text-decoration-color: rgb(255, 0, 0);
}
.normal-reliability {
text-decoration: underline;
text-decoration-color: yellow;
}
.high-reliability {
text-decoration: underline;
text-decoration-color: #00ff00;
}

View File

@ -1,5 +1,6 @@
import {getDisabled, disable, enable, getAllDisabled, enableAll, disableAll} from "../../store/store";
import {matches, Reliability} from "../../match/matches";
import { Settings, Setting } from "../../store/settings";
async function main() {
const disabled = await getDisabled()
@ -52,6 +53,51 @@ async function main() {
subContainer.append(row)
}
const settingsContainer = document.getElementById("settings-container")
for (const s of Settings) {
const row = document.createElement('tr')
const name = document.createElement('td')
const nameValue = document.createElement('p')
nameValue.innerText = s.name
const buttons = document.createElement('td')
buttons.classList.add('buttons')
const on = document.createElement('a')
on.innerText = 'On'
const off = document.createElement('a')
off.innerText = 'Off'
const info = document.createElement('a')
info.target = "_blank"
info.href = s.info_url
info.innerText = "🛈"
await s.get_status() ? on.classList.add('active') : off.classList.add('active')
on.onclick = async function () {
if (!on.classList.contains('disabled')) {
await s.enable()
on.classList.add('active')
off.classList.remove('active')
}
}
off.onclick = async function () {
if (!off.classList.contains('disabled')) {
await s.disable()
on.classList.remove('active')
off.classList.add('active')
}
}
name.append(nameValue)
buttons.append(on, off)
if (s.info_url) {
buttons.append(info)
}
row.append(name, buttons)
settingsContainer.append(row)
}
const allOnButton = document.getElementById('all').getElementsByTagName('a')[0]
const allOffButton = document.getElementById('all').getElementsByTagName('a')[1]

View File

@ -5,8 +5,7 @@ const rollupPluginCommonJs = require('@rollup/plugin-commonjs')
const rollupPluginNodeResolve = require('@rollup/plugin-node-resolve').default
const rollupPluginReplace = require('@rollup/plugin-replace')
const rollupPluginTypescript = require('@rollup/plugin-typescript')
const sass = require('node-sass')
const sassPluginNodeImport = require('node-sass-package-importer')
const sass = require('sass')
const typescript = require('typescript')
async function buildManifest() {
@ -47,15 +46,12 @@ async function buildHtml() {
async function buildCss() {
const files = {
'src/ui/popup/popup.sass': 'build/ui/popup/popup.css',
'src/ui/player/player.sass': 'build/ui/player/player.css'
'src/ui/popup/popup.scss': 'build/ui/popup/popup.css',
'src/ui/player/player.scss': 'build/ui/player/player.css'
}
for (const [src, dst] of Object.entries(files)) {
const compiled = sass.renderSync({
file: src,
importer: sassPluginNodeImport()
})
const compiled = await sass.compileAsync(src)
fs.mkdirSync(path.dirname(dst), {recursive: true})
fs.writeFileSync(dst, compiled.css)
}