├── .gitignore ├── icons ├── icon_16.ico ├── icon_16.png ├── icon_32.png ├── icon_48.png └── icon_128.png ├── public ├── error.html ├── popup.css └── popup.html ├── scripts ├── background.js ├── popup.js └── content.js ├── manifest.json ├── manifest_firefox.json ├── LICENSE ├── documentation ├── Features.md └── ReleaseNotes.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /icons/icon_16.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pedro-Gregorio/TubeMod/HEAD/icons/icon_16.ico -------------------------------------------------------------------------------- /icons/icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pedro-Gregorio/TubeMod/HEAD/icons/icon_16.png -------------------------------------------------------------------------------- /icons/icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pedro-Gregorio/TubeMod/HEAD/icons/icon_32.png -------------------------------------------------------------------------------- /icons/icon_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pedro-Gregorio/TubeMod/HEAD/icons/icon_48.png -------------------------------------------------------------------------------- /icons/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pedro-Gregorio/TubeMod/HEAD/icons/icon_128.png -------------------------------------------------------------------------------- /public/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

TubeMod

11 |

Open YouTube to use this extension.

12 |
13 |

Current Version 1.13.0

14 | 15 | 16 | -------------------------------------------------------------------------------- /scripts/background.js: -------------------------------------------------------------------------------- 1 | chrome.tabs.onActivated.addListener((activeInfo) => { 2 | chrome.tabs.get(activeInfo.tabId, (tab) => { 3 | if (tab.url && tab.url.includes("youtube.com")) { 4 | chrome.action.setPopup({ popup: "../public/popup.html" }); 5 | } else { 6 | chrome.action.setPopup({ popup: "../public/error.html" }); 7 | } 8 | }); 9 | }); 10 | 11 | chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { 12 | if (changeInfo.status === "complete" && tab.url) { 13 | if (tab.url.includes("youtube.com")) { 14 | chrome.action.setPopup({ popup: "../public/popup.html" }); 15 | } else { 16 | chrome.action.setPopup({ popup: "" }); 17 | } 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "TubeMod", 4 | "version": "1.13.0", 5 | "description": "TubeMod allows you to customize YouTube's UI to your preferences, by keeping you focused on what matters!", 6 | "action": { 7 | "default_popup": "./public/popup.html" 8 | }, 9 | "icons": { 10 | "16": "/icons/icon_16.png", 11 | "32": "/icons/icon_32.png", 12 | "48": "/icons/icon_48.png", 13 | "128": "/icons/icon_128.png" 14 | }, 15 | "permissions": ["tabs", "activeTab", "storage"], 16 | "background": { 17 | "service_worker": "./scripts/background.js" 18 | }, 19 | "content_scripts": [ 20 | { 21 | "matches": ["*://www.youtube.com/*", "*://m.youtube.com/*"], 22 | "js": ["./scripts/content.js"], 23 | "run_at": "document_start" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /manifest_firefox.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "TubeMod", 4 | "version": "1.13.0", 5 | "description": "TubeMod allows you to customize YouTube's UI to your preferences, by keeping you focused on what matters!", 6 | "action": { 7 | "default_popup": "./public/popup.html" 8 | }, 9 | "icons": { 10 | "16": "/icons/icon_16.png", 11 | "32": "/icons/icon_32.png", 12 | "48": "/icons/icon_48.png", 13 | "128": "/icons/icon_128.png" 14 | }, 15 | "permissions": ["tabs", "activeTab", "storage"], 16 | "background": { 17 | "scripts": ["./scripts/background.js"] 18 | }, 19 | "content_scripts": [ 20 | { 21 | "matches": ["*://www.youtube.com/*", "*://m.youtube.com/*"], 22 | "js": ["./scripts/content.js"], 23 | "run_at": "document_start" 24 | } 25 | ], 26 | "browser_specific_settings": { 27 | "gecko": { 28 | "id": "tubemod@extension.com" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Pedro Gregório 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 | -------------------------------------------------------------------------------- /scripts/popup.js: -------------------------------------------------------------------------------- 1 | let inputs = document.querySelectorAll("input"); 2 | let collapsibleElements = document.getElementsByClassName("collapsible"); 3 | 4 | document.addEventListener("DOMContentLoaded", () => { 5 | chrome.storage.local.get(["tubemod_elements"], (result) => { 6 | const elements = result.tubemod_elements 7 | ? JSON.parse(result.tubemod_elements) 8 | : null; 9 | 10 | if (elements !== null) { 11 | elements.forEach((element) => { 12 | if (document.getElementById(element.id)) { 13 | document.getElementById(element.id).checked = element.checked; 14 | } 15 | }); 16 | } 17 | 18 | for (i = 0; i < collapsibleElements.length; i++) { 19 | collapsibleElements[i].addEventListener("click", function () { 20 | this.classList.toggle("active"); 21 | var content = this.nextElementSibling; 22 | if (content.style.display === "block") { 23 | content.style.display = "none"; 24 | } else { 25 | content.style.display = "block"; 26 | } 27 | }); 28 | } 29 | 30 | inputs.forEach((element) => { 31 | element.addEventListener("change", () => { 32 | chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { 33 | chrome.tabs.sendMessage(tabs[0].id, { 34 | action: { 35 | target: element.id, 36 | hide: element.checked, 37 | }, 38 | }); 39 | }); 40 | }); 41 | }); 42 | }); 43 | }); 44 | 45 | chrome.runtime.onMessage.addListener(function (message) { 46 | if (message.type === "popup") { 47 | chrome.storage.local.set({ elements: JSON.stringify(message.data) }); 48 | } 49 | }); 50 | 51 | document.getElementById("reset-settings").addEventListener("click", () => { 52 | chrome.storage.local.clear(() => { 53 | console.info("Settings cleared."); 54 | }); 55 | chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { 56 | chrome.tabs.sendMessage(tabs[0].id, { 57 | action: "clearLocalStorage", 58 | }); 59 | }); 60 | window.close(); 61 | }); 62 | 63 | document.getElementById("save-settings").addEventListener("click", () => { 64 | chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { 65 | chrome.tabs.sendMessage(tabs[0].id, { 66 | action: "saveSettings", 67 | }); 68 | }); 69 | }); 70 | 71 | document 72 | .getElementById("import-settings") 73 | .addEventListener("change", async (e) => { 74 | let file = e.target.files.item(0); 75 | 76 | const text = await file.text(); 77 | chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { 78 | chrome.tabs.sendMessage(tabs[0].id, { 79 | action: "importSettings", 80 | content: text, 81 | }); 82 | }); 83 | }); 84 | 85 | // [...document.querySelectorAll('#sidebar input')].every(checkbox => checkbox.checked) -> if all the checkboxes are checked, we may want to collapse the sidebar or simply remove the left margin 86 | -------------------------------------------------------------------------------- /documentation/Features.md: -------------------------------------------------------------------------------- 1 | # TubeMod - Features 2 | 3 | This file contains the currently available features, as well as planned ones - according to requests. 4 | 5 | ## Planned Features 6 | 7 | These are the features currently planned for future releases. More will be added upon users requests. 8 | 9 | - Allow Extension Popup to be placed anywhere / moved around 10 | - Remove Rounded borders across YouTube 11 | - Hide the Likes counter, instead of the button itself 12 | 13 | In later versions of TubeMod (2.0.0+), actions and themes will be introduced: 14 | 15 | - Automatically Expand Video Description 16 | - Automatically Copy the current Video URL 17 | - Automatically set the video to Theatre Mode 18 | - Automatically set the video playback speed 19 | - Access directly to the "Videos" tab, when visiting a specific Channel 20 | - Allow users to change Themes - customizing YouTube's UI even further 21 | 22 | ## Features 23 | 24 | ### General 25 | 26 | These changes affect every page on YouTube. 27 | 28 | - Hide Scheduled Videos 29 | - Hide Livestreams 30 | - Hide Video Previews 31 | - Hide Video Thumbnails 32 | - Hide Video Extra Information (Views and Posted Time) 33 | 34 | ### Header 35 | 36 | These apply to the header of YouTube - visible across every page. 37 | 38 | - Hide YouTube Logo 39 | - Hide Search Bar 40 | - Hide Microphone Search Icon 41 | - Hide Create Icon 42 | - Hide Notifications Icon 43 | 44 | ### Sidebar 45 | 46 | These changes are applied to YouTube's Sidebar. 47 | 48 | - Hide Home Page Button 49 | - Hide Shorts Button 50 | - Hide Subscriptions Button 51 | - Hide YouTube Music Button, for Premium users 52 | - Hide You Button 53 | - Hide Your Channel Button 54 | - Hide History Button 55 | - Hide Playlists Button 56 | - Hide Your Videos Button 57 | - Hide Your Movies and TV Button 58 | - Hide Your Podcasts Button 59 | - Hide Your Courses Button 60 | - Hide Watch Later Button 61 | - Hide Liked Videos Button 62 | - Hide Downloads Button, for Premium Users 63 | - Hide My Clips Button 64 | - Hide Downloads Button, for Premium users 65 | - Hide Subscriptions Panel 66 | - Hide Explore Panel 67 | - Hide YouTube Content Panel 68 | - Hide YouTube Settings Panel 69 | - Hide YouTube Footer Panel 70 | 71 | ### Home Page 72 | 73 | These options modify YouTube's Home Page. 74 | 75 | - Hide Tabs 76 | - Hide Ads Panel 77 | - Hide Posts / Shorts / News 78 | 79 | ### Subscriptions 80 | 81 | These changes apply when you access the Subscriptions page. 82 | 83 | - Hide Shorts Section 84 | 85 | ### Video 86 | 87 | These changes take effect when you open a new YouTube video. 88 | 89 | - Hide Title 90 | - Hide Subscribers Count 91 | - Hide Subscribed Button (when you're subscribed to a channel) 92 | - Hide Likes/Dislikes 93 | - Hide Share Video Button 94 | - Hide Download Button 95 | - Hide Extra Buttons (Clip, Save, Donate, etc.) 96 | - Hide Description 97 | - Hide Views Count 98 | - Hide Chapters, Transcript and Links in the Description 99 | - Hide Categories/Games 100 | - Hide Comments 101 | - Hide Tabs 102 | - Hide Ads Panel 103 | - Hide Offer Module 104 | - Hide Suggested Videos 105 | - Hide Shorts on Suggested Videos 106 | - Hide Suggestion Wall, in the end of the video 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TubeMod 2 | 3 | ## What is it? 4 | 5 | TubeMod is a Browser Extension that allows users to hide specific YouTube elements, giving them more control over their viewing experience. Rather than permanently removing elements, TubeMod provides simple toggles to hide parts of YouTube’s UI, making it a lightweight and customizable solution. It is planned, for a future release, allowing users to completely remove elements, instead of simply hiding them. 6 | 7 | ## How to Use 8 | 9 | After installing the extension: 10 | 11 | 1. Click on the TubeMod icon in your browser toolbar. 12 | 2. Toggle the YouTube elements you want to hide via the popup interface. 13 | 3. Changes will be applied instantly. Enjoy your customized YouTube experience! 14 | 15 | ## How to Install 16 | 17 | ### From the Chrome Web Store 18 | 19 | 1. Go to the [TubeMod Extension Page on Chrome Web Store](https://chromewebstore.google.com/detail/tubemod/mhhalndcidpfcemnlidabgieccknndei). 20 | 2. Click 'Add to _Your Browser_' and follow the prompts. 21 | 22 | ### From the FireFox Add-Ons 23 | 24 | 1. Go to the [TubeMod Extension Page on Firefox Add Ons](https://addons.mozilla.org/en-GB/firefox/addon/tubemod/). 25 | 2. Click 'Add to Firefox' and follow the prompts. 26 | 27 | ## How to set up Dev 28 | 29 | 1. Clone the repository 30 | 31 | ``` 32 | git clone https://github.com/Pedro-Gregorio/TubeMod.git 33 | cd TubeMod 34 | ``` 35 | 36 | If the clone command doesn't work just download the repository as a ZIP file and extract it to your desired location. 37 | 38 | Depending on your browser you will have to take different routes: 39 | 40 | ### For Chromium based browsers: 41 | 42 | 2. Go to `chrome://extensions`. The "chrome" part might be changed to the name of the browser. 43 | 3. Enable the developer mode toggle. 44 | 4. Click on "Load unpacked". 45 | 5. Select the folder where the repository is (it should contain the manifest.json) 46 | 6. Every time you make a change either reload the extension or remove it an add it again. The latter works better when dealing with js parts of the code. 47 | 48 | ### For Firefox based browsers: 49 | 50 | 2. Change the name of the `manifest.json` to something like `manifest_chrome.json` to avoid conflicts. 51 | 3. Also rename `manifest_firefox.json` to `manifest.json`. 52 | 4. On Firefox set it up as a temporary add-on by going to `about:debugging` and on the "Load Temporary Addon" click add and select any file of the repository. 53 | 5. Every time you make a change either reload the temporary add-on or remove it and add it again. The latter works better when dealing with js parts of the code. 54 | 6. Before pushing your changes to GitHub, make sure to revert the name changes in the manifests. 55 | 56 | Before pushing to GitHub, please update [the release notes](./documentation/ReleaseNotes.md) with the feature you're adding. 57 | 58 | Some things to consider: 59 | 60 | - Keep your commit messages clean by starting with `Add`, `Change`, `Fix` or `Remove`, depending on what you're doing. 61 | - If possible, add evidences, of what you're working on, to the PR message. 62 | 63 | ## Features and Future Plans 64 | 65 | Check all the available and planned features [here](./documentation/Features.md). 66 | 67 | ## How can I support the project? 68 | 69 | The extension will always remain free to use. While contributions are not required, any support is greatly appreciated! If you'd like to contribute, you can: 70 | 71 | - Buy me a coffee on [Ko-Fi](https://ko-fi.com/pedrogregorio). 72 | - Share the extension with others or leave a review. 73 | - Submit feedback, report bugs, or suggest new features via the Issues section on GitHub or via [email](mailto:pedro-gregorio@outlook.pt). 74 | 75 | ## License 76 | 77 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 78 | -------------------------------------------------------------------------------- /public/popup.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-background: #1c1b1c; 3 | --color-primary: #bf3131; 4 | --color-secondary: #e36464; 5 | --color-tertiary: #d8a25e; 6 | --color-accent: #eeeeee; 7 | } 8 | 9 | * { 10 | background-color: var(--color-background); 11 | color: var(--color-accent); 12 | } 13 | 14 | body { 15 | width: 25rem; 16 | padding: 0 1rem; 17 | } 18 | 19 | label { 20 | cursor: pointer; 21 | } 22 | 23 | .title-container { 24 | margin-bottom: 1rem; 25 | } 26 | 27 | .title-container h1, 28 | .error-container h1 { 29 | font-size: 30px; 30 | font-weight: bold; 31 | text-align: center; 32 | color: var(--color-primary); 33 | } 34 | 35 | .title-container, 36 | .error-container h3 { 37 | font-size: 14px; 38 | padding-left: 1rem; 39 | } 40 | 41 | .error-container { 42 | margin: 3rem 0rem; 43 | } 44 | 45 | .settings-title-container { 46 | padding: 0rem 1rem; 47 | } 48 | 49 | .container { 50 | display: flex; 51 | flex-direction: column; 52 | align-items: start; 53 | justify-content: center; 54 | margin-bottom: 10px; 55 | padding-left: 1rem; 56 | padding-right: 1rem; 57 | -ms-overflow-style: none !important; /* Internet Explorer 10+ */ 58 | scrollbar-width: none !important; /* Firefox */ 59 | } 60 | 61 | .container::-webkit-scrollbar { 62 | display: none !important; /* Safari and Chrome */ 63 | } 64 | 65 | .checkbox-container { 66 | width: 100%; 67 | display: flex; 68 | align-items: center; 69 | justify-content: space-between; 70 | margin-top: 4px; 71 | padding: 2px 4px; 72 | } 73 | 74 | .checkbox-container label { 75 | font-size: 14px; 76 | font-weight: bold; 77 | } 78 | 79 | .version-container { 80 | padding: 0rem 1rem; 81 | text-align: end; 82 | } 83 | 84 | .switch { 85 | position: relative; 86 | display: inline-block; 87 | min-width: 50px; 88 | height: 30px; 89 | outline: none; 90 | } 91 | 92 | .switch input { 93 | opacity: 0; 94 | width: 0; 95 | height: 0; 96 | } 97 | 98 | .slider { 99 | position: absolute; 100 | cursor: pointer; 101 | top: 0; 102 | left: 0; 103 | right: 0; 104 | bottom: 0; 105 | background-color: var(--color-accent); 106 | -webkit-transition: 0.4s; 107 | transition: 0.4s; 108 | outline: none; 109 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 110 | } 111 | 112 | .slider:before { 113 | position: absolute; 114 | content: ""; 115 | height: 22px; 116 | width: 22px; 117 | left: 4px; 118 | bottom: 4px; 119 | background-color: var(--color-background); 120 | -webkit-transition: 0.4s; 121 | transition: 0.4s; 122 | outline: none; 123 | } 124 | 125 | input:checked + .slider { 126 | background-color: var(--color-tertiary); 127 | } 128 | 129 | input:focus + .slider { 130 | box-shadow: 0 0 1px var(--color-tertiary); 131 | } 132 | 133 | input:checked + .slider:before { 134 | -webkit-transform: translateX(21px); 135 | -ms-transform: translateX(21px); 136 | transform: translateX(21px); 137 | } 138 | 139 | .slider.round { 140 | border-radius: 34px; 141 | } 142 | 143 | .slider.round:before { 144 | border-radius: 50%; 145 | } 146 | 147 | .collapsible { 148 | padding-left: 1rem; 149 | cursor: pointer; 150 | width: 100%; 151 | outline: none; 152 | display: block; 153 | gap: 10px; 154 | font-size: 16px; 155 | font-weight: bold; 156 | color: var(--color-accent); 157 | background-color: var(--color-primary); 158 | padding: 8px; 159 | border-radius: 12px; 160 | border: none; 161 | margin-bottom: 10px; 162 | -webkit-transition: 0.4s; 163 | transition: 0.4s; 164 | } 165 | 166 | .collapsible:hover { 167 | background-color: var(--color-secondary); 168 | color: var(--color-accent); 169 | } 170 | 171 | .active { 172 | background-color: var(--color-tertiary); 173 | color: var(--color-background); 174 | } 175 | 176 | .btn-settings-container { 177 | margin-top: 1rem; 178 | width: 100%; 179 | display: flex; 180 | justify-content: space-around; 181 | align-items: end; 182 | gap: 6px; 183 | } 184 | 185 | .button { 186 | cursor: pointer; 187 | padding: 6px; 188 | outline: none; 189 | border: 1px solid var(--color-secondary); 190 | font-weight: bold; 191 | -webkit-transition: 0.4s; 192 | transition: 0.4s; 193 | width: 35%; 194 | border-radius: 3px; 195 | font-size: 12px; 196 | text-align: center; 197 | } 198 | 199 | .button:hover { 200 | background-color: var(--color-secondary); 201 | 202 | a { 203 | background-color: var(--color-secondary); 204 | } 205 | } 206 | 207 | .button a { 208 | -webkit-transition: 0.4s; 209 | transition: 0.4s; 210 | text-decoration: none; 211 | } 212 | 213 | .button:hover .version { 214 | text-align: right; 215 | } 216 | 217 | .version { 218 | text-align: right; 219 | } 220 | -------------------------------------------------------------------------------- /documentation/ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # TubeMod - Release Notes 2 | 3 | ## 1.13.0 4 | 5 | Add: 6 | 7 | - Home: 8 | - Hide Members Only Content 9 | - Hide Primetime Movies for You Section 10 | - Hide Partially Viewed Videos 11 | 12 | - Channel (new): 13 | - Pause Channel Trailer 14 | 15 | - Videos 16 | - Hide 'New' Badge under recommended videos 17 | 18 | - Downloads: 19 | - Hide 'Downloaded' badge beneath downloaded videos 20 | 21 | 22 | Fix: 23 | 24 | - General 25 | - Hide Thumbnails 26 | - Blur Thumbnails 27 | 28 | ## 1.12.0 29 | 30 | Add: 31 | 32 | - General: 33 | - Set all video titles to lowercase 34 | 35 | - Header: 36 | - Add Youtube Studio Icon next to the Notification Icon 37 | 38 | - Sidebar: 39 | - Hide Sidebar 40 | 41 | - Home: 42 | - Hide Youtube Playables 43 | - Hide Trending 44 | - Hide Breaking News 45 | 46 | - Video: 47 | - Hide 'Playback Speed' Video Settings Option 48 | - Hide Video Progress Bar dot 49 | - Hide 'Thanks' Button 50 | - Hide 'Clip' Button 51 | - Hide 'Save' Button 52 | - Hide Progress Bar Gradient 53 | - Hide Comments Replies 54 | 55 | Fix: 56 | 57 | - Hiding Home Page tabs no longer cuts the top thumbnails; 58 | - Selector being used to hide the Sleep Timer element is properly defined now; 59 | 60 | Remove: 61 | 62 | - Video: 63 | - Hide 'Extra Buttons' Option was removed, but individual options were added. 64 | 65 | ## 1.11.0 66 | 67 | Add: 68 | 69 | - Home: 70 | - Hide Video Ad (included in the Hide Sponsored Content toggle) 71 | 72 | - Video: 73 | - Hide 'Avatars' on video comments 74 | 75 | Fix: 76 | 77 | - Header 78 | - Hide Search Bar and Create Icon are again working properly. 79 | 80 | ## 1.10.0 81 | 82 | Add: 83 | 84 | - Sidebar: 85 | - Hide 'Your Podcasts' 86 | 87 | - Home: 88 | - Hide 'Podcasts' and 'Playlists' 89 | 90 | - Video 91 | - Hide 'Sleep Timer' Video Settings Option 92 | - Hide 'Shorts remixing this video' in Video Description 93 | - Hide 'People mentioned' in Video Description 94 | - Hide 'AI-generated video summary' 95 | - Hide 'Watch on YouTube' Offer Module 96 | - Show (not hide 😉) Video Thumbnail next to the Video Player 97 | 98 | Fix: 99 | 100 | - Playlist mixes on the Home Page weren't being properly hidden. 101 | - Hiding all videos on the Home Page would also cause videos on the subscription page to hide, by going back and forth. 102 | 103 | ## 1.9.0 104 | 105 | Add: 106 | 107 | - Sidebar: 108 | - Hide 'New' (Blue Dot) Indicator on Channels 109 | 110 | - Home: 111 | - Hide 'Posts' Feature 112 | - Hide 'Shorts' Feature 113 | - Hide Already Viewed Videos (100%) 114 | 115 | - Video: 116 | - Hide 'Show Chat' Button on Live Streams 117 | 118 | Remove: 119 | 120 | - Sidebar: 121 | - Hide 'Your Channel' Feature 122 | 123 | Fix: 124 | 125 | - Hiding Scheduled Videos no longer hides posts 126 | - Issue with Hiding Video Views 127 | 128 | ## 1.8.0 129 | 130 | Add: 131 | 132 | - Home: 133 | - Hide All Videos 134 | 135 | - Video: 136 | - Hide Join Button 137 | - Hide Collapsed Actions Button 138 | 139 | Fix: 140 | 141 | - Suggestion Wall (on the end of the video) wasn't properly formatted when using TubeMod 142 | - Preventing Views from showing again if hidden 143 | 144 | ## 1.7.0 145 | 146 | Add: 147 | 148 | - General: 149 | - Blur Video Thumbnails 150 | 151 | - Profile Settings: 152 | - Hide 'Sign Out' Button 153 | 154 | - Home: 155 | - Hide Mix Playlists 156 | 157 | - Search: 158 | - Hide Shorts 159 | - Hide 'People Also Search For' 160 | 161 | - Trending: 162 | - Hide Shorts 163 | 164 | - Video: 165 | - Hide Subscribe Button 166 | - Hide Video Live Chat Replay 167 | 168 | - Import / Export TubeMod Settings 169 | 170 | Fix: 171 | 172 | - Toggles now maintain their width regardless of label length 173 | - When removing everything from the sidebar, no more horizontal bars are shown 174 | 175 | ## 1.6.0 176 | 177 | Add: 178 | 179 | - General: 180 | 181 | - Hide Live Videos (Streams) 182 | - Hide Video Previews 183 | - Hide Thumbnails (both videos and playlists) 184 | - Hide Video Extra Information - Views and Posted Time 185 | 186 | - Header: 187 | 188 | - Hide Search Bar 189 | 190 | - Video: 191 | - Hide Subscribed Button (when you're subscribed to a channel) 192 | - Hide Download Button 193 | - Hide Extra Buttons (besides Share and Download) 194 | 195 | Fix: 196 | 197 | - Miscellaneous 198 | - Fixed an issue where users had to reset the settings every time a newer version was released. 199 | 200 | - Sidebar 201 | - Fixed an issue with the Sidebar Footer, as it was messing up with posts creation. [View the issue On GitHub](https://github.com/Pedro-Gregorio/TubeMod/issues/60). 202 | 203 | ## 1.5.0 204 | 205 | ### **Note from Pedro**: 206 | 207 | #### Regarding Performance 208 | 209 | 1.5.0 introduces a big performance upgrade, in comparison to 1.4.0. From what I've tested, having the extension on or off is the same, when it comes to components loading in the page. In the next few weeks, I'll study and think if it makes sense to remove elements, instead of simply hiding them - perhaps even give the users the option to choose between deleting elements, requiring a reload on unchecking, or simply hiding them, which requires no reload to show them again, but keeps them "loading" in the background (comments or suggested content in a video, for example). 210 | 211 | #### Regarding Actions 212 | 213 | 1.5.0 removes two actions, which could be performed: automatically expand the description and hide the Stream chat. TubeMod will focus on UI customization for now, leaving actions to a later stage of the extension. 214 | 215 | Add: 216 | 217 | - Sidebar: 218 | 219 | - Hide 'You' Sidebar Option 220 | - Hide Your Courses Option 221 | - Hide Your Movies and TV Option 222 | - Hide Settings Section 223 | - Hide Footer Section 224 | 225 | - Video: 226 | - Hide Subscribers Counter in Video 227 | - Hide Views in Video 228 | - Hide Share Button 229 | - Hide Chapters in Video Description 230 | - Hide Transcript in Video Description 231 | - Hide Links in Video Description 232 | - Hide Suggested Videos Wall in the end of the video 233 | 234 | Change: 235 | 236 | - Hide Scheduled Videos was moved to General, as it also hides scheduled videos on the homepage 237 | 238 | Fix: 239 | 240 | - Shorts were appearing on the Home Page, even when the toggle was enabled 241 | 242 | Disabled: 243 | 244 | - Auto Expand Description 245 | - Hide Stream Chat Automatically 246 | 247 | Note: Both these features are related to taking actions, and that will be a later focus of the extension. For now, the focus will be around hiding content, as gracefully as possible, without impacting on performance. 248 | 249 | ## 1.4.0 250 | 251 | Add: 252 | 253 | - Hide YouTube Music in the Sidebar Option, for Premium users 254 | - Hide Transfers Sidebar Option, for Premium users 255 | - Hide Video Suggested Shorts Option 256 | 257 | ## 1.3.0 258 | 259 | Add: 260 | 261 | - "Support This Extension" Button to popup 262 | - "Send Feedback" Button to popup 263 | - "Release Notes" hyperlink to popup 264 | - Auto Expand Video Description 265 | 266 | Fix: 267 | 268 | - Hide Home Page Tabs was affecting Channel Video Section Tabs 269 | 270 | ## 1.2.0 271 | 272 | Add: 273 | 274 | - Error Page when opening the extension outside of YouTube 275 | 276 | Fix: 277 | 278 | - "Create Button" Locator for Opera 279 | 280 | ## 1.1.0 281 | 282 | Add: 283 | 284 | - MutationObserver to properly track elements state 285 | 286 | Change: 287 | 288 | - Subscription Page Shorts Locator 289 | 290 | ## 1.0.0 291 | 292 | Add: 293 | 294 | - First Official TubeMod release. Check the [Features Doc](Features.md) for more details. 295 | -------------------------------------------------------------------------------- /public/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

TubeMod

11 |

Make YouTube your own!

12 |
13 | 14 | 15 | 78 | 79 | 80 | 133 | 134 | 135 | 144 | 145 | 146 | 322 | 323 | 324 | 424 | 425 | 426 | 439 | 440 | 441 | 457 | 458 | 459 | 468 | 469 | 470 | 479 | 480 | 481 | 855 | 856 | 857 | 877 | 878 | 879 | 888 | 889 |
890 | 895 | 902 |
903 |
904 | 905 | 906 | 907 | 908 |
909 | 910 |

911 | Current Version 1.13.0 | 912 | Release Notes 917 |

918 | 919 | 920 | 921 | 922 | -------------------------------------------------------------------------------- /scripts/content.js: -------------------------------------------------------------------------------- 1 | const DISPLAY = "display"; 2 | const BACKGROUND = "background"; 3 | const FILTER = "filter"; 4 | const TEXT_TRANSFORM = "text-transform"; 5 | const DISPLAY_NONE = "none"; 6 | const LOWERCASE = "lowercase"; 7 | const YT_RED = "#F03"; 8 | 9 | const PAGE_TYPES = { 10 | HOME: "home", 11 | SUBSCRIPTIONS: "subscriptions", 12 | VIDEO: "video", 13 | SEARCH: "search", 14 | TRENDING: "trending", 15 | DOWNLOADS: "downloads", 16 | CHANNEL: "channel", 17 | }; 18 | 19 | function saveSettings() { 20 | const a = document.createElement("a"); 21 | chrome.storage.local.get( 22 | ["tubemod_elements", "tubemod_version"], 23 | (result) => { 24 | if (chrome.runtime.lastError) { 25 | console.error(chrome.runtime.lastError); 26 | } else { 27 | const file = new Blob([JSON.stringify(result)], { 28 | type: "application/json", 29 | }); 30 | a.href = URL.createObjectURL(file); 31 | a.download = "tubeModSettings.json"; 32 | a.click(); 33 | } 34 | } 35 | ); 36 | document.body.removeChild(a); 37 | } 38 | 39 | function importSettings(settings) { 40 | chrome.storage.local.set({ 41 | tubemod_elements: JSON.parse(settings)["tubemod_elements"], 42 | tubemod_version: JSON.parse(settings)["tubemod_version"], 43 | }); 44 | location.reload(); 45 | alert("TubeMod settings uploaded and applied!"); 46 | } 47 | 48 | function getCurrentPageType() { 49 | const url = window.location.href; 50 | if ( 51 | url === "https://www.youtube.com/" || 52 | url.startsWith("https://www.youtube.com/?") 53 | ) { 54 | return PAGE_TYPES.HOME; 55 | } else if (url.includes("/watch")) { 56 | return PAGE_TYPES.VIDEO; 57 | } else if (url.includes("/feed/subscriptions")) { 58 | return PAGE_TYPES.SUBSCRIPTIONS; 59 | } else if (url.includes("/results?search_query")) { 60 | return PAGE_TYPES.SEARCH; 61 | } else if (url.includes("/feed/trending")) { 62 | return PAGE_TYPES.TRENDING; 63 | } else if (url.includes("/feed/downloads")) { 64 | return PAGE_TYPES.DOWNLOADS; 65 | } else if (url.includes("/@")) { 66 | return PAGE_TYPES.CHANNEL; 67 | } 68 | return null; 69 | } 70 | 71 | const STORAGE = { 72 | tubemod_version: "1.13.0", 73 | tubemod_elements: [ 74 | { 75 | id: "scheduled-videos", 76 | selector: 77 | "//ytd-rich-item-renderer[.//ytd-thumbnail-overlay-time-status-renderer[@overlay-style='UPCOMING']]", 78 | checked: false, 79 | property: DISPLAY, 80 | style: DISPLAY_NONE, 81 | pageTypes: [], 82 | }, 83 | { 84 | id: "live-videos", 85 | selector: 86 | "//ytd-rich-item-renderer[.//div[@id='meta']/ytd-badge-supported-renderer[not(@hidden)]]", 87 | checked: false, 88 | property: DISPLAY, 89 | style: DISPLAY_NONE, 90 | pageTypes: [], 91 | }, 92 | { 93 | id: "video-previews", 94 | selector: "//div[@id='video-preview' or @id='mouseover-overlay']", 95 | checked: false, 96 | property: DISPLAY, 97 | style: DISPLAY_NONE, 98 | pageTypes: [], 99 | }, 100 | { 101 | id: "video-thumbnails", 102 | selector: "//div[@id='thumbnail'] | //yt-collection-thumbnail-view-model", 103 | checked: false, 104 | property: DISPLAY, 105 | style: DISPLAY_NONE, 106 | pageTypes: [], 107 | }, 108 | { 109 | id: "video-thumbnails-blur", 110 | selector: 111 | "//ytd-thumbnail//yt-image | //ytd-playlist-thumbnail | //yt-thumbnail-view-model", 112 | checked: false, 113 | property: FILTER, 114 | style: "blur(10px)", 115 | pageTypes: [], 116 | }, 117 | { 118 | id: "video-meta-data", 119 | selector: "//div[@id='metadata-line']", 120 | checked: false, 121 | property: DISPLAY, 122 | style: DISPLAY_NONE, 123 | pageTypes: [], 124 | }, 125 | { 126 | id: "lowercase-title", 127 | selector: 128 | "//ytd-watch-metadata//div[@id='title'] | //*[@id='video-title']", 129 | checked: false, 130 | property: TEXT_TRANSFORM, 131 | style: LOWERCASE, 132 | pageTypes: [], 133 | }, 134 | { 135 | id: "download-badge", 136 | selector: "//p[text()[contains(., 'Downloaded')]]", 137 | checked: false, 138 | property: DISPLAY, 139 | style: DISPLAY_NONE, 140 | pageTypes: [PAGE_TYPES.DOWNLOADS], 141 | }, 142 | { 143 | id: "logo", 144 | selector: "//ytd-topbar-logo-renderer", 145 | checked: false, 146 | property: DISPLAY, 147 | style: DISPLAY_NONE, 148 | pageTypes: [], 149 | }, 150 | { 151 | id: "search-bar", 152 | selector: "//ytd-searchbox[@id='search'] | //yt-searchbox", 153 | checked: false, 154 | property: DISPLAY, 155 | style: DISPLAY_NONE, 156 | pageTypes: [], 157 | }, 158 | { 159 | id: "microphone-search", 160 | selector: "//*[@id='voice-search-button']", 161 | checked: false, 162 | property: DISPLAY, 163 | style: DISPLAY_NONE, 164 | pageTypes: [], 165 | }, 166 | { 167 | id: "create", 168 | selector: 169 | "//div[@id='buttons' and contains(@class, 'ytd-masthead')]/ytd-button-renderer", 170 | checked: false, 171 | property: DISPLAY, 172 | style: DISPLAY_NONE, 173 | pageTypes: [], 174 | }, 175 | { 176 | id: "notifications", 177 | selector: "//ytd-notification-topbar-button-renderer", 178 | checked: false, 179 | property: DISPLAY, 180 | style: DISPLAY_NONE, 181 | pageTypes: [], 182 | }, 183 | { 184 | id: "studio-button", 185 | selector: "//ytd-notification-topbar-button-renderer", 186 | checked: false, 187 | pageTypes: [], 188 | }, 189 | { 190 | id: "profile-sign-out", 191 | selector: "//ytd-compact-link-renderer/a[@href='/logout']", 192 | checked: false, 193 | property: DISPLAY, 194 | style: DISPLAY_NONE, 195 | pageTypes: [], 196 | }, 197 | { 198 | id: "sidebar", 199 | selector: 200 | "//tp-yt-app-drawer | //ytd-mini-guide-renderer | //yt-icon-button[@id='guide-button']", 201 | checked: false, 202 | property: DISPLAY, 203 | style: DISPLAY_NONE, 204 | pageTypes: [], 205 | }, 206 | { 207 | id: "home", 208 | selector: 209 | "//ytd-guide-entry-renderer[a[@href='/']] | //ytd-mini-guide-entry-renderer[a[@href='/']]", 210 | checked: false, 211 | property: DISPLAY, 212 | style: DISPLAY_NONE, 213 | pageTypes: [], 214 | }, 215 | { 216 | id: "shorts", 217 | selector: 218 | "//ytd-guide-entry-renderer[a[@title='Shorts']] | //ytd-mini-guide-entry-renderer[a[@title='Shorts']] | //ytd-guide-entry-renderer[a[@title='YouTube Shorts']] | //ytd-mini-guide-entry-renderer[a[@title='YouTube Shorts']]", 219 | checked: false, 220 | property: DISPLAY, 221 | style: DISPLAY_NONE, 222 | pageTypes: [], 223 | }, 224 | { 225 | id: "subscriptions", 226 | selector: 227 | "//ytd-guide-entry-renderer[a[@href='/feed/subscriptions']] | //ytd-mini-guide-entry-renderer[a[@href='/feed/subscriptions']]", 228 | checked: false, 229 | property: DISPLAY, 230 | style: DISPLAY_NONE, 231 | pageTypes: [], 232 | }, 233 | { 234 | id: "youtube-music", 235 | selector: 236 | "(//ytd-guide-entry-renderer[a[@href='https://music.youtube.com/']])[1] | //ytd-mini-guide-entry-renderer[a[@href='https://music.youtube.com/']]", 237 | checked: false, 238 | property: DISPLAY, 239 | style: DISPLAY_NONE, 240 | pageTypes: [], 241 | }, 242 | { 243 | id: "you", 244 | selector: 245 | "(//div[@id='header']/ytd-guide-entry-renderer)[1] | //ytd-mini-guide-entry-renderer[a[@href='/feed/you']]", 246 | checked: false, 247 | property: DISPLAY, 248 | style: DISPLAY_NONE, 249 | pageTypes: [], 250 | }, 251 | { 252 | id: "history", 253 | selector: "//ytd-guide-entry-renderer[a[@href='/feed/history']]", 254 | checked: false, 255 | property: DISPLAY, 256 | style: DISPLAY_NONE, 257 | pageTypes: [], 258 | }, 259 | { 260 | id: "playlists", 261 | selector: "//ytd-guide-entry-renderer[a[@href='/feed/playlists']]", 262 | checked: false, 263 | property: DISPLAY, 264 | style: DISPLAY_NONE, 265 | pageTypes: [], 266 | }, 267 | { 268 | id: "my-videos", 269 | selector: 270 | "//ytd-guide-entry-renderer[a[starts-with(@href, 'https://studio.youtube.com/channel')]]", 271 | checked: false, 272 | property: DISPLAY, 273 | style: DISPLAY_NONE, 274 | pageTypes: [], 275 | }, 276 | { 277 | id: "your-movies-and-tv", 278 | selector: 279 | "//ytd-guide-entry-renderer[a[@href='/feed/storefront?bp=ogUCKAQ%3D']]", 280 | checked: false, 281 | property: DISPLAY, 282 | style: DISPLAY_NONE, 283 | pageTypes: [], 284 | }, 285 | { 286 | id: "your-podcasts", 287 | selector: "//ytd-guide-entry-renderer[a[@href='/feed/podcasts']]", 288 | checked: false, 289 | property: DISPLAY, 290 | style: DISPLAY_NONE, 291 | pageTypes: [], 292 | }, 293 | { 294 | id: "watch-later", 295 | selector: "//ytd-guide-entry-renderer[a[@href='/playlist?list=WL']]", 296 | checked: false, 297 | property: DISPLAY, 298 | style: DISPLAY_NONE, 299 | pageTypes: [], 300 | }, 301 | { 302 | id: "courses", 303 | selector: "//ytd-guide-entry-renderer[a[@href='/feed/courses']]", 304 | checked: false, 305 | property: DISPLAY, 306 | style: DISPLAY_NONE, 307 | pageTypes: [], 308 | }, 309 | { 310 | id: "liked-videos", 311 | selector: "//ytd-guide-entry-renderer[a[@href='/playlist?list=LL']]", 312 | checked: false, 313 | property: DISPLAY, 314 | style: DISPLAY_NONE, 315 | pageTypes: [], 316 | }, 317 | { 318 | id: "my-clips", 319 | selector: "//ytd-guide-entry-renderer[a[@href='/feed/clips']]", 320 | checked: false, 321 | property: DISPLAY, 322 | style: DISPLAY_NONE, 323 | pageTypes: [], 324 | }, 325 | { 326 | id: "transfers", 327 | selector: 328 | "//div[@id='section-items']/ytd-guide-downloads-entry-renderer | //ytd-mini-guide-entry-renderer[a[@href='/feed/downloads']]", 329 | checked: false, 330 | property: DISPLAY, 331 | style: DISPLAY_NONE, 332 | pageTypes: [], 333 | }, 334 | { 335 | id: "subscriptions-panel", 336 | selector: "(//ytd-guide-section-renderer)[2]", 337 | checked: false, 338 | property: DISPLAY, 339 | style: DISPLAY_NONE, 340 | pageTypes: [], 341 | }, 342 | { 343 | id: "subscriptions-panel-new-indicator", 344 | selector: 345 | "//ytd-guide-entry-renderer[@line-end-style='dot']//div[@id='newness-dot']", 346 | checked: false, 347 | property: DISPLAY, 348 | style: DISPLAY_NONE, 349 | pageTypes: [], 350 | }, 351 | { 352 | id: "explore-panel", 353 | selector: "(//ytd-guide-section-renderer)[3]", 354 | checked: false, 355 | property: DISPLAY, 356 | style: DISPLAY_NONE, 357 | pageTypes: [], 358 | }, 359 | { 360 | id: "youtube-panel", 361 | selector: "(//ytd-guide-section-renderer)[4]", 362 | checked: false, 363 | property: DISPLAY, 364 | style: DISPLAY_NONE, 365 | pageTypes: [], 366 | }, 367 | { 368 | id: "youtube-settings", 369 | selector: "(//ytd-guide-section-renderer)[5]", 370 | checked: false, 371 | property: DISPLAY, 372 | style: DISPLAY_NONE, 373 | pageTypes: [], 374 | }, 375 | { 376 | id: "youtube-footer", 377 | selector: "//ytd-guide-renderer/div[@id='footer']", 378 | checked: false, 379 | property: DISPLAY, 380 | style: DISPLAY_NONE, 381 | pageTypes: [], 382 | }, 383 | { 384 | id: "tabs", 385 | selector: 386 | "//ytd-feed-filter-chip-bar-renderer[@component-style='FEED_FILTER_CHIP_BAR_STYLE_TYPE_DEFAULT']/..", 387 | checked: false, 388 | property: DISPLAY, 389 | style: DISPLAY_NONE, 390 | pageTypes: [PAGE_TYPES.HOME], 391 | }, 392 | { 393 | id: "ads", 394 | selector: 395 | "//ytd-ad-slot-renderer/ancestor::ytd-rich-item-renderer | //*[@id='player-ads'] | //ytd-banner-promo-renderer/.. | //div[@id='masthead-ad']", 396 | checked: false, 397 | property: DISPLAY, 398 | style: DISPLAY_NONE, 399 | pageTypes: [PAGE_TYPES.HOME], 400 | }, 401 | { 402 | id: "all-videos", 403 | selector: "//*[@page-subtype='home']/div[@id='primary']", 404 | checked: false, 405 | property: DISPLAY, 406 | style: DISPLAY_NONE, 407 | pageTypes: [PAGE_TYPES.HOME], 408 | }, 409 | { 410 | id: "members-only", 411 | selector: 412 | "//ytd-rich-item-renderer[.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='M8 1C4.13 1 1 4.13 1 8s3.13 7 7 7 7-3.13 7-7-3.13-7-7-7Zm2.47 10.5L7.99 9.94 5.5 11.5l.67-2.97L4 6.5l2.87-.22L8.01 3.5l1.12 2.78L12 6.5 9.82 8.53l.65 2.97Z']]", 413 | checked: false, 414 | property: DISPLAY, 415 | style: DISPLAY_NONE, 416 | pageTypes: [PAGE_TYPES.HOME], 417 | }, 418 | { 419 | id: "home-posts", 420 | selector: "//ytd-rich-section-renderer[.//ytd-post-renderer]", 421 | checked: false, 422 | property: DISPLAY, 423 | style: DISPLAY_NONE, 424 | pageTypes: [PAGE_TYPES.HOME], 425 | }, 426 | { 427 | id: "home-shorts", 428 | selector: "//ytd-rich-section-renderer[.//ytm-shorts-lockup-view-model]", 429 | checked: false, 430 | property: DISPLAY, 431 | style: DISPLAY_NONE, 432 | pageTypes: [PAGE_TYPES.HOME], 433 | }, 434 | { 435 | id: "home-news", 436 | selector: 437 | "//ytd-rich-section-renderer[not(.//ytm-shorts-lockup-view-model) and not(.//div[contains(@class, 'button-container') and not(@hidden)][.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='m18 9.28-6.35 6.35-6.37-6.35.72-.71 5.64 5.65 5.65-5.65z']])]", 438 | checked: false, 439 | property: DISPLAY, 440 | style: DISPLAY_NONE, 441 | pageTypes: [PAGE_TYPES.HOME], 442 | }, 443 | { 444 | id: "home-trending", 445 | selector: 446 | "//ytd-rich-section-renderer[.//div[contains(@class, 'button-container') and not(@hidden)][.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='m18 9.28-6.35 6.35-6.37-6.35.72-.71 5.64 5.65 5.65-5.65z']]]", 447 | checked: false, 448 | property: DISPLAY, 449 | style: DISPLAY_NONE, 450 | pageTypes: [PAGE_TYPES.HOME], 451 | }, 452 | { 453 | id: "home-playables", 454 | selector: 455 | "//ytd-rich-section-renderer[.//a[contains(@href, '/playables')]]", 456 | checked: false, 457 | property: DISPLAY, 458 | style: DISPLAY_NONE, 459 | pageTypes: [PAGE_TYPES.HOME], 460 | }, 461 | { 462 | id: "home-primetime-movies", 463 | selector: "//ytd-rich-section-renderer[.//a[@href='/feed/storefront']]", 464 | checked: false, 465 | property: DISPLAY, 466 | style: DISPLAY_NONE, 467 | pageTypes: [PAGE_TYPES.HOME], 468 | }, 469 | { 470 | id: "playlist-mix", 471 | selector: 472 | "//ytd-rich-item-renderer[.//ytd-playlist-thumbnail[not(@hidden)]] | //ytd-rich-item-renderer[.//a[contains(@href, 'start_radio=1')]]", 473 | checked: false, 474 | property: DISPLAY, 475 | style: DISPLAY_NONE, 476 | pageTypes: [PAGE_TYPES.HOME], 477 | }, 478 | { 479 | id: "podcast-playlist", 480 | selector: "//ytd-rich-item-renderer[.//a[contains(@href, 'pp=')]]", 481 | checked: false, 482 | property: DISPLAY, 483 | style: DISPLAY_NONE, 484 | pageTypes: [PAGE_TYPES.HOME], 485 | }, 486 | { 487 | id: "viewed-videos", 488 | selector: 489 | "//ytd-rich-item-renderer[.//div[@id='progress'][contains(@style, 'width: 100%')]]", 490 | checked: false, 491 | property: DISPLAY, 492 | style: DISPLAY_NONE, 493 | pageTypes: [PAGE_TYPES.HOME], 494 | }, 495 | { 496 | id: "partially-viewed-videos", 497 | selector: 498 | "//ytd-rich-item-renderer[.//ytd-thumbnail[not(@is-live-video)]//div[@id='progress']]", 499 | checked: false, 500 | property: DISPLAY, 501 | style: DISPLAY_NONE, 502 | pageTypes: [PAGE_TYPES.HOME], 503 | }, 504 | { 505 | id: "subscriptions-shorts", 506 | selector: "//ytd-rich-shelf-renderer/../.. | //ytd-rich-shelf-renderer", 507 | checked: false, 508 | property: DISPLAY, 509 | style: DISPLAY_NONE, 510 | pageTypes: [PAGE_TYPES.SUBSCRIPTIONS], 511 | }, 512 | { 513 | id: "search-shorts", 514 | selector: "//ytd-reel-shelf-renderer", 515 | checked: false, 516 | property: DISPLAY, 517 | style: DISPLAY_NONE, 518 | pageTypes: [PAGE_TYPES.SEARCH], 519 | }, 520 | { 521 | id: "search-people-also-searched", 522 | selector: "//div[@id='contents']/ytd-horizontal-card-list-renderer", 523 | checked: false, 524 | property: DISPLAY, 525 | style: DISPLAY_NONE, 526 | pageTypes: [PAGE_TYPES.SEARCH], 527 | }, 528 | { 529 | id: "trending-shorts", 530 | selector: "//ytd-video-renderer[.//a[contains(@href, '/shorts')]]", 531 | checked: false, 532 | property: DISPLAY, 533 | style: DISPLAY_NONE, 534 | pageTypes: [PAGE_TYPES.TRENDING], 535 | }, 536 | { 537 | id: "channel-trailer", 538 | selector: "//*[@id='c4-player']", 539 | checked: false, 540 | pageTypes: [PAGE_TYPES.CHANNEL], 541 | }, 542 | { 543 | id: "video-progress-bar-dot", 544 | selector: "//div[@class='ytp-scrubber-container']", 545 | checked: false, 546 | property: DISPLAY, 547 | style: DISPLAY_NONE, 548 | pageTypes: [PAGE_TYPES.VIDEO], 549 | }, 550 | { 551 | id: "video-settings-playback-speed", 552 | selector: 553 | "//div[@role='menuitem'][.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='M10,8v8l6-4L10,8L10,8z M6.3,5L5.7,4.2C7.2,3,9,2.2,11,2l0.1,1C9.3,3.2,7.7,3.9,6.3,5z M5,6.3L4.2,5.7C3,7.2,2.2,9,2,11 l1,.1C3.2,9.3,3.9,7.7,5,6.3z M5,17.7c-1.1-1.4-1.8-3.1-2-4.8L2,13c0.2,2,1,3.8,2.2,5.4L5,17.7z M11.1,21c-1.8-0.2-3.4-0.9-4.8-2 l-0.6,.8C7.2,21,9,21.8,11,22L11.1,21z M22,12c0-5.2-3.9-9.4-9-10l-0.1,1c4.6,.5,8.1,4.3,8.1,9s-3.5,8.5-8.1,9l0.1,1 C18.2,21.5,22,17.2,22,12z']]", 554 | checked: false, 555 | property: DISPLAY, 556 | style: DISPLAY_NONE, 557 | pageTypes: [PAGE_TYPES.VIDEO], 558 | }, 559 | { 560 | id: "video-settings-sleep-timer", 561 | selector: 562 | "//div[@role='menuitem'][.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='M16.67,4.31C19.3,5.92,21,8.83,21,12c0,4.96-4.04,9-9,9c-2.61,0-5.04-1.12-6.72-3.02C5.52,17.99,5.76,18,6,18 c6.07,0,11-4.93,11-11C17,6.08,16.89,5.18,16.67,4.31 M14.89,2.43C15.59,3.8,16,5.35,16,7c0,5.52-4.48,10-10,10 c-1,0-1.97-0.15-2.89-0.43C4.77,19.79,8.13,22,12,22c5.52,0,10-4.48,10-10C22,7.48,19,3.67,14.89,2.43L14.89,2.43z M12,6H6v1h4.5 L6,10.99v0.05V12h6v-1H7.5L12,7.01V6.98V6L12,6z']]", 563 | checked: false, 564 | property: DISPLAY, 565 | style: DISPLAY_NONE, 566 | pageTypes: [PAGE_TYPES.VIDEO], 567 | }, 568 | { 569 | id: "video-title", 570 | selector: "//div[@id='above-the-fold']/div[@id='title']", 571 | checked: false, 572 | property: DISPLAY, 573 | style: DISPLAY_NONE, 574 | pageTypes: [PAGE_TYPES.VIDEO], 575 | }, 576 | { 577 | id: "video-subscribers", 578 | selector: "(//div[@id='upload-info'])[1]/yt-formatted-string", 579 | checked: false, 580 | property: DISPLAY, 581 | style: DISPLAY_NONE, 582 | pageTypes: [PAGE_TYPES.VIDEO], 583 | }, 584 | { 585 | id: "video-subscribe-button", 586 | selector: "//yt-button-shape[@id='subscribe-button-shape']", 587 | checked: false, 588 | property: DISPLAY, 589 | style: DISPLAY_NONE, 590 | pageTypes: [PAGE_TYPES.VIDEO], 591 | }, 592 | { 593 | id: "video-subscribed-button", 594 | selector: "//div[@id='notification-preference-button']", 595 | checked: false, 596 | property: DISPLAY, 597 | style: DISPLAY_NONE, 598 | pageTypes: [PAGE_TYPES.VIDEO], 599 | }, 600 | { 601 | id: "video-join-button", 602 | selector: "//div[@id='sponsor-button']", 603 | checked: false, 604 | property: DISPLAY, 605 | style: DISPLAY_NONE, 606 | pageTypes: [PAGE_TYPES.VIDEO], 607 | }, 608 | { 609 | id: "video-likes-dislikes", 610 | selector: "//segmented-like-dislike-button-view-model", 611 | checked: false, 612 | property: DISPLAY, 613 | style: DISPLAY_NONE, 614 | pageTypes: [PAGE_TYPES.VIDEO], 615 | }, 616 | { 617 | id: "video-share", 618 | selector: "//div[@id='top-level-buttons-computed']/yt-button-view-model", 619 | checked: false, 620 | property: DISPLAY, 621 | style: DISPLAY_NONE, 622 | pageTypes: [PAGE_TYPES.VIDEO], 623 | }, 624 | { 625 | id: "video-download", 626 | selector: "//ytd-download-button-renderer", 627 | checked: false, 628 | property: DISPLAY, 629 | style: DISPLAY_NONE, 630 | pageTypes: [PAGE_TYPES.VIDEO], 631 | }, 632 | { 633 | id: "video-thanks", 634 | selector: 635 | "//div[@id='flexible-item-buttons']/yt-button-view-model[.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='M11 17h2v-1h1c.55 0 1-.45 1-1v-3c0-.55-.45-1-1-1h-3v-1h4V8h-2V7h-2v1h-1c-.55 0-1 .45-1 1v3c0 .55.45 1 1 1h3v1H9v2h2v1zm5.5-15c-1.74 0-3.41.88-4.5 2.28C10.91 2.88 9.24 2 7.5 2 4.42 2 2 4.64 2 7.99c0 4.12 3.4 7.48 8.55 12.58L12 22l1.45-1.44C18.6 15.47 22 12.11 22 7.99 22 4.64 19.58 2 16.5 2zm-3.75 17.85-.75.74-.74-.73-.04-.04C6.27 14.92 3 11.69 3 7.99 3 5.19 4.98 3 7.5 3c1.4 0 2.79.71 3.71 1.89L12 5.9l.79-1.01C13.71 3.71 15.1 3 16.5 3 19.02 3 21 5.19 21 7.99c0 3.7-3.28 6.94-8.25 11.86z']]", 636 | checked: false, 637 | property: DISPLAY, 638 | style: DISPLAY_NONE, 639 | pageTypes: [PAGE_TYPES.VIDEO], 640 | }, 641 | { 642 | id: "video-save", 643 | selector: 644 | "//div[@id='flexible-item-buttons']/yt-button-view-model[.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='M18 4v15.06l-5.42-3.87-.58-.42-.58.42L6 19.06V4h12m1-1H5v18l7-5 7 5V3z']]", 645 | checked: false, 646 | property: DISPLAY, 647 | style: DISPLAY_NONE, 648 | pageTypes: [PAGE_TYPES.VIDEO], 649 | }, 650 | { 651 | id: "video-clip", 652 | selector: 653 | "//div[@id='flexible-item-buttons']/yt-button-view-model[.//*[local-name() = 'svg']//*[local-name() = 'path' and @d='M8 7c0 .55-.45 1-1 1s-1-.45-1-1 .45-1 1-1 1 .45 1 1zm-1 9c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3.79-7.77L21 18.44V20h-3.27l-5.76-5.76-1.27 1.27c.19.46.3.96.3 1.49 0 2.21-1.79 4-4 4s-4-1.79-4-4 1.79-4 4-4c.42 0 .81.08 1.19.2l1.37-1.37-1.11-1.11C8 10.89 7.51 11 7 11c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4c0 .43-.09.84-.21 1.23zm-.71.71-.43-.44.19-.58c.11-.34.16-.64.16-.92 0-1.65-1.35-3-3-3S4 5.35 4 7s1.35 3 3 3c.36 0 .73-.07 1.09-.21l.61-.24.46.46 1.11 1.11.71.71-.71.71-1.37 1.37-.43.43-.58-.18C7.55 14.05 7.27 14 7 14c-1.65 0-3 1.35-3 3s1.35 3 3 3 3-1.35 3-3c0-.38-.07-.75-.22-1.12l-.25-.61.47-.47 1.27-1.27.71-.71.71.71L18.15 19H20v-.15l-9.92-9.91zM17.73 4H21v1.56l-5.52 5.52-2.41-2.41L17.73 4zm.42 1-3.67 3.67 1 1L20 5.15V5h-1.85z']]", 654 | checked: false, 655 | property: DISPLAY, 656 | style: DISPLAY_NONE, 657 | pageTypes: [PAGE_TYPES.VIDEO], 658 | }, 659 | { 660 | id: "video-collapsed-buttons", 661 | selector: 662 | "//div[@id='top-row']//ytd-menu-renderer//yt-button-shape[@id='button-shape']", 663 | checked: false, 664 | property: DISPLAY, 665 | style: DISPLAY_NONE, 666 | pageTypes: [PAGE_TYPES.VIDEO], 667 | }, 668 | { 669 | id: "stream-show-chat", 670 | selector: "//div[@id='show-hide-button']", 671 | checked: false, 672 | property: DISPLAY, 673 | style: DISPLAY_NONE, 674 | pageTypes: [PAGE_TYPES.VIDEO], 675 | }, 676 | { 677 | id: "video-description", 678 | selector: "//div[@id='description-inner']/parent::div", 679 | checked: false, 680 | property: DISPLAY, 681 | style: DISPLAY_NONE, 682 | pageTypes: [PAGE_TYPES.VIDEO], 683 | }, 684 | { 685 | id: "video-views", 686 | selector: 687 | "//div[@id='view-count'] | (//yt-formatted-string[@id='info']/span)[position()<3]", 688 | checked: false, 689 | property: DISPLAY, 690 | style: DISPLAY_NONE, 691 | pageTypes: [PAGE_TYPES.VIDEO], 692 | }, 693 | { 694 | id: "video-people-mentioned-description", 695 | selector: "//yt-video-attributes-section-view-model", 696 | checked: false, 697 | property: DISPLAY, 698 | style: DISPLAY_NONE, 699 | pageTypes: [PAGE_TYPES.VIDEO], 700 | }, 701 | { 702 | id: "video-chapters-description", 703 | selector: 704 | "//ytd-horizontal-card-list-renderer[contains (@class, 'ytd-structured-description-content-renderer')]", 705 | checked: false, 706 | property: DISPLAY, 707 | style: DISPLAY_NONE, 708 | pageTypes: [PAGE_TYPES.VIDEO], 709 | }, 710 | { 711 | id: "video-shorts-description", 712 | selector: 713 | "//ytd-structured-description-content-renderer//ytd-reel-shelf-renderer", 714 | checked: false, 715 | property: DISPLAY, 716 | style: DISPLAY_NONE, 717 | pageTypes: [PAGE_TYPES.VIDEO], 718 | }, 719 | { 720 | id: "video-transcription-description", 721 | selector: "//ytd-video-description-transcript-section-renderer", 722 | checked: false, 723 | property: DISPLAY, 724 | style: DISPLAY_NONE, 725 | pageTypes: [PAGE_TYPES.VIDEO], 726 | }, 727 | { 728 | id: "video-channel-links-description", 729 | selector: "//ytd-video-description-infocards-section-renderer", 730 | checked: false, 731 | property: DISPLAY, 732 | style: DISPLAY_NONE, 733 | pageTypes: [PAGE_TYPES.VIDEO], 734 | }, 735 | { 736 | id: "video-live-chat-replay", 737 | selector: "//div[@id='teaser-carousel']", 738 | checked: false, 739 | property: DISPLAY, 740 | style: DISPLAY_NONE, 741 | pageTypes: [PAGE_TYPES.VIDEO], 742 | }, 743 | { 744 | id: "video-summary-ai-generated", 745 | selector: "//div[@id='expandable-metadata']", 746 | checked: false, 747 | property: DISPLAY, 748 | style: DISPLAY_NONE, 749 | pageTypes: [PAGE_TYPES.VIDEO], 750 | }, 751 | { 752 | id: "video-comments", 753 | selector: "//ytd-comments[@id='comments']", 754 | checked: false, 755 | property: DISPLAY, 756 | style: DISPLAY_NONE, 757 | pageTypes: [PAGE_TYPES.VIDEO], 758 | }, 759 | { 760 | id: "video-comments-avatars", 761 | selector: "//ytd-comments[@id='comments']//div[@id='author-thumbnail']/a", 762 | checked: false, 763 | property: DISPLAY, 764 | style: DISPLAY_NONE, 765 | pageTypes: [PAGE_TYPES.VIDEO], 766 | }, 767 | { 768 | id: "video-comments-replies", 769 | selector: "//ytd-comments[@id='comments']//div[@id='replies']", 770 | checked: false, 771 | property: DISPLAY, 772 | style: DISPLAY_NONE, 773 | pageTypes: [PAGE_TYPES.VIDEO], 774 | }, 775 | { 776 | id: "video-categories-games", 777 | selector: "//ytd-rich-metadata-row-renderer/../..", 778 | checked: false, 779 | property: DISPLAY, 780 | style: DISPLAY_NONE, 781 | pageTypes: [PAGE_TYPES.VIDEO], 782 | }, 783 | { 784 | id: "video-ads", 785 | selector: "//div[@id='player-ads']", 786 | checked: false, 787 | property: DISPLAY, 788 | style: DISPLAY_NONE, 789 | pageTypes: [PAGE_TYPES.VIDEO], 790 | }, 791 | { 792 | id: "video-offer-module", 793 | selector: "//div[@id='offer-module']", 794 | checked: false, 795 | property: DISPLAY, 796 | style: DISPLAY_NONE, 797 | pageTypes: [PAGE_TYPES.VIDEO], 798 | }, 799 | { 800 | id: "video-tabs", 801 | selector: "//yt-related-chip-cloud-renderer", 802 | checked: false, 803 | property: DISPLAY, 804 | style: DISPLAY_NONE, 805 | pageTypes: [PAGE_TYPES.VIDEO], 806 | }, 807 | { 808 | id: "video-suggested-videos", 809 | selector: 810 | "//div[@id='contents']/parent::ytd-item-section-renderer[contains(@class, 'watch-next')]", 811 | checked: false, 812 | property: DISPLAY, 813 | style: DISPLAY_NONE, 814 | pageTypes: [PAGE_TYPES.VIDEO], 815 | }, 816 | { 817 | id: "video-suggested-videos-new-badge", 818 | selector: 819 | "//ytd-compact-video-renderer//div/ytd-badge-supported-renderer[.//div[contains(@class, 'badge')]]", 820 | checked: false, 821 | property: DISPLAY, 822 | style: DISPLAY_NONE, 823 | pageTypes: [PAGE_TYPES.VIDEO], 824 | }, 825 | { 826 | id: "video-suggested-shorts", 827 | selector: 828 | "//div[@id='contents']/parent::ytd-item-section-renderer[contains(@class, 'watch-next')]//ytd-reel-shelf-renderer", 829 | checked: false, 830 | property: DISPLAY, 831 | style: DISPLAY_NONE, 832 | pageTypes: [PAGE_TYPES.VIDEO], 833 | }, 834 | { 835 | id: "video-suggestion-wall", 836 | selector: "//div[@class='ytp-endscreen-content']", 837 | checked: false, 838 | property: DISPLAY, 839 | style: DISPLAY_NONE, 840 | pageTypes: [PAGE_TYPES.VIDEO], 841 | }, 842 | { 843 | id: "video-thumbnail", 844 | selector: "//ytd-watch-next-secondary-results-renderer/div[@id='items']", 845 | checked: false, 846 | pageTypes: [PAGE_TYPES.VIDEO], 847 | }, 848 | { 849 | id: "youtube-progress-bar", 850 | selector: "//div[contains(@class,'ytp-play-progress')]", 851 | checked: false, 852 | property: BACKGROUND, 853 | style: YT_RED, 854 | pageTypes: [PAGE_TYPES.VIDEO], 855 | }, 856 | ], 857 | }; 858 | 859 | const eventBus = { 860 | listeners: {}, 861 | subscribe(event, callback) { 862 | if (!this.listeners[event]) { 863 | this.listeners[event] = []; 864 | } 865 | this.listeners[event].push(callback); 866 | }, 867 | publish(event, data) { 868 | if (this.listeners[event]) { 869 | this.listeners[event].forEach((callback) => callback(data)); 870 | } 871 | }, 872 | }; 873 | 874 | function waitForElements(selector, callback) { 875 | const observer = new MutationObserver((mutations, obs) => { 876 | const element = document.evaluate( 877 | selector, 878 | document, 879 | null, 880 | XPathResult.FIRST_ORDERED_NODE_TYPE, 881 | null 882 | ).singleNodeValue; 883 | if (element) { 884 | obs.disconnect(); 885 | callback([element]); 886 | } 887 | }); 888 | 889 | observer.observe(document.body, { 890 | childList: true, 891 | subtree: true, 892 | }); 893 | 894 | const existingElement = document.evaluate( 895 | selector, 896 | document, 897 | null, 898 | XPathResult.FIRST_ORDERED_NODE_TYPE, 899 | null 900 | ).singleNodeValue; 901 | if (existingElement) { 902 | callback([existingElement]); 903 | return; 904 | } 905 | } 906 | 907 | function debounce(func, wait) { 908 | let timeout; 909 | return (...args) => { 910 | clearTimeout(timeout); 911 | timeout = setTimeout(() => func.apply(this, args), wait); 912 | }; 913 | } 914 | 915 | class YouTubeElement { 916 | constructor(config) { 917 | Object.assign(this, config); 918 | } 919 | 920 | async toggle(hide) { 921 | this.checked = hide; 922 | return this.handleVisibility(hide); 923 | } 924 | 925 | handleVisibility(hide) { 926 | const currentPageType = getCurrentPageType(); 927 | if ( 928 | this.pageTypes.length > 0 && 929 | !this.pageTypes.includes(currentPageType) 930 | ) { 931 | return; 932 | } 933 | this.applyVisibility(hide); 934 | } 935 | 936 | applyVisibility(hide) { 937 | const elements = document.evaluate( 938 | this.selector, 939 | document, 940 | null, 941 | XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, 942 | null 943 | ); 944 | 945 | for (let i = 0; i < elements.snapshotLength; i++) { 946 | hide 947 | ? (elements.snapshotItem(i).style[this.property] = this.style) 948 | : (elements.snapshotItem(i).style[this.property] = ""); 949 | } 950 | 951 | if (this.id === "channel-trailer" && this.checked) { 952 | document.querySelector("video").pause(); 953 | } 954 | 955 | if (this.id === "home-posts") { 956 | const postsElement = document.querySelector( 957 | "ytd-rich-section-renderer:has(ytd-post-renderer)" 958 | ); 959 | if (postsElement) { 960 | hide ? (postsElement.disabled = true) : (postsElement.disabled = false); 961 | } 962 | } 963 | 964 | if (this.id === "video-views") { 965 | const viewsElement = document.getElementById("view-count"); 966 | if (viewsElement) { 967 | hide ? (viewsElement.disabled = true) : (viewsElement.disabled = false); 968 | } 969 | } 970 | 971 | if (this.id === "video-shorts-description") { 972 | const descriptionShorts = document.querySelector( 973 | "ytd-structured-description-content-renderer > div#items > ytd-reel-shelf-renderer" 974 | ); 975 | if (descriptionShorts) { 976 | hide 977 | ? (descriptionShorts.disabled = true) 978 | : (descriptionShorts.disabled = false); 979 | } 980 | } 981 | 982 | if (this.id === "tabs") { 983 | const frostedBar = document.getElementById("frosted-glass"); 984 | if (frostedBar) { 985 | frostedBar.style.height = hide 986 | ? frostedBar.style.setProperty("height", "56px") 987 | : frostedBar.style.setProperty("height", "112px"); 988 | } 989 | } 990 | 991 | if (this.id === "you") { 992 | const elementWithTopBorder = document.querySelector( 993 | "ytd-guide-collapsible-section-entry-renderer" 994 | ); 995 | if (elementWithTopBorder) { 996 | elementWithTopBorder.style.borderTop = hide 997 | ? "none" 998 | : "1px solid var(--yt-spec-10-percent-layer)"; 999 | } 1000 | } 1001 | 1002 | if (this.id === "my-clips") { 1003 | const elementWithBottomBorder = document.querySelector( 1004 | "ytd-guide-section-renderer" 1005 | ); 1006 | if (elementWithBottomBorder) { 1007 | elementWithBottomBorder.style.borderBottom = hide 1008 | ? "none" 1009 | : "1px solid var(--yt-spec-10-percent-layer)"; 1010 | } 1011 | } 1012 | 1013 | if (this.id === "video-thumbnail") { 1014 | const thumbnailElement = document.getElementById( 1015 | "video-thumbnail-tubemod" 1016 | ); 1017 | 1018 | if (hide && thumbnailElement === null) { 1019 | const items = document.querySelector( 1020 | "ytd-watch-next-secondary-results-renderer div#items" 1021 | ); 1022 | 1023 | let currentVideo = new URL(document.URL); 1024 | let videoId = currentVideo.searchParams.get("v"); 1025 | let thumbnailSource = `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`; 1026 | 1027 | if (items) { 1028 | let ytImage = document.createElement("ytd-thumbnail"); 1029 | 1030 | let anchorTag = document.createElement("a"); 1031 | anchorTag.setAttribute("target", "_blank"); 1032 | anchorTag.setAttribute( 1033 | "href", 1034 | "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" 1035 | ); 1036 | 1037 | let image = document.createElement("img"); 1038 | image.setAttribute("id", "video-thumbnail-tubemod"); 1039 | image.setAttribute("src", thumbnailSource); 1040 | image.setAttribute( 1041 | "class", 1042 | "yt-core-image--fill-parent-width yt-core-image--loaded" 1043 | ); 1044 | image.setAttribute( 1045 | "style", 1046 | "border-radius: 8px; margin-bottom: 8px;" 1047 | ); 1048 | 1049 | anchorTag.append(image); 1050 | ytImage.append(anchorTag); 1051 | items.prepend(ytImage); 1052 | } 1053 | } else if (!hide && thumbnailElement) { 1054 | thumbnailElement.remove(); 1055 | } 1056 | } 1057 | 1058 | if (this.id === "sidebar") { 1059 | const videoContainer = document.querySelector( 1060 | "ytd-app[guide-persistent-and-visible] ytd-page-manager.ytd-app" 1061 | ); 1062 | 1063 | if (videoContainer) { 1064 | videoContainer.style.marginLeft = hide ? 0 : null; 1065 | } 1066 | } 1067 | 1068 | if (this.id === "studio-button") { 1069 | const youtubeStudioButton = document.getElementById( 1070 | "studio-button-tubemod" 1071 | ); 1072 | 1073 | if (hide && youtubeStudioButton === null) { 1074 | const youtubeStudioButtonAnchor = document.createElement("a"); 1075 | youtubeStudioButtonAnchor.setAttribute( 1076 | "href", 1077 | "https://studio.youtube.com/" 1078 | ); 1079 | youtubeStudioButtonAnchor.setAttribute("id", "studio-button-tubemod"); 1080 | youtubeStudioButtonAnchor.setAttribute("style", "margin-right: 8px;"); 1081 | 1082 | const svgNS = "http://www.w3.org/2000/svg"; 1083 | const svg = document.createElementNS(svgNS, "svg"); 1084 | svg.setAttribute( 1085 | "fill", 1086 | window.matchMedia && 1087 | window.matchMedia("(prefers-color-scheme: dark)").matches 1088 | ? "#fff" 1089 | : "#000" 1090 | ); 1091 | svg.setAttribute("width", "24"); 1092 | svg.setAttribute("height", "24"); 1093 | svg.setAttribute("viewBox", "0 0 24 24"); 1094 | svg.setAttribute( 1095 | "style", 1096 | "pointer-events: none; display: inherit; width: 100%; height: 100%;" 1097 | ); 1098 | 1099 | const path = document.createElementNS(svgNS, "path"); 1100 | path.setAttribute( 1101 | "d", 1102 | "M10 9.35 15 12l-5 2.65ZM12 3a.73.73 0 00-.31.06L4.3 7.28a.79.79 0 00-.3.52v8.4a.79.79 0 00.3.52l7.39 4.22a.83.83 0 00.62 0l7.39-4.22a.79.79 0 00.3-.52V7.8a.79.79 0 00-.3-.52l-7.39-4.22A.73.73 0 0012 3m0-1a1.6 1.6 0 01.8.19l7.4 4.22A1.77 1.77 0 0121 7.8v8.4a1.77 1.77 0 01-.8 1.39l-7.4 4.22a1.78 1.78 0 01-1.6 0l-7.4-4.22A1.77 1.77 0 013 16.2V7.8a1.77 1.77 0 01.8-1.39l7.4-4.22A1.6 1.6 0 0112 2Zm0 4a.42.42 0 00-.17 0l-4.7 2.8a.59.59 0 00-.13.39v5.61a.65.65 0 00.13.37l4.7 2.8A.42.42 0 0012 18a.34.34 0 00.17 0l4.7-2.81a.56.56 0 00.13-.39V9.19a.62.62 0 00-.13-.37L12.17 6A.34.34 0 0012 6m0-1a1.44 1.44 0 01.69.17L17.39 8A1.46 1.46 0 0118 9.19v5.61a1.46 1.46 0 01-.61 1.2l-4.7 2.81A1.44 1.44 0 0112 19a1.4 1.4 0 01-.68-.17L6.62 16A1.47 1.47 0 016 14.8V9.19A1.47 1.47 0 016.62 8l4.7-2.8A1.4 1.4 0 0112 5Z" 1103 | ); 1104 | 1105 | svg.appendChild(path); 1106 | 1107 | const div = document.createElement("div"); 1108 | div.setAttribute( 1109 | "style", 1110 | "width: 24px; height: 24px; display: block; fill: currentcolor;" 1111 | ); 1112 | 1113 | div.appendChild(svg); 1114 | 1115 | const span = document.createElement("span"); 1116 | span.setAttribute( 1117 | "class", 1118 | "yt-icon-shape style-scope yt-icon yt-spec-icon-shape" 1119 | ); 1120 | span.appendChild(div); 1121 | 1122 | const button = document.createElement("button"); 1123 | button.setAttribute( 1124 | "style", 1125 | "display: inline-block; vertical-align: middle; justify-items: center; color: inherit; outline: none; background: none; margin: 0; border: none; padding: 0; width: 100%; height: 100%; line-height: 0; cursor: pointer;" 1126 | ); 1127 | button.append(span); 1128 | 1129 | const ytIconButton = document.createElement("div"); 1130 | ytIconButton.setAttribute("style", "height: 40px; width: 40px;"); 1131 | ytIconButton.append(button); 1132 | 1133 | youtubeStudioButtonAnchor.appendChild(ytIconButton); 1134 | 1135 | const headerButtons = document.querySelector( 1136 | "ytd-masthead div#buttons" 1137 | ); 1138 | const headerButtonsChildren = Array.from(headerButtons.children); 1139 | const position = 1; 1140 | 1141 | headerButtons.insertBefore( 1142 | youtubeStudioButtonAnchor, 1143 | headerButtonsChildren[position] 1144 | ); 1145 | } else if (!hide && youtubeStudioButton) { 1146 | youtubeStudioButton.remove(); 1147 | } 1148 | } 1149 | } 1150 | } 1151 | 1152 | class ElementManager { 1153 | constructor() { 1154 | this.elements = []; 1155 | this.observer = null; 1156 | this.initialize(); 1157 | } 1158 | 1159 | async initialize() { 1160 | const storedElements = await this.getStoredElements(); 1161 | this.elements = storedElements.map((el) => new YouTubeElement(el)); 1162 | } 1163 | 1164 | async getStoredElements() { 1165 | return new Promise((resolve) => { 1166 | chrome.storage.local.get( 1167 | ["tubemod_elements", "tubemod_version"], 1168 | (result) => { 1169 | if ( 1170 | result.tubemod_elements && 1171 | result.tubemod_version === STORAGE.tubemod_version 1172 | ) { 1173 | resolve(JSON.parse(result.tubemod_elements)); 1174 | } else if ( 1175 | result.tubemod_elements && 1176 | result.tubemod_version !== STORAGE.tubemod_version 1177 | ) { 1178 | const storedElements = JSON.parse(result.tubemod_elements); 1179 | const mergedElements = STORAGE.tubemod_elements.map( 1180 | (newElement) => { 1181 | const storedElement = storedElements.find( 1182 | (el) => el.id === newElement.id 1183 | ); 1184 | if (storedElement) { 1185 | return { ...newElement, checked: storedElement.checked }; 1186 | } 1187 | return newElement; 1188 | } 1189 | ); 1190 | chrome.storage.local.set({ 1191 | tubemod_elements: JSON.stringify(mergedElements), 1192 | tubemod_version: STORAGE.tubemod_version, 1193 | }); 1194 | resolve(mergedElements); 1195 | } else { 1196 | chrome.storage.local.set({ 1197 | tubemod_elements: JSON.stringify(STORAGE.tubemod_elements), 1198 | tubemod_version: STORAGE.tubemod_version, 1199 | }); 1200 | resolve(STORAGE.tubemod_elements); 1201 | } 1202 | } 1203 | ); 1204 | }); 1205 | } 1206 | 1207 | async saveElements() { 1208 | const serializedElements = JSON.stringify(this.elements); 1209 | await chrome.storage.local.set({ 1210 | tubemod_elements: serializedElements, 1211 | tubemod_version: STORAGE.tubemod_version, 1212 | }); 1213 | } 1214 | 1215 | async handleAction(action) { 1216 | const element = this.elements.find((el) => el.id === action.target); 1217 | if (element) { 1218 | await element.toggle(action.hide); 1219 | await this.saveElements(); 1220 | } 1221 | } 1222 | 1223 | setupObserver() { 1224 | this.observer?.disconnect(); 1225 | this.observer = new MutationObserver( 1226 | debounce(this.handleMutations.bind(this), 100) 1227 | ); 1228 | this.observer.observe(document.body, { childList: true, subtree: true }); 1229 | } 1230 | 1231 | handleMutations() { 1232 | this.applyAllElements(getCurrentPageType()); 1233 | } 1234 | 1235 | async applyAllElements(pageType) { 1236 | const relevantElements = this.elements.filter( 1237 | (el) => el.pageTypes.length === 0 || el.pageTypes.includes(pageType) 1238 | ); 1239 | 1240 | await Promise.all( 1241 | relevantElements.map((element) => { 1242 | element.checked !== undefined ? element.toggle(element.checked) : null; 1243 | }) 1244 | ); 1245 | } 1246 | } 1247 | 1248 | class TubeMod { 1249 | constructor() { 1250 | this.elementManager = new ElementManager(); 1251 | this.setupEventListeners(); 1252 | } 1253 | 1254 | setupEventListeners() { 1255 | chrome.runtime.onMessage.addListener(this.handleMessage.bind(this)); 1256 | window.addEventListener( 1257 | "DOMContentLoaded", 1258 | this.handleYouTubeNavigate.bind(this) 1259 | ); 1260 | window.addEventListener("popstate", this.handleYouTubeNavigate.bind(this)); 1261 | window.addEventListener("load", this.handleLoad.bind(this)); 1262 | } 1263 | 1264 | handleMessage(request) { 1265 | if (request.action === "clearLocalStorage") { 1266 | this.clearLocalStorage(); 1267 | } else if (request.action === "saveSettings") { 1268 | saveSettings(); 1269 | } else if (request.action === "importSettings") { 1270 | importSettings(request.content); 1271 | } else { 1272 | this.elementManager.handleAction(request.action); 1273 | } 1274 | } 1275 | 1276 | clearLocalStorage() { 1277 | chrome.storage.local.clear(() => { 1278 | chrome.storage.local.set( 1279 | { elements: JSON.stringify(STORAGE.tubemod_elements) }, 1280 | () => { 1281 | console.info("Default settings restored."); 1282 | location.reload(); 1283 | } 1284 | ); 1285 | }); 1286 | } 1287 | 1288 | handleLoad() { 1289 | this.elementManager.applyAllElements(getCurrentPageType()); 1290 | this.elementManager.setupObserver(); 1291 | } 1292 | 1293 | handleYouTubeNavigate() { 1294 | this.elementManager.applyAllElements(getCurrentPageType()); 1295 | } 1296 | } 1297 | 1298 | const tubeMod = new TubeMod(); 1299 | --------------------------------------------------------------------------------