mirror of
https://github.com/bytedream/stream-bypass.git
synced 2025-05-31 06:30:55 +02:00
Initial commit
This commit is contained in:
commit
ecd194c1fe
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk --no-cache add python3 nodejs npm
|
||||
|
||||
RUN npm install -g typescript sass
|
||||
|
||||
COPY [".", "."]
|
||||
|
||||
CMD ["python3", "build.py", "-b", "-c"]
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
95
README.md
Normal file
95
README.md
Normal file
@ -0,0 +1,95 @@
|
||||
# Stream Bypass
|
||||
|
||||
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)):
|
||||
- [streamtape.com](https://streamtape.com/)
|
||||
- [vidoza.net](https://vidoza.net/)
|
||||
|
||||
<details id="example">
|
||||
<summary><b>How it's working</b></summary>
|
||||
<img src="example.gif" alt="">
|
||||
</details>
|
||||
|
||||
The addon was tested on
|
||||
- Firefox (92.0)
|
||||
- Chromium (92.0)
|
||||
- Opera (78.0)
|
||||
|
||||
## Installing
|
||||
|
||||
### Firefox
|
||||
|
||||
Install the `.xpi` (firefox addon) file from the [latest release](https://github.com/ByteDream/vivosx-source-redirector/releases/latest).
|
||||
|
||||
### Chromium / Google Chrome
|
||||
|
||||
1. Download the `stream-bypass-<version>.zip` file from the [latest release](https://github.com/ByteDream/vivosx-source-redirector/releases/latest) and unzip it (with [7zip](https://www.7-zip.org/) or something like that).
|
||||
2. Go into your browser and type `chrome://extensions` in the address bar.
|
||||
3. Turn the developer mode in the top right corner on.
|
||||
4. Click Load unpacked.
|
||||
5. Choose the cloned / unzipped directory.
|
||||
|
||||
### Opera
|
||||
|
||||
1. Download the `stream-bypass-<version>.zip` file from the [latest release](https://github.com/ByteDream/vivosx-source-redirector/releases/latest) and unzip it (with [7zip](https://www.7-zip.org/) or something like that).
|
||||
2. Go into your browser and type `opera://extensions` in the address bar.
|
||||
3. Turn the developer mode in the top right corner on.
|
||||
4. Click Load unpacked.
|
||||
5. Choose the cloned / unzipped directory.
|
||||
|
||||
## Compiling
|
||||
|
||||
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).
|
||||
|
||||
### Manual
|
||||
|
||||
For compiling everything bare bones, you need [typescript](https://www.typescriptlang.org/) and [sass](https://sass-lang.com/) installed.
|
||||
- Compile typescript
|
||||
```
|
||||
$ tsc -p src
|
||||
```
|
||||
- Compile sass (replace `<path to sass file>` with every `.sass` file in the `src` directory)
|
||||
```
|
||||
$ sass --no-source-map <path to sass file>
|
||||
```
|
||||
The compiled output will be in the `src` directory.
|
||||
|
||||
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.
|
||||
|
||||
### Docker
|
||||
|
||||
For this, you need [docker](https://www.docker.com/) to be installed.
|
||||
- Build the docker image
|
||||
```
|
||||
$ docker build -t stream-bypass .
|
||||
```
|
||||
- Compile
|
||||
```
|
||||
$ docker rum --rm -v build:/build stream-bypass
|
||||
```
|
||||
The compiled output will remain in a (new created if not existing) `build` directory.
|
||||
|
||||
##### 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
|
||||
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
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details.
|
105
build.py
Executable file
105
build.py
Executable file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
|
||||
def load_matches():
|
||||
matched = []
|
||||
|
||||
indexed = False
|
||||
pattern = re.compile(r"(?<=\[')\S*(?=',)")
|
||||
for line in open('src/match.ts', 'r'):
|
||||
if not indexed:
|
||||
if 'constmatches=[' in line.replace(' ', ''):
|
||||
indexed = True
|
||||
else:
|
||||
match = pattern.findall(line)
|
||||
if match:
|
||||
matched.append(match[0])
|
||||
else:
|
||||
break
|
||||
|
||||
return matched
|
||||
|
||||
|
||||
def write_manifest():
|
||||
matches = load_matches()
|
||||
manifest = json.load(open('src/manifest.json', 'r'))
|
||||
|
||||
for content_script in manifest['content_scripts']:
|
||||
content_script['matches'] = [f'*://{match}/*' for match in matches]
|
||||
|
||||
json.dump(manifest, open('src/manifest.json', 'w'), indent=2)
|
||||
|
||||
|
||||
def write_supported():
|
||||
open('SUPPORTED', 'w').writelines([f'{match}\n' for match in load_matches()])
|
||||
|
||||
|
||||
def copy_built():
|
||||
if not shutil.which('tsc'):
|
||||
sys.stderr.write('The typescript compiler `tsc` could not be found')
|
||||
sys.exit(1)
|
||||
elif not shutil.which('sass'):
|
||||
sys.stderr.write('The sass compiler `sass` could not be found')
|
||||
sys.exit(1)
|
||||
|
||||
write_manifest()
|
||||
|
||||
subprocess.call(['tsc', '-p', 'src'])
|
||||
|
||||
build_path = Path('build')
|
||||
if build_path.is_dir():
|
||||
for file in build_path.rglob('*'):
|
||||
if file.is_dir():
|
||||
shutil.rmtree(str(file))
|
||||
else:
|
||||
file.unlink()
|
||||
else:
|
||||
build_path.mkdir()
|
||||
for file in Path('src').rglob('*'):
|
||||
build_file = build_path.joinpath(str(file)[4:])
|
||||
if file.is_dir():
|
||||
build_file.mkdir(parents=True)
|
||||
elif file.suffix == '.sass':
|
||||
css_file = str(file)[:-4] + 'css'
|
||||
subprocess.call(['sass', '--no-source-map', file, css_file])
|
||||
shutil.copy(css_file, str(build_path.joinpath(css_file[4:])))
|
||||
elif file.name == 'tsconfig.json':
|
||||
continue
|
||||
elif file.suffix != '.ts':
|
||||
shutil.copy(str(file), str(build_file))
|
||||
|
||||
|
||||
def clean_build():
|
||||
for file in Path('src').rglob('*'):
|
||||
if file.suffix in ['.js', '.css', '.map']:
|
||||
file.unlink()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-m', '--manifest', action='store_true', help='Builds the manifest.json file for addon information in ./src')
|
||||
parser.add_argument('-s', '--supported', action='store_true', help='Builds the SUPPORTED file with all supported domains in the current directory')
|
||||
parser.add_argument('-b', '--build', action='store_true', help='Creates a ./build folder and builds all typescript / sass files')
|
||||
parser.add_argument('-c', '--clean', action='store_true', help='Cleans the ./src folder from .js, .css and .map files')
|
||||
|
||||
parsed = parser.parse_args()
|
||||
|
||||
if parsed.manifest:
|
||||
write_manifest()
|
||||
if parsed.supported:
|
||||
write_supported()
|
||||
if parsed.build:
|
||||
copy_built()
|
||||
if parsed.clean:
|
||||
clean_build()
|
||||
|
||||
if not parsed.manifest and not parsed.supported and not parsed.build and not parsed.clean:
|
||||
print('\n'.join(load_matches()))
|
BIN
example.gif
Normal file
BIN
example.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 665 KiB |
BIN
src/icons/stream-bypass.png
Normal file
BIN
src/icons/stream-bypass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
24
src/index.ts
Normal file
24
src/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// @ts-ignore
|
||||
chrome.storage.local.get(['all', 'disabled'], function (result) {
|
||||
let keys = Object.keys(result)
|
||||
if (keys.indexOf('all') !== -1 && !result['all']) {
|
||||
return
|
||||
}
|
||||
// @ts-ignore
|
||||
for (let match of matches) {
|
||||
if (window.location.href.indexOf(match[0]) !== -1) {
|
||||
if (keys.indexOf('disabled') === -1 || result['disabled'].indexOf(match[0]) === -1) {
|
||||
let regex = match[1] as RegExp
|
||||
let matchClass = match[2] as Match
|
||||
|
||||
let re
|
||||
if (regex === null) {
|
||||
location.assign(matchClass === null ? document.body.innerHTML : matchClass.match(new RegExp('').exec(document.body.innerHTML)))
|
||||
} else if ((re = regex.exec(document.body.innerHTML)) !== null) {
|
||||
location.assign(matchClass === null ? re[0] : matchClass.match(re))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
34
src/manifest.json
Normal file
34
src/manifest.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Stream Bypass",
|
||||
"author": "ByteDream",
|
||||
"description": "",
|
||||
"version": "1.0.0",
|
||||
"homepage_url": "https://github.com/ByteDream/stream-bypass",
|
||||
"content_scripts": [
|
||||
{
|
||||
"all_frames": true,
|
||||
"matches": [
|
||||
"*://streamtape.com/*",
|
||||
"*://vidoza.net/*",
|
||||
"*://vivo.sx/*",
|
||||
"*://vupload.com/*"
|
||||
],
|
||||
"js": [
|
||||
"match.js",
|
||||
"index.js"
|
||||
]
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
"storage"
|
||||
],
|
||||
"browser_action": {
|
||||
"default_icon": "icons/stream-bypass.png",
|
||||
"default_title": "Stream Bypass",
|
||||
"default_popup": "popup/popup.html"
|
||||
},
|
||||
"icons": {
|
||||
"128": "icons/stream-bypass.png"
|
||||
}
|
||||
}
|
43
src/match.ts
Normal file
43
src/match.ts
Normal file
@ -0,0 +1,43 @@
|
||||
interface Match {
|
||||
match(match: RegExpMatchArray): string
|
||||
}
|
||||
|
||||
class Streamtape implements Match {
|
||||
match(match: RegExpMatchArray): string {
|
||||
return `https://streamtape.com/get_video?${match[0]}`
|
||||
}
|
||||
}
|
||||
|
||||
class Vivo implements Match {
|
||||
match(match: RegExpMatchArray): string {
|
||||
return this.rot47(decodeURIComponent(match[1]))
|
||||
}
|
||||
|
||||
// decrypts a string with the rot47 algorithm (https://en.wikipedia.org/wiki/ROT13#Variants)
|
||||
rot47(encoded: string): string {
|
||||
const s = []
|
||||
for(let i = 0; i < encoded.length; i++) {
|
||||
const j = encoded.charCodeAt(i)
|
||||
if((j >= 33) && (j <= 126)) {
|
||||
s[i] = String.fromCharCode(33+((j+ 14)%94))
|
||||
} else {
|
||||
s[i] = String.fromCharCode(j)
|
||||
}
|
||||
}
|
||||
return s.join('')
|
||||
}
|
||||
}
|
||||
|
||||
class Vupload implements Match {
|
||||
match(match: RegExpMatchArray): string {
|
||||
return `https://www3.megaupload.to/${match[0]}/v.mp4`
|
||||
}
|
||||
}
|
||||
|
||||
// every match HAS to be on an separate line
|
||||
const matches = [
|
||||
['streamtape.com', new RegExp(/id=\S*(?=')/gm), new Streamtape()],
|
||||
['vidoza.net', new RegExp(/(?<=src:(\s*)?")\S*(?=")/gm), null],
|
||||
['vivo.sx', new RegExp(/source:\s*'(\S+)'/gm), new Vivo()],
|
||||
['vupload.com', new RegExp(/(?<=class\|)\w*/gm), new Vupload()]
|
||||
]
|
23
src/popup/popup.html
Normal file
23
src/popup/popup.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<link rel="stylesheet" href="popup.css">
|
||||
<script src="../match.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="all">
|
||||
<div class="buttons">
|
||||
<a>On</a>
|
||||
<a>Off</a>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<table id="sub-container">
|
||||
</table>
|
||||
</div>
|
||||
<script src="./popup.js"></script>
|
||||
</body>
|
||||
</html>
|
35
src/popup/popup.sass
Normal file
35
src/popup/popup.sass
Normal file
@ -0,0 +1,35 @@
|
||||
body
|
||||
background-color: #2b2a33
|
||||
font-weight: bold
|
||||
max-height: 500px
|
||||
|
||||
|
||||
a, p
|
||||
color: white
|
||||
font-size: 16px
|
||||
margin: 5px 0
|
||||
|
||||
|
||||
a
|
||||
border: 1px solid #281515
|
||||
cursor: pointer
|
||||
font-weight: normal
|
||||
padding: 5px 8px
|
||||
|
||||
&.active
|
||||
background-color: rgba(255, 65, 65, 0.74)
|
||||
cursor: default
|
||||
|
||||
&.disabled
|
||||
background-color: grey
|
||||
cursor: not-allowed
|
||||
|
||||
|
||||
hr
|
||||
margin: 3px 0
|
||||
|
||||
|
||||
#all
|
||||
display: flex
|
||||
justify-content: center
|
||||
margin: 10px 0
|
103
src/popup/popup.ts
Normal file
103
src/popup/popup.ts
Normal file
@ -0,0 +1,103 @@
|
||||
function enableAll(enable: boolean) {
|
||||
// @ts-ignore
|
||||
chrome.storage.local.set({'all': enable})
|
||||
|
||||
// @ts-ignore
|
||||
for (let button of document.getElementById('sub-container').getElementsByTagName('a')) {
|
||||
enable ? button.classList.remove('disabled') : button.classList.add('disabled')
|
||||
}
|
||||
}
|
||||
|
||||
function enableOne(website: string, enable: boolean) {
|
||||
// @ts-ignore
|
||||
chrome.storage.local.get(['disabled'], function (result) {
|
||||
let disabled: string[] = Object.keys(result).length === 0 ? [] : result['disabled']
|
||||
if (enable && disabled.indexOf(website) !== -1) {
|
||||
disabled.splice(disabled.indexOf(website), 1)
|
||||
} else if (!enable && disabled.indexOf(website) === -1) {
|
||||
disabled.push(website)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
chrome.storage.local.set({'disabled': disabled})
|
||||
})
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
chrome.storage.local.get(['all', 'disabled'], function (result) {
|
||||
let allDisabled = result['all'] !== undefined && !result['all']
|
||||
let disabled = new Map()
|
||||
|
||||
if (allDisabled) {
|
||||
// @ts-ignore
|
||||
for (let match of matches) {
|
||||
disabled.set(match[0], false)
|
||||
}
|
||||
} else {
|
||||
if (Object.keys(result).indexOf('disabled') !== -1) {
|
||||
for (let disable of result['disabled']) {
|
||||
disabled.set(disable, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let subContainer = document.getElementById('sub-container')
|
||||
// @ts-ignore
|
||||
for (let match of matches) {
|
||||
let row = document.createElement('tr')
|
||||
|
||||
let name = document.createElement('td')
|
||||
let nameValue = document.createElement('p')
|
||||
nameValue.innerText = match[0]
|
||||
let buttons = document.createElement('td')
|
||||
buttons.classList.add('buttons')
|
||||
let on = document.createElement('a')
|
||||
on.innerText = 'On'
|
||||
let off = document.createElement('a')
|
||||
off.innerText = 'Off'
|
||||
disabled.has(match[0]) ? off.classList.add('active') : on.classList.add('active')
|
||||
if (allDisabled) {
|
||||
on.classList.add('disabled')
|
||||
off.classList.add('disabled')
|
||||
}
|
||||
|
||||
on.onclick = function () {
|
||||
if (!on.classList.contains('disabled')) {
|
||||
enableOne(match[0], true)
|
||||
on.classList.add('active')
|
||||
off.classList.remove('active')
|
||||
}
|
||||
}
|
||||
off.onclick = function () {
|
||||
if (!off.classList.contains('disabled')) {
|
||||
enableOne(match[0], false)
|
||||
on.classList.remove('active')
|
||||
off.classList.add('active')
|
||||
}
|
||||
}
|
||||
|
||||
name.append(nameValue)
|
||||
buttons.append(on, off)
|
||||
row.append(name, buttons)
|
||||
subContainer.append(row)
|
||||
}
|
||||
|
||||
let allButtons = document.getElementById('all').getElementsByTagName('a')
|
||||
allButtons[0].onclick = function () {
|
||||
if (!allButtons[0].classList.contains('disabled')) {
|
||||
enableAll(true)
|
||||
allButtons[0].classList.add('active')
|
||||
allButtons[1].classList.remove('active')
|
||||
}
|
||||
}
|
||||
allButtons[1].onclick = function () {
|
||||
if (!allButtons[1].classList.contains('disabled')) {
|
||||
enableAll(false)
|
||||
allButtons[0].classList.remove('active')
|
||||
allButtons[1].classList.add('active')
|
||||
}
|
||||
}
|
||||
allDisabled ? allButtons[1].classList.add('active') : allButtons[0].classList.add('active')
|
||||
})
|
15
src/tsconfig.json
Normal file
15
src/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es5",
|
||||
"scripthost",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user