diff --git a/.gitignore b/.gitignore
index 567609b..7c4f701 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
+.idea/
build/
+dist/
+node_modules/
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index bc4df72..0000000
--- a/Dockerfile
+++ /dev/null
@@ -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"]
diff --git a/LICENSE b/LICENSE
index ce19ae9..9e0c471 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2021 ByteDream
+Copyright (c) 2022 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
diff --git a/README.md b/README.md
index 0fcc003..3d57aa0 100644
--- a/README.md
+++ b/README.md
@@ -2,65 +2,52 @@
A multi-browser addon / extension for multiple streaming providers which redirects directly to the source video.
-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) or in [show all](#all-supported-websites) below):
-- [streamtape.com](https://streamtape.com)
-- [vivo.sx](https://vivo.sx)
-- [voe.sx](https://voe.sx)
+
-
+## 📝 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.
- How it's working
+ How it's working:
-The addon was tested on
-- Firefox (96.0.3)
-- Ungoogled Chromium (97.0)
-- Vivaldi (5.0)
-- Opera (83.0)
-
-## Installing
+## 📥 Installation
### Firefox
-Install the addon directly from the [firefox addon store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/) or from the [latest release](https://smartrelease.bytedream.org/github/ByteDream/stream-bypass/stream_bypass-{tag}_firefox.xpi) (if anything newer is available since firefox disables the plugin sometimes because of a setting which is necessary to run the addon properly).
+Install the addon directly from the [firefox addon store](https://addons.mozilla.org/de/firefox/addon/stream-bypass/).
### Chromium / Google Chrome
@@ -78,54 +65,60 @@ Install the addon directly from the [firefox addon store](https://addons.mozilla
4. Click Load unpacked.
5. Choose the cloned / unzipped directory.
-## Compiling
+## 📜 Supported sites
-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).
+| Site | Supported | Note |
+|-----------------------------------------------------------------------|-----------|--------------------------------------------------|
+| [doodstream.com](doodstream.com) / [dood.pm](https://dood.pm) | ❌ | Reverse engineering the site costs too much time |
+| [evoload.io](https://evoload.io) | ✔️ | |
+| [mixdrop.co](https://mixdrop.co) | ✔ ️ | |
+| [mp4upload.com](https://mp4upload.com) | ❌ | URL can be extracted but not played |
+| [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) | ✔ | |
+| [vupload.com](https://vupload.com) | ✔ | |
-### Manual
+- ✔️: Everything ok.
+- ⚠: Included in the addon but will probably not work. See `Note` in this case, an explanation why will stand there in the most cases.
+- ❌: Not included / supported by the addon. This can have various reasons. See `Note` for an explanation.
-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 `` with every `.sass` file in the `src` directory)
- ```
- $ sass --no-source-map
- ```
-The compiled output will be in the `src` 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.
-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.
+## ⚙️ Compiling
-### Docker
+If you want to compile the addon from source and not using the [installation](#installation) way, follow the instructions.
-For this, you need [docker](https://www.docker.com/) to be installed.
-- Build the docker image
- ```
- $ docker build -t stream-bypass .
- ```
-- Compile
- ```
- $ docker run --rm -v build:/build stream-bypass
- ```
-The compiled output will remain in a (new created if not existing) `build` directory.
+Requirements:
+- `npm` installed.
+- A copy of this repository and a shell / console open in the copied directory.
+
+If the requirements are satisfied, you can continue with the following commands:
+```shell
+# install all dependencies
+$ npm install
+
+# build the extension source to a build/ directory
+$ npm run build
+
+# same as build + create a bundle zipfile at dist/
+$ npm run bundle
+```
##### Install
-If you want to use the addon in Chromium or any browser which is based on it (almost every other, Google Chrome, Opera, ...), follow the steps in [installing](#installing).
-When using firefox, use the following
+If you want to use the addon in Chromium or any browser which is based on it (almost every other, Google Chrome, Opera, ...), follow the steps in [installation](#-installation).
+When using firefox, use the following:
1. Type `about:debugging` in the browser's address bar.
2. Select 'This Firefox' tab (maybe named different, depending on your language).
3. Under `Temporary Extensions`, click `Load Temporary Add-on`.
4. Choose any file in the directory where the compiled sources are.
-## License
+## ⚖ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.
diff --git a/SUPPORTED b/SUPPORTED
deleted file mode 100644
index 43d4d99..0000000
--- a/SUPPORTED
+++ /dev/null
@@ -1,14 +0,0 @@
-evoload.io
-mcloud.to
-mixdrop.co
-newgrounds.com
-streamtape.com
-streamzz.to
-thevideome.com
-vidlox.me
-vidstream.pro
-vidoza.net
-vivo.st
-vivo.sx
-voe.sx
-vupload.com
diff --git a/build.py b/build.py
deleted file mode 100644
index c316cf2..0000000
--- a/build.py
+++ /dev/null
@@ -1,158 +0,0 @@
-#!/usr/bin/python3
-
-import argparse
-import io
-import json
-import sys
-import urllib.request
-import zipfile
-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:
- if not line.strip().startswith('//'):
- 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 write_readme():
- firefox_pattern = re.compile(r'Mozilla Firefox (?P.+)')
- chromium_pattern = re.compile(r'(?P\d+\.\d+)')
- tested = {}
-
- stdout, stderr = subprocess.Popen(['firefox', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
- if stderr == b'':
- tested['Firefox'] = re.search(firefox_pattern, stdout.decode('utf-8').replace('\n', '')).group('version')
-
- for command, name in {'chromium': 'Ungoogled Chromium', 'vivaldi-stable': 'Vivaldi', 'opera': 'Opera'}.items():
- stdout, stderr = subprocess.Popen([command, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
- if stderr == b'':
- tested[name] = re.search(chromium_pattern, stdout.decode('utf-8').replace('\n', '')).group('version')
-
- # it this the right syntax if i want to read and write to a file? * dreams in python3.10 *
- with open('README.md', 'r') as read_file:
- readme = read_file.read()
-
- # adds all available websites
- all_providers_regex = r'(?<=
-
-
-
diff --git a/src/popup/popup.ts b/src/popup/popup.ts
deleted file mode 100644
index e9dc161..0000000
--- a/src/popup/popup.ts
+++ /dev/null
@@ -1,163 +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]
- switch (match[3]) {
- case 1: // low
- nameValue.classList.add('low-reliability')
- // @ts-ignore
- tippy(nameValue, {
- content: 'Low reliability: Errors may occur often'
- })
- break
- case 2: // normal
- nameValue.classList.add('normal-reliability')
- // @ts-ignore
- tippy(nameValue, {
- content: 'Normal reliability: Save to use but errors may occur'
- })
- break
- case 3: //high
- nameValue.classList.add('high-reliability')
- // @ts-ignore
- tippy(nameValue, {
- content: 'High reliability: Errors are very unlikely to happen'
- })
- break
- }
- let buttons = document.createElement('td')
- buttons.classList.add('buttons')
- let on = document.createElement('a')
- on.innerText = 'On'
- // @ts-ignore
- let onTippy = tippy(on, {
- content: `Enable ${match[0]}`,
- onMount: () => {
- if (on.classList.contains('active') || off.classList.contains('disabled')) {
- onTippy.hide()
- }
- }
- })
- let off = document.createElement('a')
- off.innerText = 'Off'
- // @ts-ignore
- let offTippy = tippy(off, {
- content: `Disable ${match[0]}`,
- onMount: () => {
- if (off.classList.contains('active') || off.classList.contains('disabled')) {
- offTippy.hide()
- }
- }
- })
- 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')
- let allOn = allButtons[0]
- allButtons[0].onclick = function () {
- if (!allButtons[0].classList.contains('disabled')) {
- enableAll(true)
- allButtons[0].classList.add('active')
- allButtons[1].classList.remove('active')
- }
- }
- // @ts-ignore
- let allOnTippy = tippy(allOn, {
- content: 'Enable all websites',
- onMount: () => {
- if (allButtons[0].classList.contains('active')) {
- allOnTippy.hide()
- }
- }
- })
- allButtons[1].onclick = function () {
- if (!allButtons[1].classList.contains('disabled')) {
- enableAll(false)
- allButtons[0].classList.remove('active')
- allButtons[1].classList.add('active')
- }
- }
- // @ts-ignore
- let allOffTippy = tippy(allButtons[1], {
- content: 'Disable all websites',
- onMount: () => {
- if (allButtons[1].classList.contains('active')) {
- allOffTippy.hide()
- }
- }
- })
- allDisabled ? allButtons[1].classList.add('active') : allButtons[0].classList.add('active')
-})
diff --git a/src/res/hls.html b/src/res/hls.html
deleted file mode 100644
index dee155c..0000000
--- a/src/res/hls.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- m3u8
-
-
-
-
-
-
-
-
-
diff --git a/src/res/hls.sass b/src/res/hls.sass
deleted file mode 100644
index 4e4ad28..0000000
--- a/src/res/hls.sass
+++ /dev/null
@@ -1,16 +0,0 @@
-body
- background-color: #131313
-
-html, body, video
- width: 100%
- height: 100%
- margin: 0
-
-video
- position: absolute
- top: 0
- left: 0
-
-#message
- color: white
- text-align: center
diff --git a/src/res/hls.ts b/src/res/hls.ts
deleted file mode 100644
index bcc23dd..0000000
--- a/src/res/hls.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-function showMessage(message: string) {
- let messageElement = document.getElementById('message') as HTMLParagraphElement
- messageElement.innerHTML = message
- messageElement.hidden = false
- document.getElementById('video').hidden = true
-}
-
-function loadHls() {
- let url = window.location.hash.substring(1)
- let video = document.getElementById('video') as HTMLVideoElement;
-
- video.controls = true
- if (video.canPlayType('application/vnd.apple.mpegurl')) {
- video.src = url
- // @ts-ignore
- } else if (Hls.isSupported()) {
- // @ts-ignore
- let hls = new Hls()
- hls.loadSource(url)
- hls.attachMedia(video)
-
- let searchParams = new URLSearchParams(window.location.search)
- let rawReliability = parseInt(searchParams.get('reliability'))
-
- let thirdPartyFallback = setTimeout(() => {
- let message: string
-
- switch (rawReliability) {
- case 1: // 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 domain (${searchParams.get('domain')}) and try again`
- break
- case 2: // 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 3: // 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 here.
- 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 you it's working then`
- break
- }
-
- // shows a message if hls could not be loaded
- showMessage(`Could not load hls video. ${message}`)
- }, rawReliability * 3000)
-
- document.title = searchParams.get('domain')
-
- // @ts-ignore
- hls.on(Hls.Events.MANIFEST_PARSED, () => {
- clearTimeout(thirdPartyFallback)
- document.getElementById('video').hidden = false
- document.getElementById('message').hidden = true
- })
- } else {
- // shows a message if hls is not supported
- showMessage(`Failed to play m3u8 video (hls is not supported). Try again or create a new issue here`)
- }
-}
-
-loadHls()
diff --git a/src/store/store.ts b/src/store/store.ts
new file mode 100644
index 0000000..60de76f
--- /dev/null
+++ b/src/store/store.ts
@@ -0,0 +1,64 @@
+import {Match, matches} from "../match/match";
+
+async function storageGet(key: string): Promise {
+ return new Promise((resolve) => {
+ chrome.storage.local.get(key, (value) => {
+ resolve(value[key])
+ })
+ })
+}
+
+async function storageSet(key: string, value: any) {
+ const obj = {}
+ obj[key] = value
+ await chrome.storage.local.set(obj)
+}
+
+export async function getDisabled(): Promise {
+ const localMatches = []
+
+ for (const disabled of (await storageGet('disabled') as string[]) || []) {
+ let m: Match
+ if ((m = matches.find((v) => v.id === disabled)) !== undefined) {
+ localMatches.push(m)
+ }
+ }
+
+ return localMatches
+}
+
+export async function getAllDisabled(): Promise {
+ const value = await storageGet('all')
+ return value !== undefined ? value as unknown as boolean : false
+}
+
+export async function enableAll() {
+ await storageSet('all', false)
+ await chrome.browserAction.setIcon({
+ path: null
+ })
+}
+
+export async function disableAll() {
+ await storageSet('all', true)
+ await chrome.browserAction.setIcon({
+ path: {
+ 48: chrome.runtime.getURL('icons/disabled_48.png'),
+ 128: chrome.runtime.getURL('icons/disabled_128.png')
+ }
+ })
+}
+
+export async function enable(match: Match) {
+ const disabled = await storageGet('disabled') || []
+ disabled.splice(disabled.indexOf(match.id))
+ await storageSet('disabled', disabled)
+}
+
+export async function disable(match: Match) {
+ const disabled = await storageGet('disabled') as string[] || []
+ if (disabled.indexOf(match.id) !== undefined) {
+ disabled.push(match.id)
+ await storageSet('disabled', disabled)
+ }
+}
diff --git a/src/tsconfig.json b/src/tsconfig.json
index 17aecd3..f28cb2b 100644
--- a/src/tsconfig.json
+++ b/src/tsconfig.json
@@ -1,17 +1,18 @@
{
"compilerOptions": {
- "module": "commonjs",
- "target": "es2015",
+ "module": "esnext",
+ "target": "es2019",
"removeComments": true,
+ "esModuleInterop": true,
"lib": [
"dom",
"es5",
"scripthost",
"es2015.collection",
"es2015.promise"
- ]
+ ],
},
"exclude": [
- "node_modules"
- ],
-}
\ No newline at end of file
+ "../node_modules"
+ ]
+}
diff --git a/src/ui/player/player.html b/src/ui/player/player.html
new file mode 100644
index 0000000..8bcf77d
--- /dev/null
+++ b/src/ui/player/player.html
@@ -0,0 +1,15 @@
+
+
+
+
+ HLS
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ui/player/player.sass b/src/ui/player/player.sass
new file mode 100644
index 0000000..152f77a
--- /dev/null
+++ b/src/ui/player/player.sass
@@ -0,0 +1,22 @@
+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
diff --git a/src/ui/player/player.ts b/src/ui/player/player.ts
new file mode 100644
index 0000000..0f3cd18
--- /dev/null
+++ b/src/ui/player/player.ts
@@ -0,0 +1,79 @@
+import {Match, matches, Reliability} from "../../match/match";
+// @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('video').hidden = true
+}
+
+async function play_native(url: string, match: Match) {
+ const video = document.getElementById('video') as HTMLVideoElement
+ video.controls = true
+ video.src = url
+}
+
+async function play_hls(url: string, match: Match) {
+ const video = document.getElementById('video') as HTMLVideoElement
+ video.controls = true
+
+ if (video.canPlayType('application/vnd.apple.mpegurl')) {
+ video.src = url
+ } else if (Hls.isSupported()) {
+ const hls = new Hls({
+ enableWorker: false
+ })
+ 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 here.
+ 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}`)
+ }
+ } else {
+ show_message('Failed to play m3u8 video (hls is not supported). Try again or create a new issue here')
+ }
+}
+
+async function main() {
+ const urlQuery = new URLSearchParams(window.location.search)
+ const id = urlQuery.get('id')
+ const url = urlQuery.get('url')
+
+ const match = matches.find((m) => m.id === id)
+ if (match === undefined) {
+ show_message(`Invalid id: ${id}. Please report this here`)
+ return
+ }
+ document.title = match.name
+
+ url.endsWith('.m3u8') ? await play_hls(url, match) : await play_native(url, match)
+}
+
+main()
diff --git a/src/ui/popup/popup.html b/src/ui/popup/popup.html
new file mode 100644
index 0000000..8d1516b
--- /dev/null
+++ b/src/ui/popup/popup.html
@@ -0,0 +1,23 @@
+
+
+
+
+ Stream-Bypass
+
+
+
+
+