mirror of
https://github.com/bytedream/stream-bypass.git
synced 2025-06-27 10:30:31 +02:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
e027c2e09e | |||
f9a0656d4d | |||
382d8b1268 | |||
5b8639ce6a | |||
841c824590 | |||
2055a3ea81 | |||
0262d1853c | |||
81da6600e6 | |||
817f5b82f9 | |||
9a17fb0d9b | |||
17f8aab216 | |||
44d4c9cbcf | |||
b34531b982 | |||
e699d3885c | |||
396038a803 | |||
bd64d4ed0b | |||
a207c336b0 | |||
2460657f2a | |||
698ed5ac3c | |||
dc42220f09 | |||
e146649bbf | |||
424e34190c | |||
c5f4f8b246 | |||
e2b8d884af | |||
b07d0b4be6 | |||
2d0441997c | |||
53e040db46 | |||
6d1ff3fbea | |||
4cf76eb62a | |||
672b920f31 | |||
1e166b5ecc | |||
213b996755 | |||
de0d8d5a41 | |||
1f34f74e11 |
10
README.md
10
README.md
@ -27,7 +27,7 @@ A multi-browser addon / extension for multiple streaming providers which redirec
|
||||
•
|
||||
<a href="#-supported-sites">Supported Sites 📜</a>
|
||||
•
|
||||
<a href="#%EF%B8%8F-compiling">Compiling ⚙️</a>
|
||||
<a href="#%EF%B8%8F-building">Building ⚙️</a>
|
||||
•
|
||||
<a href="#-license">License ⚖</a>
|
||||
</p>
|
||||
@ -69,10 +69,10 @@ Install the addon directly from the [firefox addon store](https://addons.mozilla
|
||||
|
||||
| Site | Supported | Note |
|
||||
|-----------------------------------------------------------------------|-----------|--------------------------------------------------|
|
||||
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ❌ | Reverse engineering the site costs too much time |
|
||||
| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔️ | |
|
||||
| [evoload.io](https://evoload.io) | ✔️ | |
|
||||
| [mixdrop.co](https://mixdrop.co) | ✔ ️ | |
|
||||
| [mp4upload.com](https://mp4upload.com) | ❌ | URL can be extracted but not played |
|
||||
| [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) | ✔ | |
|
||||
@ -90,9 +90,9 @@ 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.
|
||||
|
||||
## ⚙️ Compiling
|
||||
## ⚙️ Building
|
||||
|
||||
If you want to compile the addon from source and not using the [installation](#installation) way, follow the instructions.
|
||||
If you want to build the addon from source and not using the [installation](#installation) way, follow the instructions.
|
||||
|
||||
Requirements:
|
||||
- `npm` installed.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stream-bypass",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.4",
|
||||
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
|
16
src/background.ts
Normal file
16
src/background.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {getMatch} from "./match/match";
|
||||
import {storageGet, storageSet} from "./store/store";
|
||||
import {Match} from "./match/matches";
|
||||
|
||||
chrome.webRequest.onBeforeRedirect.addListener(async details => {
|
||||
// check if redirects origins from a previous redirect
|
||||
if (await storageGet('redirect') === undefined) {
|
||||
let match: Match
|
||||
if ((match = await getMatch(new URL(details.url).host)) !== undefined) {
|
||||
await storageSet('redirect', match.id)
|
||||
}
|
||||
}
|
||||
}, {
|
||||
urls: ['<all_urls>'],
|
||||
types: ['main_frame', 'sub_frame']
|
||||
})
|
42
src/index.ts
42
src/index.ts
@ -1,23 +1,37 @@
|
||||
import {matches} from "./match/match";
|
||||
import {getAllDisabled, getDisabled} from "./store/store";
|
||||
import {getMatch} from "./match/match";
|
||||
import {storageDelete, storageGet} from "./store/store";
|
||||
import {Match, matches} from "./match/matches";
|
||||
|
||||
async function main() {
|
||||
if (await getAllDisabled()) {
|
||||
return
|
||||
let match: Match;
|
||||
let redirect = false;
|
||||
if ((match = await getMatch(window.location.host)) === undefined) {
|
||||
let id: string
|
||||
if ((id = await storageGet('redirect')) !== undefined) {
|
||||
redirect = true
|
||||
match = matches.find(m => m.id === id)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (const match of matches) {
|
||||
if (!match.domains.some((v) => window.location.host.indexOf(v) !== -1) || ((await getDisabled()).some((v) => v === match))) {
|
||||
continue
|
||||
}
|
||||
const re = document.body.innerHTML.match(match.regex)
|
||||
if (re === null) return
|
||||
if (redirect) await storageDelete('redirect')
|
||||
|
||||
const re = document.body.innerHTML.match(match.regex)
|
||||
if (re === null) {
|
||||
continue
|
||||
}
|
||||
const url = await match.match(re)
|
||||
|
||||
const url = await match.match(re)
|
||||
location.assign(chrome.runtime.getURL(`ui/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}`))
|
||||
if (match.replace && !url.endsWith('.m3u8')) {
|
||||
const player = document.createElement('video')
|
||||
player.style.width = '100%'
|
||||
player.style.height = '100%'
|
||||
player.controls = true
|
||||
player.src = url
|
||||
|
||||
document.body.innerHTML = ''
|
||||
document.body.append(player)
|
||||
} else {
|
||||
window.location.assign(chrome.runtime.getURL(`ui/player/player.html?id=${match.id}&url=${encodeURIComponent(url)}&domain=${window.location.host}`))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.0.0",
|
||||
"version": "2.1.3",
|
||||
"homepage_url": "https://github.com/ByteDream/stream-bypass",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
@ -14,19 +14,7 @@
|
||||
{
|
||||
"all_frames": true,
|
||||
"matches": [
|
||||
"*://*.evoload.io/*",
|
||||
"*://*.mixdrop.co/*",
|
||||
"*://*.newgrounds.com/*",
|
||||
"*://*.streamtape.com/*",
|
||||
"*://*.streamzz.to/*",
|
||||
"*://*.streamz.ws/*",
|
||||
"*://*.upstream.to/*",
|
||||
"*://*.vidlox.me/*",
|
||||
"*://*.vidoza.net/*",
|
||||
"*://*.vivo.sx/*",
|
||||
"*://*.voe.sx/*",
|
||||
"*://*.voeunblk.com/*",
|
||||
"*://*.vupload.com/*"
|
||||
"<all_urls>"
|
||||
],
|
||||
"js": [
|
||||
"index.js"
|
||||
@ -34,8 +22,18 @@
|
||||
"run_at": "document_end"
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background.js"
|
||||
]
|
||||
},
|
||||
"permissions": [
|
||||
"storage"
|
||||
"storage",
|
||||
"webRequest",
|
||||
"<all_urls>"
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
"ui/player/*"
|
||||
],
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
|
@ -1,231 +1,14 @@
|
||||
export enum Reliability {
|
||||
HIGH = 1,
|
||||
NORMAL,
|
||||
LOW,
|
||||
}
|
||||
import {Match, matches} from "./matches";
|
||||
import {getAllDisabled, getDisabled} from "../store/store";
|
||||
|
||||
export abstract class Match {
|
||||
name: string
|
||||
id: string
|
||||
reliability: Reliability
|
||||
domains: string[]
|
||||
regex: RegExp
|
||||
abstract match(match: RegExpMatchArray): Promise<string>
|
||||
|
||||
notice?: string
|
||||
}
|
||||
|
||||
class Evoload implements Match {
|
||||
name = 'Evoload'
|
||||
id = 'evoload'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'evoload.io'
|
||||
]
|
||||
regex = new RegExp(/.*/gm)
|
||||
|
||||
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']
|
||||
}
|
||||
}
|
||||
|
||||
class Mixdrop implements Match {
|
||||
name = 'Mixdrop'
|
||||
id = 'mixdrop'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'mixdrop.co'
|
||||
]
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
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]}`
|
||||
}
|
||||
}
|
||||
|
||||
/*class Mp4Upload implements Match {
|
||||
name = 'Mp4Upload'
|
||||
id = 'mp4upload'
|
||||
reliability = Reliability.LOW
|
||||
domains = [
|
||||
'mp4upload.com'
|
||||
]
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://${match[34]}.mp4upload.com:${match[89]}/d/${match[88]}/video.mp4`
|
||||
}
|
||||
}*/
|
||||
|
||||
class Newgrounds implements Match {
|
||||
name = 'Newgrounds'
|
||||
id = 'newgrounds'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'newgrounds.com'
|
||||
]
|
||||
regex = new RegExp(/.*/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
let id = window.location.pathname.split('/').slice(-1)[0]
|
||||
let response = await fetch(`https://www.newgrounds.com/portal/video/${id}`, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
let json = await response.json()
|
||||
return decodeURI(json['sources'][Object.keys(json['sources'])[0]][0]['src'])
|
||||
}
|
||||
}
|
||||
|
||||
class Streamtape implements Match {
|
||||
name = 'Streamtape'
|
||||
id = 'streamtape'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'streamtape.com'
|
||||
]
|
||||
regex = new RegExp(/id=\S*(?=')/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://streamtape.com/get_video?${match[0]}`
|
||||
}
|
||||
}
|
||||
|
||||
class Streamzz implements Match {
|
||||
name = 'Streamzz'
|
||||
id = 'streamzz'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'streamzz.to',
|
||||
'streamz.ws'
|
||||
]
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://get.${document.domain.split('.')[0]}.tw/getlink-${match.sort((a, b) => b.length - a.length)[0]}.dll`
|
||||
}
|
||||
}
|
||||
|
||||
class Upstream implements Match {
|
||||
name = 'Upstream'
|
||||
id = 'upstream'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'upstream.to'
|
||||
]
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://${match[49]}.upstreamcdn.co/hls/${match[148]}/master.m3u8`
|
||||
}
|
||||
}
|
||||
|
||||
class 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]
|
||||
}
|
||||
}
|
||||
|
||||
class Vidoza implements Match {
|
||||
name = 'Vidoza'
|
||||
id = 'vidoza'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'vidoza.net'
|
||||
]
|
||||
regex = new RegExp(/(?<=src:\s?").+?(?=")/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
class 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]))
|
||||
export async function getMatch(host: string): Promise<Match | undefined> {
|
||||
if (await getAllDisabled()) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
for (const match of matches) {
|
||||
if (match.domains.some(v => host.indexOf(v) !== -1) && !((await getDisabled()).some(v => v === match))) {
|
||||
return match
|
||||
}
|
||||
return s.join('')
|
||||
}
|
||||
}
|
||||
|
||||
class Voe implements Match {
|
||||
name = 'Voe'
|
||||
id = 'voe'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'voe.sx',
|
||||
'voeunblk.com'
|
||||
]
|
||||
regex = new RegExp(/https?:\/\/\S*m3u8(?=")/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
class Vupload implements Match {
|
||||
name = 'Vupload'
|
||||
id = 'vupload'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'vupload.com'
|
||||
]
|
||||
regex = new RegExp(/(?<=src:\s?").+?(?=")/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
export const matches = [
|
||||
new Evoload(),
|
||||
new Mixdrop(),
|
||||
new Newgrounds(),
|
||||
new Streamtape(),
|
||||
new Streamzz(),
|
||||
new Upstream(),
|
||||
new Vidlox(),
|
||||
new Vidoza(),
|
||||
new Vivo(),
|
||||
new Voe(),
|
||||
new Vupload()
|
||||
]
|
||||
|
267
src/match/matches.ts
Normal file
267
src/match/matches.ts
Normal file
@ -0,0 +1,267 @@
|
||||
export enum Reliability {
|
||||
HIGH = 1,
|
||||
NORMAL,
|
||||
LOW,
|
||||
}
|
||||
|
||||
export abstract class Match {
|
||||
name: string
|
||||
id: string
|
||||
reliability: Reliability
|
||||
domains: string[]
|
||||
replace?: boolean
|
||||
regex: RegExp
|
||||
abstract match(match: RegExpMatchArray): Promise<string>
|
||||
|
||||
notice?: string
|
||||
}
|
||||
|
||||
class Doodstream implements Match {
|
||||
name = 'Doodstream'
|
||||
id = 'doodstream'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'doodstream.com',
|
||||
'dood.pm',
|
||||
'dood.ws',
|
||||
'dood.wf'
|
||||
]
|
||||
replace = true
|
||||
regex = new RegExp(/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
const response = await fetch(`https://${window.location.host}${match[1]}`, {
|
||||
headers: {
|
||||
'Range': 'bytes=0-'
|
||||
},
|
||||
referrer: `https://${window.location.host}/e/${window.location.pathname.split('/').slice(-1)[0]}`,
|
||||
});
|
||||
|
||||
return `${await response.text()}1234567890${match[2]}${Date.now()}`
|
||||
}
|
||||
}
|
||||
|
||||
class Evoload implements Match {
|
||||
name = 'Evoload'
|
||||
id = 'evoload'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'evoload.io'
|
||||
]
|
||||
regex = new RegExp(/.*/gm)
|
||||
|
||||
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']
|
||||
}
|
||||
}
|
||||
|
||||
class Mixdrop implements Match {
|
||||
name = 'Mixdrop'
|
||||
id = 'mixdrop'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'mixdrop.co'
|
||||
]
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
const prefix = /(?<=\/\/)[a|s](?=-)/.exec(document.body.innerHTML)[0]
|
||||
const subdomain = match[1].length < match[2].length ? match[1] : match[2]
|
||||
const domain = match.slice().sort((a, b) => b.length - a.length).find(m => /^[a-z]+$/.test(m))
|
||||
const id = match[1].length > match[2].length ? match[1] : match[2]
|
||||
const tld = match.find(m => ['net', 'io', 'to', 'sx', 'com'].indexOf(m) !== -1)
|
||||
const s = match.slice().sort((a, b) => b.length - a.length).slice(1)[0]
|
||||
const e_t = match.find(m => m.length === 10 && !isNaN(parseInt(m)))
|
||||
|
||||
return `https://${prefix}-${subdomain}.${domain}.${tld}/v/${id}.mp4?s=${s}&e=${e_t}&_t=${e_t}`
|
||||
}
|
||||
}
|
||||
|
||||
class Mp4Upload implements Match {
|
||||
name = 'Mp4Upload'
|
||||
id = 'mp4upload'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'mp4upload.com'
|
||||
]
|
||||
replace = true
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://${match[34]}.mp4upload.com:${match[89]}/d/${match[88]}/video.mp4`
|
||||
}
|
||||
}
|
||||
|
||||
class Newgrounds implements Match {
|
||||
name = 'Newgrounds'
|
||||
id = 'newgrounds'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'newgrounds.com'
|
||||
]
|
||||
regex = new RegExp(/.*/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
let id = window.location.pathname.split('/').slice(-1)[0]
|
||||
let response = await fetch(`https://www.newgrounds.com/portal/video/${id}`, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
let json = await response.json()
|
||||
return decodeURI(json['sources'][Object.keys(json['sources'])[0]][0]['src'])
|
||||
}
|
||||
}
|
||||
|
||||
class Streamtape implements Match {
|
||||
name = 'Streamtape'
|
||||
id = 'streamtape'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'streamtape.com'
|
||||
]
|
||||
regex = new RegExp(/id=.*(?=')/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://streamtape.com/get_video?${match.reverse()[0]}`
|
||||
}
|
||||
}
|
||||
|
||||
class Streamzz implements Match {
|
||||
name = 'Streamzz'
|
||||
id = 'streamzz'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'streamzz.to',
|
||||
'streamz.ws'
|
||||
]
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://get.${document.domain.split('.')[0]}.tw/getlink-${match.sort((a, b) => b.length - a.length)[0]}.dll`
|
||||
}
|
||||
}
|
||||
|
||||
class Upstream implements Match {
|
||||
name = 'Upstream'
|
||||
id = 'upstream'
|
||||
reliability = Reliability.NORMAL
|
||||
domains = [
|
||||
'upstream.to'
|
||||
]
|
||||
regex = new RegExp(/(?<=\|)\w{2,}/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return `https://${match[49]}.upstreamcdn.co/hls/${match[148]}/master.m3u8`
|
||||
}
|
||||
}
|
||||
|
||||
class 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]
|
||||
}
|
||||
}
|
||||
|
||||
class Vidoza implements Match {
|
||||
name = 'Vidoza'
|
||||
id = 'vidoza'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'vidoza.net'
|
||||
]
|
||||
regex = new RegExp(/(?<=src:\s?").+?(?=")/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
class 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'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'voe.sx'
|
||||
]
|
||||
regex = new RegExp(/https?:\/\/\S*m3u8(?=")/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
class Vupload implements Match {
|
||||
name = 'Vupload'
|
||||
id = 'vupload'
|
||||
reliability = Reliability.HIGH
|
||||
domains = [
|
||||
'vupload.com'
|
||||
]
|
||||
regex = new RegExp(/(?<=src:\s?").+?(?=")/gm)
|
||||
|
||||
async match(match: RegExpMatchArray): Promise<string> {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
|
||||
export const matches = [
|
||||
new Doodstream(),
|
||||
new Evoload(),
|
||||
new Mixdrop(),
|
||||
new Mp4Upload(),
|
||||
new Newgrounds(),
|
||||
new Streamtape(),
|
||||
new Streamzz(),
|
||||
new Upstream(),
|
||||
new Vidlox(),
|
||||
new Vidoza(),
|
||||
new Vivo(),
|
||||
new Voe(),
|
||||
new Vupload()
|
||||
]
|
@ -1,6 +1,6 @@
|
||||
import {Match, matches} from "../match/match";
|
||||
import {Match, matches} from "../match/matches";
|
||||
|
||||
async function storageGet(key: string): Promise<any> {
|
||||
export async function storageGet(key: string): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get(key, (value) => {
|
||||
resolve(value[key])
|
||||
@ -8,12 +8,16 @@ async function storageGet(key: string): Promise<any> {
|
||||
})
|
||||
}
|
||||
|
||||
async function storageSet(key: string, value: any) {
|
||||
export async function storageSet(key: string, value: any) {
|
||||
const obj = {}
|
||||
obj[key] = value
|
||||
await chrome.storage.local.set(obj)
|
||||
}
|
||||
|
||||
export async function storageDelete(key: string) {
|
||||
await chrome.storage.local.remove(key)
|
||||
}
|
||||
|
||||
export async function getDisabled(): Promise<Match[]> {
|
||||
const localMatches = []
|
||||
|
||||
@ -29,7 +33,7 @@ export async function getDisabled(): Promise<Match[]> {
|
||||
|
||||
export async function getAllDisabled(): Promise<boolean> {
|
||||
const value = await storageGet('all')
|
||||
return value !== undefined ? value as unknown as boolean : false
|
||||
return value !== undefined ? String(value).toLowerCase() === 'true' : false
|
||||
}
|
||||
|
||||
export async function enableAll() {
|
||||
|
@ -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/new">here</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -12,11 +12,17 @@ video
|
||||
left: 0
|
||||
|
||||
#message-container
|
||||
visibility: hidden
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
flex-direction: column
|
||||
color: white
|
||||
text-align: center
|
||||
height: 100%
|
||||
|
||||
#message
|
||||
color: white
|
||||
text-align: center
|
||||
& *
|
||||
visibility: inherit
|
||||
|
||||
& a, & a:visited
|
||||
color: red
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {Match, matches, Reliability} from "../../match/match";
|
||||
import {Match, matches, Reliability} 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
|
||||
}
|
||||
|
||||
@ -49,12 +49,12 @@ async function play_hls(url: string, match: Match) {
|
||||
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,
|
||||
Try to refresh the page and if the error still exists you might want to open a new issue.
|
||||
When you're using Tor, 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}`)
|
||||
show_message(`Could not load 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>')
|
||||
@ -65,13 +65,14 @@ async function main() {
|
||||
const urlQuery = new URLSearchParams(window.location.search)
|
||||
const id = urlQuery.get('id')
|
||||
const url = 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>`)
|
||||
return
|
||||
}
|
||||
document.title = match.name
|
||||
document.title = `Stream Bypass (${domain})`
|
||||
|
||||
url.endsWith('.m3u8') ? await play_hls(url, match) : await play_native(url, match)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {getDisabled, disable, enable, getAllDisabled, enableAll, disableAll} from "../../store/store";
|
||||
import {matches, Reliability} from "../../match/match";
|
||||
import {matches, Reliability} from "../../match/matches";
|
||||
|
||||
async function main() {
|
||||
const disabled = await getDisabled()
|
||||
|
@ -9,59 +9,11 @@ const sass = require('node-sass')
|
||||
const sassPluginNodeImport = require('node-sass-package-importer')
|
||||
const typescript = require('typescript')
|
||||
|
||||
function getDomains() {
|
||||
// because nodejs is nodejs, the simple commented out code below cannot be used.
|
||||
// thus, the following bloated regexes must be used
|
||||
/*const manifestMatches = []
|
||||
for (const m of matches) {
|
||||
for (const domain of m.domains) {
|
||||
manifestMatches.push(`*://*.${domain}/*`)
|
||||
}
|
||||
}
|
||||
manifest['content_scripts']['matches'] = manifestMatches*/
|
||||
|
||||
let domains = []
|
||||
|
||||
const matchesRegex = new RegExp(/export\s+const\s+matches\s+=\s+(?<matches>\[.*?])/gms)
|
||||
const matchesClassesRegex = new RegExp(/(?<!\/\/\s*)new\s+(?<class>\w+)\(\)/gms)
|
||||
|
||||
const matchTs = fs.readFileSync('src/match/match.ts')
|
||||
const jsMatches = matchesRegex.exec(matchTs).groups.matches
|
||||
let m
|
||||
while ((m = matchesClassesRegex.exec(jsMatches))) {
|
||||
if (m.index === matchesClassesRegex.lastIndex) {
|
||||
matchesClassesRegex.lastIndex++
|
||||
}
|
||||
|
||||
if (m.groups.class !== undefined) {
|
||||
const classDomainsRegex = new RegExp('class\\s+' + m.groups.class + '.*?domains\\s*=\\s*(?<domains>\\[.*?])', 'gms')
|
||||
let mm
|
||||
while ((mm = classDomainsRegex.exec(matchTs))) {
|
||||
if (mm.index === classDomainsRegex.lastIndex) {
|
||||
classDomainsRegex.lastIndex++
|
||||
}
|
||||
|
||||
if (mm.groups.domains !== undefined) {
|
||||
const matches = []
|
||||
for (const domain of JSON.parse(mm.groups.domains.replace(/'/g, '"', -1))) {
|
||||
matches.push(domain)
|
||||
}
|
||||
domains = domains.concat(matches)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return domains
|
||||
}
|
||||
|
||||
async function buildManifest() {
|
||||
const manifest = JSON.parse(fs.readFileSync('src/manifest.json'))
|
||||
|
||||
manifest['version'] = process.env.npm_package_version
|
||||
|
||||
manifest['content_scripts'][0]['matches'] = getDomains().map((domain) => {return `*://*.${domain}/*`})
|
||||
|
||||
fs.writeFileSync('src/manifest.json', JSON.stringify(manifest, null, 2))
|
||||
}
|
||||
|
||||
@ -115,6 +67,7 @@ async function buildJs() {
|
||||
'src/ui/player/player.ts': 'build/ui/player/player.js',
|
||||
|
||||
'src/index.ts': 'build/index.js',
|
||||
'src/background.ts': 'build/background.js'
|
||||
}
|
||||
|
||||
for (const [src, dst] of Object.entries(files)) {
|
||||
|
Reference in New Issue
Block a user