├── .github
└── workflows
│ └── build.yml
├── LICENSE
├── README.md
├── screenshots
├── adblocker-counter.png
├── adblocker-notallowed.jpg
└── adblocker-tos.png
└── src
├── icons
└── logo.png
├── main
├── background.js
├── content.js
├── options.html
└── options.js
└── manifest.json
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: "Build and Release"
2 | on:
3 | push:
4 | tags:
5 | - '*'
6 |
7 | jobs:
8 | build:
9 | name: "Build"
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: "Checkout"
13 | uses: actions/checkout@v1
14 |
15 | - name: "web-ext build"
16 | id: web-ext-build
17 | uses: kewisch/action-web-ext@v1
18 | with:
19 | cmd: build
20 | source: src
21 | filename: "{name}-{version}.xpi"
22 | ignoreFiles: '[ "package.json","package-lock.json","yarn.lock" ]'
23 |
24 | - name: "Upload Unsigned Artifact"
25 | uses: actions/upload-artifact@v3
26 | with:
27 | name: unsigned.xpi
28 | path: ${{ steps.web-ext-build.outputs.target }}
29 |
30 | - name: "web-ext sign"
31 | id: web-ext-sign
32 | uses: kewisch/action-web-ext@v1
33 | with:
34 | cmd: sign
35 | source: ${{ steps.web-ext-build.outputs.target }}
36 | channel: listed
37 | apiKey: ${{ secrets.AMO_SIGN_KEY }}
38 | apiSecret: ${{ secrets.AMO_SIGN_SECRET }}
39 | timeout: 900000
40 |
41 | - name: "Upload Signed Artifact"
42 | uses: actions/upload-artifact@v3
43 | with:
44 | name: signed.xpi
45 | path: ${{ steps.web-ext-sign.outputs.target }}
46 |
47 | - name: "Create Release"
48 | uses: softprops/action-gh-release@v1
49 | env:
50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 | with:
52 | files: ${{ steps.web-ext-sign.outputs.target }}
53 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Fayçal Mitidji
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # YT Siphon
6 | ### Redirect YouTube URLs to Alternative Frontends Manually
7 |
8 | **YT Siphon** is a browser extension that lets you choose to open YouTube videos in alternative frontends. Instead of auto-redirecting, it gives you the power to decide with simple shortcuts.
9 | **Key Features**:
10 | - **`Alt+J`**: Open the current YouTube video in your chosen frontend.
11 | - *Tip*: You can customize this shortcut in your browser settings.
12 | - **`Shift+Click`**: While on YouTube, use this to open a video (by clicking on thumbnails / links) in the configured frontend in a new tab.
13 |
14 | Perfect for those who like to browse the YouTube homepage and selectively watch videos on alternative frontends. Also handy if someone shares a YouTube link with you and you prefer watching it on a different frontend.
15 |
16 | ## Motivation
17 | YouTube started cracking down on Ad Blocker usage, so this is my workaround.
18 |
19 | 
20 | 
21 | 
22 |
23 | ## Installation
24 |
25 | ### Mozilla Add-Ons site
26 | [Get the add-on from addons.mozilla.org](https://addons.mozilla.org/en-US/firefox/addon/yt-siphon/)
27 |
28 | ### Manual
29 | 1. Download the `.xpi` addon file from the [Releases page](https://github.com/d3vr/yt-siphon/releases/)
30 | 2. Go to `about:addons`
31 | 3. Click the gear icon and select `Install Add-on From File`
32 |
33 | ### Cloned repo
34 | 1. Clone the repository or download as zip
35 | 2. Go to `about:debugging`
36 | 3. Click on `This Firefox`
37 | 4. Then `Load Temporary Add-on`
38 | 5. Select `manifest.json` in the `src/` directory from the cloned repo.
39 |
40 |
41 | ## Configuration
42 | 1. Visit YouTube then click the Add-Ons icon to open the panel and click the gear icon next to `YT Siphon`
43 | 2. Select `Always Allow on www.youtube.com`
44 | 3. ...
45 | 4. Profit?
46 |
47 | You can change the default configured alternative frontend (`piped.video`) from the options page. You can click the extension's icon to access the options page.
48 |
49 | ## Credit
50 | - GPT-4 and [Phind](https://www.phind.com/) helped a lot while creating this add-on.
51 | - DALL-E 3 created the logo / icon.
52 |
53 | ## License
54 | [MIT](https://www.tldrlegal.com/license/mit-license)
55 |
--------------------------------------------------------------------------------
/screenshots/adblocker-counter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/d3vr/yt-siphon/cfcbb2a75dbefccd510913864c1ef3ff0043cd47/screenshots/adblocker-counter.png
--------------------------------------------------------------------------------
/screenshots/adblocker-notallowed.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/d3vr/yt-siphon/cfcbb2a75dbefccd510913864c1ef3ff0043cd47/screenshots/adblocker-notallowed.jpg
--------------------------------------------------------------------------------
/screenshots/adblocker-tos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/d3vr/yt-siphon/cfcbb2a75dbefccd510913864c1ef3ff0043cd47/screenshots/adblocker-tos.png
--------------------------------------------------------------------------------
/src/icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/d3vr/yt-siphon/cfcbb2a75dbefccd510913864c1ef3ff0043cd47/src/icons/logo.png
--------------------------------------------------------------------------------
/src/main/background.js:
--------------------------------------------------------------------------------
1 | const YOUTUBE_HOSTNAME = 'www.youtube.com';
2 | const DEFAULT_FRONTEND_URL = 'piped.video';
3 |
4 |
5 | chrome.storage.sync.get(['frontendUrl'], function(data) {
6 | if (!data || !data.frontendUrl) {
7 | chrome.storage.sync.set({ 'frontendUrl': DEFAULT_FRONTEND_URL });
8 | }
9 | });
10 |
11 | browser.action.onClicked.addListener((tab) => {
12 | browser.runtime.openOptionsPage();
13 | });
14 |
15 |
16 | chrome.commands.onCommand.addListener(function(command) {
17 | console.log(command);
18 | if (command === "open_alt_frontend") {
19 | console.log("Execute action");
20 | chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
21 | const tab = tabs[0];
22 | if (tab.url && tab.url.includes(YOUTUBE_HOSTNAME)) {
23 | console.log(tab.url);
24 | chrome.storage.sync.get(['frontendUrl'], function(data) {
25 | let frontend = data.frontendUrl;
26 | console.log(frontend);
27 | let newUrl = tab.url.replace(YOUTUBE_HOSTNAME, frontend);
28 | chrome.tabs.update(tab.id, { url: newUrl });
29 | });
30 | }
31 | });
32 | }
33 | });
34 |
35 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
36 | if (message.openTab) {
37 | chrome.tabs.create({ url: message.openTab, active: false });
38 | }
39 | });
40 |
41 |
--------------------------------------------------------------------------------
/src/main/content.js:
--------------------------------------------------------------------------------
1 | const YOUTUBE_HOSTNAME = "www.youtube.com";
2 |
3 | document.body.addEventListener("click", function(e) {
4 | if (
5 | e.shiftKey &&
6 | (e.target.closest('a[href^="/watch?"]') ||
7 | e.target.closest("ytd-thumbnail"))
8 | ) {
9 | e.preventDefault();
10 | let videoLink = e.target.closest('a[href^="/watch?"]').href;
11 | chrome.storage.sync.get(["frontendUrl"], function(data) {
12 | if (data.frontendUrl) {
13 | let newLink = videoLink.replace(YOUTUBE_HOSTNAME, data.frontendUrl);
14 | chrome.runtime.sendMessage({ openTab: newLink });
15 | }
16 | });
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/src/main/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Options
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main/options.js:
--------------------------------------------------------------------------------
1 | document.getElementById('save').addEventListener('click', function() {
2 | const frontendUrl = document.getElementById('frontendUrl').value;
3 | chrome.storage.sync.set({ 'frontendUrl': frontendUrl }, function() {
4 | alert('Frontend URL saved!');
5 | });
6 | });
7 |
8 | // Load any previously saved frontend URL
9 | chrome.storage.sync.get(['frontendUrl'], function(data) {
10 | if (data.frontendUrl) {
11 | document.getElementById('frontendUrl').value = data.frontendUrl;
12 | }
13 | });
14 |
15 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "YT Siphon",
4 | "version": "1.0.6",
5 | "description": "Redirect YouTube URLs to alternative frontends",
6 | "icons": {
7 | "512": "icons/logo.png"
8 | },
9 | "action": {
10 | "default_icon": {
11 | "512": "icons/logo.png"
12 | }
13 | },
14 | "background": {
15 | "scripts": [
16 | "main/background.js"
17 | ]
18 | },
19 | "options_ui": {
20 | "page": "main/options.html",
21 | "open_in_tab": true
22 | },
23 | "permissions": [
24 | "tabs",
25 | "scripting",
26 | "activeTab",
27 | "storage"
28 | ],
29 | "commands": {
30 | "open_alt_frontend": {
31 | "suggested_key": {
32 | "default": "Alt+J"
33 | }
34 | }
35 | },
36 | "content_scripts": [
37 | {
38 | "matches": [
39 | "https://www.youtube.com/*"
40 | ],
41 | "js": [
42 | "main/content.js"
43 | ]
44 | }
45 | ],
46 | "browser_specific_settings": {
47 | "gecko": {
48 | "id": "ytsiphon@f3.al",
49 | "strict_min_version": "109.0"
50 | }
51 | },
52 | "host_permissions": [
53 | "*://*.youtube.com/*"
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------