├── .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 | YT Siphon Logo 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 | ![Ad Blocker Not Allowed](screenshots/adblocker-notallowed.jpg) 20 | ![Ad Blocker TOS](screenshots/adblocker-tos.png) 21 | ![Ad Blocker Counter](screenshots/adblocker-counter.png) 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 | --------------------------------------------------------------------------------