28 Commits

Author SHA1 Message Date
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
13 changed files with 215 additions and 151 deletions

View File

@ -67,22 +67,22 @@ Install the addon directly from the [firefox addon store](https://addons.mozilla
## 📜 Supported sites ## 📜 Supported sites
| Site | Supported | Note | | 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) | | [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ✔️ | |
| [evoload.io](https://evoload.io) | ✔️ | | | [evoload.io](https://evoload.io) | ✔️ | |
| [mixdrop.co](https://mixdrop.co) | ✔ | | | [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) | ✔ | | | [newgrounds.com](https://newgrounds.com) | ✔ | |
| [streamtape.com](https://streamtape.com) | ✔ | | | [streamtape.com](https://streamtape.com) | ✔ | |
| [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | | | [streamzz.to](https://streamzz.to) / [streamz.ws](https://streamz.ws) | ✔ | |
| [upstream.to](https://upstream.to) | ✔ | | | [upstream.to](https://upstream.to) | ✔ | |
| [videovard.sx](https://videovard.sx) | ❌ | Reverse engineering the site costs too much time | | [videovard.sx](https://videovard.sx) | ❌ | Reverse engineering the site costs too much time |
| [vidlox.me](https://vidlox.me) | ⚠ | Website down / Timeout | | [vidlox.me](https://vidlox.me) | ⚠ | Website down / Timeout |
| [vidoza.net](https://vidoza.net) | ✔ | | | [vidoza.net](https://vidoza.net) | ✔ | |
| [vivo.sx](https://vivo.sx) | ⚠️ | Website down / Timeout | | [vivo.sx](https://vivo.sx) | ⚠️ | Website down / Timeout |
| [voe.sx](https://voe.sx) / [voeunblk.com](https://voeunblk.com) | ✔ | | | [voe.sx](https://voe.sx) / [voeunblk.com](https://voeunblk.com) | ✔ | |
| [vupload.com](https://vupload.com) | ✔ | | | [vupload.com](https://vupload.com) | ✔ | |
- ✔️: Everything ok. - ✔️: Everything ok.
- ⚠: Included in the addon but will probably not work. See `Note` in this case, an explanation why will stand there in the most cases. - ⚠: Included in the addon but will probably not work. See `Note` in this case, an explanation why will stand there in the most cases.

View File

@ -1,6 +1,6 @@
{ {
"name": "stream-bypass", "name": "stream-bypass",
"version": "2.1.0", "version": "2.1.5",
"description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video", "description": "Multi-browser addon for multiple streaming providers which redirects directly to the source video",
"main": "src/index.ts", "main": "src/index.ts",
"scripts": { "scripts": {
@ -21,12 +21,12 @@
"homepage": "https://github.com/ByteDream/stream-bypass#readme", "homepage": "https://github.com/ByteDream/stream-bypass#readme",
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-node-resolve": "^13.3.0", "@rollup/plugin-node-resolve": "^14.0.1",
"@rollup/plugin-replace": "^4.0.0", "@rollup/plugin-replace": "^4.0.0",
"@rollup/plugin-typescript": "^8.3.3", "@rollup/plugin-typescript": "^8.3.3",
"@typescript-eslint/eslint-plugin": "^5.27.1", "@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.1", "@typescript-eslint/parser": "^5.27.1",
"@types/chrome": "^0.0.190", "@types/chrome": "^0.0.196",
"@types/node-sass": "^4.11.2", "@types/node-sass": "^4.11.2",
"@types/yazl": "^2.4.2", "@types/yazl": "^2.4.2",
"eslint": "^8.17.0", "eslint": "^8.17.0",

View File

@ -1,5 +1,5 @@
import {getMatch} from "./match/match"; import {getMatch} from "./match/match";
import {storageGet, storageSet} from "./store/store"; import {storageDelete, storageGet, storageSet} from "./store/store";
import {Match} from "./match/matches"; import {Match} from "./match/matches";
chrome.webRequest.onBeforeRedirect.addListener(async details => { chrome.webRequest.onBeforeRedirect.addListener(async details => {
@ -9,6 +9,8 @@ chrome.webRequest.onBeforeRedirect.addListener(async details => {
if ((match = await getMatch(new URL(details.url).host)) !== undefined) { if ((match = await getMatch(new URL(details.url).host)) !== undefined) {
await storageSet('redirect', match.id) await storageSet('redirect', match.id)
} }
} else {
await storageDelete('redirect')
} }
}, { }, {
urls: ['<all_urls>'], urls: ['<all_urls>'],

View File

@ -4,23 +4,35 @@ import {Match, matches} from "./match/matches";
async function main() { async function main() {
let match: Match; let match: Match;
let redirect = false;
if ((match = await getMatch(window.location.host)) === undefined) { if ((match = await getMatch(window.location.host)) === undefined) {
let id: string let id: string
if ((id = await storageGet('redirect')) !== undefined) { if ((id = await storageGet('redirect')) !== undefined) {
redirect = true
match = matches.find(m => m.id === id) match = matches.find(m => m.id === id)
await storageDelete('redirect')
} else { } else {
return return
} }
} }
const re = document.body.innerHTML.match(match.regex) const re = document.body.innerHTML.match(match.regex)
if (re === null) { if (re === null) return
return if (redirect) await storageDelete('redirect')
}
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}`))
}
} }
main() main()

View File

@ -3,7 +3,7 @@
"name": "Stream Bypass", "name": "Stream Bypass",
"author": "ByteDream", "author": "ByteDream",
"description": "A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.", "description": "A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.",
"version": "2.1.0", "version": "2.1.5",
"homepage_url": "https://github.com/ByteDream/stream-bypass", "homepage_url": "https://github.com/ByteDream/stream-bypass",
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {

View File

@ -9,24 +9,24 @@ export abstract class Match {
id: string id: string
reliability: Reliability reliability: Reliability
domains: string[] domains: string[]
replace?: boolean
regex: RegExp regex: RegExp
abstract match(match: RegExpMatchArray): Promise<string> abstract match(match: RegExpMatchArray): Promise<string>
notice?: 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 { class Doodstream implements Match {
name = 'Doodstream' name = 'Doodstream'
id = 'doodstream' id = 'doodstream'
reliability = Reliability.HIGH reliability = Reliability.NORMAL
domains = [ domains = [
'doodstream.com', 'doodstream.com',
'dood.pm' 'dood.pm',
'dood.ws',
'dood.wf'
] ]
replace = true
regex = new RegExp(/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s) regex = new RegExp(/(\/pass_md5\/.*?)'.*(\?token=.*?expiry=)/s)
async match(match: RegExpMatchArray): Promise<string> { async match(match: RegExpMatchArray): Promise<string> {
@ -68,30 +68,39 @@ class Evoload implements Match {
class Mixdrop implements Match { class Mixdrop implements Match {
name = 'Mixdrop' name = 'Mixdrop'
id = 'mixdrop' id = 'mixdrop'
reliability = Reliability.HIGH reliability = Reliability.NORMAL
domains = [ domains = [
'mixdrop.co' 'mixdrop.co'
] ]
regex = new RegExp(/(?<=\|)\w{2,}/gm) regex = new RegExp(/(?<=\|)\w{2,}/gm)
async match(match: RegExpMatchArray): Promise<string> { 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]}` 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 { class Mp4Upload implements Match {
name = 'Mp4Upload' name = 'Mp4Upload'
id = 'mp4upload' id = 'mp4upload'
reliability = Reliability.LOW reliability = Reliability.NORMAL
domains = [ domains = [
'mp4upload.com' 'mp4upload.com'
] ]
replace = true
regex = new RegExp(/(?<=\|)\w{2,}/gm) regex = new RegExp(/(?<=\|)\w{2,}/gm)
async match(match: RegExpMatchArray): Promise<string> { async match(match: RegExpMatchArray): Promise<string> {
return `https://${match[34]}.mp4upload.com:${match[89]}/d/${match[88]}/video.mp4` return `https://${match[34]}.mp4upload.com:${match[89]}/d/${match[88]}/video.mp4`
} }
}*/ }
class Newgrounds implements Match { class Newgrounds implements Match {
name = 'Newgrounds' name = 'Newgrounds'
@ -121,17 +130,17 @@ class Streamtape implements Match {
domains = [ domains = [
'streamtape.com' 'streamtape.com'
] ]
regex = new RegExp(/\/get_video\S*(?=')/gm) regex = new RegExp(/id=.*(?=')/gm)
async match(match: RegExpMatchArray): Promise<string> { 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 { class Streamzz implements Match {
name = 'Streamzz' name = 'Streamzz'
id = 'streamzz' id = 'streamzz'
reliability = Reliability.NORMAL reliability = Reliability.LOW
domains = [ domains = [
'streamzz.to', 'streamzz.to',
'streamz.ws' 'streamz.ws'
@ -242,8 +251,10 @@ class Vupload implements Match {
} }
export const matches = [ export const matches = [
new Doodstream(),
new Evoload(), new Evoload(),
new Mixdrop(), new Mixdrop(),
new Mp4Upload(),
new Newgrounds(), new Newgrounds(),
new Streamtape(), new Streamtape(),
new Streamzz(), new Streamzz(),

View File

@ -2,14 +2,16 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>HLS</title> <title>Stream Bypass</title>
<link rel="stylesheet" href="player.css"> <link rel="stylesheet" href="player.css">
<script src="player.js" defer></script> <script src="player.js" defer></script>
</head> </head>
<body> <body>
<video id="video"></video> <video id="video"></video>
<div id="message-container"> <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> </div>
</body> </body>
</html> </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

@ -4,14 +4,52 @@ import Hls from "hls.js";
function show_message(message: string) { function show_message(message: string) {
document.getElementById('message').innerText = message document.getElementById('message').innerText = message
document.getElementById('message-container').hidden = false document.getElementById('message-container').style.visibility = 'visible'
document.getElementById('video').hidden = true document.getElementById('video').hidden = true
} }
async function check_loaded(match: Match, check: Promise<boolean>) {
const loaded = await new Promise((resolve, _) => {
setTimeout(() => {
resolve(false)
}, match.reliability * 3000)
check
.then(value => resolve(value))
.catch(_ => resolve(false))
})
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.
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 video. ${message}`)
}
}
async function play_native(url: string, match: Match) { async function play_native(url: string, match: Match) {
const video = document.getElementById('video') as HTMLVideoElement const video = document.getElementById('video') as HTMLVideoElement
video.controls = true video.controls = true
video.src = url video.src = url
const readyState = new Promise<boolean>((resolve, _) => {
video.onloadeddata = () => resolve(true)
})
await check_loaded(match, readyState)
} }
async function play_hls(url: string, match: Match) { async function play_hls(url: string, match: Match) {
@ -27,35 +65,11 @@ async function play_hls(url: string, match: Match) {
hls.loadSource(url) hls.loadSource(url)
hls.attachMedia(video) hls.attachMedia(video)
const loaded = await new Promise((resolve, reject) => { const readyState = new Promise<boolean>((resolve, _) => {
setTimeout(() => { video.onloadeddata = () => resolve(true)
resolve(false)
}, match.reliability * 3000)
hls.on(Hls.Events.MANIFEST_PARSED, () => {
resolve(true)
})
}) })
if (!loaded) { await check_loaded(match, readyState)
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 { } 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>') 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 +79,14 @@ async function main() {
const urlQuery = new URLSearchParams(window.location.search) const urlQuery = new URLSearchParams(window.location.search)
const id = urlQuery.get('id') const id = urlQuery.get('id')
const url = urlQuery.get('url') const url = urlQuery.get('url')
const domain = urlQuery.get('domain')
const match = matches.find((m) => m.id === id) const match = matches.find((m) => m.id === id)
if (match === undefined) { 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/new">here</a>`)
return return
} }
document.title = match.name document.title = `Stream Bypass (${domain})`
url.endsWith('.m3u8') ? await play_hls(url, match) : await play_native(url, match) url.endsWith('.m3u8') ? await play_hls(url, match) : await play_native(url, match)
} }

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

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

@ -0,0 +1,66 @@
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;
}

View File

@ -47,8 +47,8 @@ async function buildHtml() {
async function buildCss() { async function buildCss() {
const files = { const files = {
'src/ui/popup/popup.sass': 'build/ui/popup/popup.css', 'src/ui/popup/popup.scss': 'build/ui/popup/popup.css',
'src/ui/player/player.sass': 'build/ui/player/player.css' 'src/ui/player/player.scss': 'build/ui/player/player.css'
} }
for (const [src, dst] of Object.entries(files)) { for (const [src, dst] of Object.entries(files)) {