├── src ├── typescript │ ├── global.d.ts │ ├── util │ │ ├── cleanURL.ts │ │ └── constants.ts │ ├── action │ │ ├── index.ts │ │ ├── desktop.ts │ │ └── settings.ts │ └── background │ │ ├── redirectShortsPage.ts │ │ ├── handlePageUpdate.ts │ │ ├── modifyGeneralPage.ts │ │ ├── modifyYouTubePage.ts │ │ └── index.ts ├── vendors │ ├── common │ │ ├── assets │ │ │ ├── icon16.png │ │ │ ├── icon48.png │ │ │ ├── icon128.png │ │ │ └── icon.svg │ │ ├── netRequestRule.json │ │ ├── _locales │ │ │ ├── en │ │ │ │ └── messages.json │ │ │ └── fr │ │ │ │ └── messages.json │ │ └── index.html │ ├── firefox │ │ ├── background.html │ │ └── manifest.json │ └── chromium │ │ └── manifest.json ├── assets │ ├── Shorts Deflector Icon 128.png │ ├── Shorts Deflector Icon 16.png │ ├── Shorts Deflector Icon 48.png │ └── Shorts Deflector Icon.svg └── resources │ └── source.css ├── promo ├── Shorts Deflector Demo.pdn ├── Shorts Deflector Dark en.png ├── Shorts Deflector Demo en.png ├── Shorts Deflector Light en.png ├── Shorts Deflector Promo Tile 1400 560.pdn ├── Shorts Deflector Promo Tile 1400 560.png ├── Shorts Deflector Promo Tile 440 280.pdn ├── Shorts Deflector Promo Tile 440 280.png ├── Shorts Deflector Promo Tile 920 680.pdn └── Shorts Deflector Promo Tile 920 680.png ├── .github ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── Release.yml ├── tsconfig.json ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── .eslintrc.json ├── README.md ├── package.json └── tailwind.config.js /src/typescript/global.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | const browser: typeof chrome; 5 | } 6 | -------------------------------------------------------------------------------- /promo/Shorts Deflector Demo.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Demo.pdn -------------------------------------------------------------------------------- /promo/Shorts Deflector Dark en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Dark en.png -------------------------------------------------------------------------------- /promo/Shorts Deflector Demo en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Demo en.png -------------------------------------------------------------------------------- /promo/Shorts Deflector Light en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Light en.png -------------------------------------------------------------------------------- /src/typescript/util/cleanURL.ts: -------------------------------------------------------------------------------- 1 | export function cleanURL(url = '') { 2 | return url.replace('shorts/', 'watch?v='); 3 | } 4 | -------------------------------------------------------------------------------- /src/vendors/common/assets/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/src/vendors/common/assets/icon16.png -------------------------------------------------------------------------------- /src/vendors/common/assets/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/src/vendors/common/assets/icon48.png -------------------------------------------------------------------------------- /src/vendors/common/assets/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/src/vendors/common/assets/icon128.png -------------------------------------------------------------------------------- /src/assets/Shorts Deflector Icon 128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/src/assets/Shorts Deflector Icon 128.png -------------------------------------------------------------------------------- /src/assets/Shorts Deflector Icon 16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/src/assets/Shorts Deflector Icon 16.png -------------------------------------------------------------------------------- /src/assets/Shorts Deflector Icon 48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/src/assets/Shorts Deflector Icon 48.png -------------------------------------------------------------------------------- /promo/Shorts Deflector Promo Tile 1400 560.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Promo Tile 1400 560.pdn -------------------------------------------------------------------------------- /promo/Shorts Deflector Promo Tile 1400 560.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Promo Tile 1400 560.png -------------------------------------------------------------------------------- /promo/Shorts Deflector Promo Tile 440 280.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Promo Tile 440 280.pdn -------------------------------------------------------------------------------- /promo/Shorts Deflector Promo Tile 440 280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Promo Tile 440 280.png -------------------------------------------------------------------------------- /promo/Shorts Deflector Promo Tile 920 680.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Promo Tile 920 680.pdn -------------------------------------------------------------------------------- /promo/Shorts Deflector Promo Tile 920 680.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evenevan/shorts-deflector/HEAD/promo/Shorts Deflector Promo Tile 920 680.png -------------------------------------------------------------------------------- /src/vendors/firefox/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask any questions you have 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Question** 11 | 12 | **Additional context** 13 | Add any other context or screenshots about your question(s) here. 14 | -------------------------------------------------------------------------------- /src/typescript/action/index.ts: -------------------------------------------------------------------------------- 1 | import './desktop.js'; 2 | import './settings.js'; 3 | import { i18nKeys, runtime } from '../util/constants.js'; 4 | 5 | i18nKeys.forEach(([htmlKey, localeKey]) => { 6 | const element = document.getElementById(htmlKey); 7 | element!.textContent = runtime.i18n.getMessage(localeKey); 8 | }); 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "module": "ES2022", 6 | "allowJs": false, 7 | "checkJs": false, 8 | "exactOptionalPropertyTypes": false, 9 | "noPropertyAccessFromIndexSignature": false, 10 | "types": [ 11 | "chrome", 12 | ] 13 | } 14 | } -------------------------------------------------------------------------------- /src/typescript/background/redirectShortsPage.ts: -------------------------------------------------------------------------------- 1 | // This method is better than tabs.update because it doesn't leave the Shorts page in your history 2 | // Using tabs.goBack and then tabs.update, which does solve the history issue, is much slower 3 | 4 | export function redirectShortsPage() { 5 | const cleanURL = window.location.toString().replace('shorts/', 'watch?v='); 6 | window.location.replace(cleanURL); 7 | } 8 | -------------------------------------------------------------------------------- /src/vendors/common/assets/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/Shorts Deflector Icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/vendors/common/netRequestRule.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "priority": 1, 5 | "action": { 6 | "type": "redirect", 7 | "redirect": { 8 | "regexSubstitution": "https://www.youtube.com/watch?v=\\2" 9 | } 10 | }, 11 | "condition": { 12 | "regexFilter": "^https://(www\\.)?youtube\\.com/shorts/(.+)", 13 | "resourceTypes": [ 14 | "main_frame" 15 | ] 16 | } 17 | } 18 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | tmp 4 | src/vendors/chromium/_locales 5 | src/vendors/chromium/assets 6 | src/vendors/chromium/index.html 7 | src/vendors/chromium/netRequestRule.json 8 | src/vendors/chromium/resources 9 | src/vendors/chromium/scripts 10 | src/vendors/common/resources 11 | src/vendors/common/scripts 12 | src/vendors/firefox/_locales 13 | src/vendors/firefox/assets 14 | src/vendors/firefox/index.html 15 | src/vendors/firefox/netRequestRule.json 16 | src/vendors/firefox/resources 17 | src/vendors/firefox/scripts -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "endOfLine": "crlf", 7 | "htmlWhitespaceSensitivity": "strict", 8 | "insertPragma": false, 9 | "printWidth": 100, 10 | "proseWrap": "always", 11 | "quoteProps": "as-needed", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleAttributePerLine": false, 15 | "singleQuote": true, 16 | "rangeStart": 0, 17 | "rangeEnd": 2147483647, 18 | "tabWidth": 4, 19 | "trailingComma": "all", 20 | "useTabs": false 21 | } -------------------------------------------------------------------------------- /src/vendors/common/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionDescription": { 3 | "message": "Seamlessly play YouTube Shorts with the normal desktop interface" 4 | }, 5 | "desktopTitle": { 6 | "message": "Switch to Desktop Interface" 7 | }, 8 | "desktopTooltip": { 9 | "message": "Available on YouTube Shorts pages" 10 | }, 11 | "automaticTitle": { 12 | "message": "Automatic Switching" 13 | }, 14 | "automaticDescription": { 15 | "message": "Automatically switch YouTube Shorts to the desktop UI." 16 | }, 17 | "changeLinksTitle": { 18 | "message": "Change Links on Pages" 19 | }, 20 | "changeLinksDescription": { 21 | "message": "Change YouTube Shorts hyperlinks directly on pages. Requires extra permissions and has a performance impact." 22 | } 23 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | **Describe the bug** 9 | A clear and concise description of what the bug is. 10 | 11 | **Expected behavior** 12 | A clear and concise description of what you expected to happen. 13 | 14 | **Version** 15 | State the version of the extension you experienced the bug with. 16 | 17 | **Browser** 18 | State the browser version that you experienced the bug with. 19 | 20 | **Reproduction** 21 | Steps to reproduce the behavior: 22 | 23 | 1. Go to '...' 24 | 2. Click on '....' 25 | 3. Scroll down to '....' 26 | 4. See error 27 | 28 | **Screenshots** 29 | If applicable, include screenshots to help explain your problem. 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /src/vendors/common/_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionDescription": { 3 | "message": "Lisez les YouTube Shorts en toute transparence avec l'interface de bureau normale." 4 | }, 5 | "desktopTitle": { 6 | "message": "Passer à l'Interface du bureau" 7 | }, 8 | "desktopTooltip": { 9 | "message": "Disponible sur les pages des YouTube Shorts" 10 | }, 11 | "automaticTitle": { 12 | "message": "Changement automatique" 13 | }, 14 | "automaticDescription": { 15 | "message": "Changement automatiquement les YouTube Shorts à l'interface de bureau." 16 | }, 17 | "changeLinksTitle": { 18 | "message": "Modifier les hyperliens sur les pages web" 19 | }, 20 | "changeLinksDescription": { 21 | "message": "Modifiez les liens hypertextes des YouTube Shorts directement sur les pages web. Nécessite des autorisations supplémentaires et a un impact sur les performances." 22 | } 23 | } -------------------------------------------------------------------------------- /.github/workflows/Release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Version" 8 | required: true 9 | type: string 10 | 11 | permissions: 12 | contents: write 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Check Environment Variables 19 | run: env 20 | 21 | - name: Checkout Repository 22 | uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | 26 | - name: Use Node.js 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: 20 30 | 31 | - name: Install Dependencies 32 | run: npm ci 33 | 34 | - name: Build 35 | run: npm run build 36 | 37 | - name: Release 38 | uses: softprops/action-gh-release@v2 39 | with: 40 | files: ./dist/*.zip 41 | fail_on_unmatched_files: true 42 | tag_name: "${{ inputs.version }}" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Evan F 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 | -------------------------------------------------------------------------------- /src/resources/source.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .h1 { 6 | @apply text-base font-semibold; 7 | } 8 | 9 | .h2 { 10 | @apply text-sm font-normal; 11 | } 12 | 13 | .h3 { 14 | @apply text-xs font-normal; 15 | } 16 | 17 | .setting-title { 18 | @apply h1 truncate text-base font-semibold; 19 | } 20 | 21 | .description { 22 | @apply h2 text-light-2 dark:text-dark-2; 23 | } 24 | 25 | .toggle-container { 26 | @apply flex h-4 w-8 items-center justify-start; 27 | } 28 | 29 | .toggle-checkbox { 30 | @apply absolute z-40 h-4 w-8 appearance-none hover:cursor-pointer disabled:hover:cursor-not-allowed; 31 | } 32 | 33 | .toggle-background { 34 | @apply z-10 h-2 w-8 rounded-full bg-light-5 dark:bg-dark-5 peer-disabled:bg-light-4 peer-disabled:dark:bg-dark-3; 35 | } 36 | 37 | .toggle-foreground { 38 | @apply absolute z-20 h-2 w-2 rounded-full bg-youtube-red ease-in-out peer-checked:w-6 peer-hover:duration-150 peer-disabled:bg-youtube-red-disabled; 39 | } 40 | 41 | .toggle-handle { 42 | @apply absolute z-30 h-4 w-4 rounded-full bg-light-4 ease-in-out peer-checked:translate-x-4 peer-hover:duration-150 peer-disabled:bg-light-3 dark:bg-dark-6 peer-disabled:dark:bg-dark-4; 43 | } 44 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 2022, 8 | "sourceType": "module", 9 | "project": "./tsconfig.json" 10 | }, 11 | "extends": [ 12 | "airbnb-base", 13 | "airbnb-typescript/base" 14 | ], 15 | "rules": { 16 | "@typescript-eslint/indent": ["error", 4], 17 | "@typescript-eslint/no-use-before-define": [ 18 | "error", { 19 | "functions": false 20 | } 21 | ], 22 | "class-methods-use-this": ["off"], 23 | "eol-last": ["error", "always"], 24 | "function-paren-newline": ["error", "consistent"], 25 | "import/prefer-default-export": ["off"], 26 | "indent": "off", 27 | "linebreak-style": ["error", "windows"], 28 | "new-cap": [ 29 | "error", { 30 | "newIsCapExceptions": ["i18n"] 31 | } 32 | ], 33 | "no-console": ["off"], 34 | "object-curly-newline": ["error", { 35 | "consistent": true 36 | }], 37 | "object-shorthand": ["error", "never"], 38 | "sort-imports": ["error", { 39 | "ignoreCase": true, 40 | "ignoreDeclarationSort": true 41 | }] 42 | } 43 | } -------------------------------------------------------------------------------- /src/vendors/chromium/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "default_popup": "./index.html", 4 | "default_icon": { 5 | "16": "./assets/icon16.png", 6 | "48": "./assets/icon48.png", 7 | "128": "./assets/icon128.png" 8 | } 9 | }, 10 | "author": "Attituding", 11 | "background": { 12 | "service_worker": "./scripts/background/index.js", 13 | "type": "module" 14 | }, 15 | "declarative_net_request": { 16 | "rule_resources": [ 17 | { 18 | "id": "shorts", 19 | "enabled": true, 20 | "path": "./netRequestRule.json" 21 | } 22 | ] 23 | }, 24 | "default_locale": "en", 25 | "description": "__MSG_extensionDescription__", 26 | "host_permissions": [ 27 | "https://www.youtube.com/*" 28 | ], 29 | "icons": { 30 | "16": "./assets/icon16.png", 31 | "48": "./assets/icon48.png", 32 | "128": "./assets/icon128.png" 33 | }, 34 | "manifest_version": 3, 35 | "minimum_chrome_version": "102", 36 | "name": "Shorts Deflector", 37 | "optional_host_permissions": [ 38 | "*://*/*" 39 | ], 40 | "permissions": [ 41 | "declarativeNetRequestWithHostAccess", 42 | "scripting", 43 | "storage", 44 | "webNavigation" 45 | ], 46 | "version": "1.17.0" 47 | } -------------------------------------------------------------------------------- /src/typescript/util/constants.ts: -------------------------------------------------------------------------------- 1 | // HTML Ids 2 | export const automaticHTMLKey = 'automatic'; 3 | export const desktopHTMLKey = 'desktop'; 4 | export const dynamicHTMLKey = 'dynamic'; 5 | export const improvePerformanceHTMLKey = 'improve-performance'; 6 | export const linkHTMLKey = 'link'; 7 | 8 | export const i18nKeys: [string, string][] = [ 9 | ['desktop-tooltip', 'desktopTooltip'], 10 | ['desktop-title', 'desktopTitle'], 11 | ['automatic-title', 'automaticTitle'], 12 | ['automatic-description', 'automaticDescription'], 13 | ['change-links-title', 'changeLinksTitle'], 14 | ['change-links-description', 'changeLinksDescription'], 15 | ]; 16 | 17 | // browser.storage Keys 18 | export const automaticStorageKey = 'automatic'; 19 | // improvePerformance is the legacy name for changeLinks 20 | export const changeLinksStorageKey = 'improvePerformance'; 21 | 22 | // Regex 23 | export const youTubeRegex = /^http(s)?:\/\/www\.youtube\.com/; 24 | export const youTubeShortsRegex = /^http(s)?:\/\/www\.youtube\.com\/shorts\/(.+)$/; 25 | 26 | // Rulesets 27 | export const shortsRuleset = 'shorts'; 28 | 29 | // Hostnames 30 | export const youTubeHostname = 'https://www.youtube.com/*'; 31 | export const allHostname = '*://*/*'; 32 | 33 | // Runtime selection 34 | // eslint-disable-next-line import/no-mutable-exports 35 | export let runtime = chrome; 36 | 37 | try { 38 | runtime = browser; 39 | // eslint-disable-next-line no-empty 40 | } catch { } 41 | -------------------------------------------------------------------------------- /src/vendors/firefox/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "default_popup": "./index.html", 4 | "default_icon": { 5 | "16": "./assets/icon.svg", 6 | "48": "./assets/icon.svg", 7 | "128": "./assets/icon.svg" 8 | } 9 | }, 10 | "author": "Attituding", 11 | "background": { 12 | "page": "./background.html" 13 | }, 14 | "browser_specific_settings": { 15 | "gecko": { 16 | "id": "shortsdeflector@addons.com", 17 | "strict_min_version": "113.0" 18 | } 19 | }, 20 | "declarative_net_request": { 21 | "rule_resources": [ 22 | { 23 | "id": "shorts", 24 | "enabled": true, 25 | "path": "./netRequestRule.json" 26 | } 27 | ] 28 | }, 29 | "default_locale": "en", 30 | "description": "__MSG_extensionDescription__", 31 | "host_permissions": [ 32 | "https://www.youtube.com/*" 33 | ], 34 | "icons": { 35 | "16": "./assets/icon.svg", 36 | "48": "./assets/icon.svg", 37 | "128": "./assets/icon.svg" 38 | }, 39 | "manifest_version": 3, 40 | "name": "Shorts Deflector", 41 | "optional_permissions": [ 42 | "*://*/*" 43 | ], 44 | "permissions": [ 45 | "declarativeNetRequestWithHostAccess", 46 | "scripting", 47 | "storage", 48 | "webNavigation" 49 | ], 50 | "version": "1.17.0" 51 | } -------------------------------------------------------------------------------- /src/typescript/background/handlePageUpdate.ts: -------------------------------------------------------------------------------- 1 | import { modifyGeneralPage } from './modifyGeneralPage.js'; 2 | import { modifyYouTubePage } from './modifyYouTubePage.js'; 3 | import { redirectShortsPage } from './redirectShortsPage.js'; 4 | import { 5 | automaticStorageKey, 6 | changeLinksStorageKey, 7 | runtime, 8 | youTubeRegex, 9 | youTubeShortsRegex, 10 | } from '../util/constants.js'; 11 | 12 | export async function handlePageUpdate(tabId: number, tab: chrome.tabs.Tab) { 13 | const { 14 | [automaticStorageKey]: automatic, 15 | [changeLinksStorageKey]: improvePerformance, 16 | } = await runtime.storage.sync.get([ 17 | automaticStorageKey, 18 | changeLinksStorageKey, 19 | ]); 20 | 21 | if ( 22 | automatic === false 23 | || (improvePerformance === false && youTubeRegex.test(tab.url!) === false) 24 | ) { 25 | return; 26 | } 27 | 28 | const url = youTubeShortsRegex.test(tab.url!); 29 | 30 | if (url) { 31 | // Redirecting 32 | 33 | await runtime.scripting.executeScript({ 34 | // @ts-ignore 35 | injectImmediately: true, 36 | target: { 37 | tabId: tabId, 38 | }, 39 | func: redirectShortsPage, 40 | }); 41 | } else { 42 | // URL Updating 43 | 44 | const script = youTubeRegex.test(tab.url!) ? modifyYouTubePage : modifyGeneralPage; 45 | 46 | await runtime.scripting.executeScript({ 47 | target: { 48 | tabId: tabId, 49 | }, 50 | func: script, 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Why does clicking on a YouTube Short on a desktop bring up the mobile-optimized interface? With no video scrubbing, harder to access comments, less accessible view count, and more, it makes no sense to use on a desktop. 4 | 5 | Shorts Deflector is an extension that allows you to seamlessly watch YouTube Shorts with the normal desktop interface. By using several techniques, this extension is super fast; you will barely notice the switch to the desktop viewer. Install now and try it out for yourself! 6 | 7 | Upon install, the "Read your browsing history" permission may seem alarming, but this permission is only used to detect page updates. 8 | 9 | Read more at https://evenevan.github.io/shorts-deflector/. 10 | 11 | # Home Page 12 | 13 | [https://evenevan.github.io/shorts-deflector/](https://evenevan.github.io/shorts-deflector/) 14 | 15 | # Install 16 | 17 | [](https://chrome.google.com/webstore/detail/shorts-deflector/gilmponliddppjjcfjmanmmfgiilikhg) [](https://addons.mozilla.org/firefox/addon/shorts-deflector/) 18 | 19 | # Contribution 20 | 21 | Want to help improve this extension? 22 | 23 | 1) Fork this project 24 | 2) Make your changes and commit/push to your fork 25 | 3) [Build](#build) 26 | 4) Test out your changes 27 | 5) Make a pull request 28 | 29 | # Build 30 | 31 | ## Prerequisites (available via cli): 32 | 33 | - Version of Node.js >= v18 w/ NPM 34 | - Bash 35 | - 7zip 36 | 37 | ## Steps: 38 | 39 | 1) Open terminal with this directory 40 | 2) Run "npm i" 41 | 3) Run "npm run build" 42 | 4) Output zips will appear in /dist folder 43 | 44 | # Develop 45 | 46 | ## Firefox 47 | 48 | Use 49 | 50 | ``` 51 | npm run firefox 52 | ``` 53 | -------------------------------------------------------------------------------- /src/typescript/action/desktop.ts: -------------------------------------------------------------------------------- 1 | import { cleanURL } from '../util/cleanURL.js'; 2 | import { 3 | desktopHTMLKey, 4 | dynamicHTMLKey, 5 | linkHTMLKey, 6 | runtime, 7 | youTubeShortsRegex, 8 | } from '../util/constants.js'; 9 | 10 | const desktopButton = document.getElementById(desktopHTMLKey) as HTMLButtonElement; 11 | const linkAnchor = document.getElementById(linkHTMLKey) as HTMLAnchorElement; 12 | const dynamicDiv = document.getElementById(dynamicHTMLKey) as HTMLDivElement; 13 | 14 | let [tab] = await runtime.tabs.query({ 15 | active: true, 16 | currentWindow: true, 17 | }); 18 | 19 | update(); 20 | 21 | runtime.tabs.onUpdated.addListener((_id, _changes, newTab) => { 22 | if (newTab.id === tab?.id) { 23 | tab = newTab; 24 | 25 | update(); 26 | } 27 | }); 28 | 29 | desktopButton.addEventListener('click', async () => { 30 | loading(); 31 | 32 | await runtime.tabs.update((tab!.id!), { 33 | url: cleanURL(tab?.url), 34 | }); 35 | }); 36 | 37 | function update() { 38 | if (tab?.status === 'complete') { 39 | loaded(); 40 | } else { 41 | loading(); 42 | } 43 | } 44 | 45 | function loading() { 46 | desktopButton.disabled = true; 47 | dynamicDiv.dataset.state = 'loading'; 48 | linkAnchor.removeAttribute('href'); 49 | linkAnchor.setAttribute('aria-disabled', 'true'); 50 | } 51 | 52 | function loaded() { 53 | const isNotYouTubeShortsPage = !tab?.url?.match(youTubeShortsRegex); 54 | 55 | desktopButton.disabled = isNotYouTubeShortsPage; 56 | dynamicDiv.dataset.state = 'link'; 57 | linkAnchor.setAttribute('aria-disabled', isNotYouTubeShortsPage.toString()); 58 | 59 | if (isNotYouTubeShortsPage) { 60 | linkAnchor.removeAttribute('href'); 61 | linkAnchor.setAttribute('tabIndex', '-1'); 62 | } else { 63 | linkAnchor.setAttribute('href', cleanURL(tab?.url)); 64 | linkAnchor.removeAttribute('tabIndex'); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shorts-deflector", 3 | "description": "Seamlessly play YouTube Shorts with the normal desktop interface.", 4 | "license": "MIT", 5 | "author": "Evan F