diff --git a/chrome/activate-page-action.js b/chrome/activate-page-action.js new file mode 100644 index 0000000..458ca1d --- /dev/null +++ b/chrome/activate-page-action.js @@ -0,0 +1,2 @@ +// https://stackoverflow.com/questions/35882089/popup-is-not-appearing-when-used-page-action +chrome.runtime.sendMessage({"message": "activate_icon"}); diff --git a/chrome/background.js b/chrome/background.js new file mode 100644 index 0000000..b2404ad --- /dev/null +++ b/chrome/background.js @@ -0,0 +1,8 @@ +// https://stackoverflow.com/questions/35882089/popup-is-not-appearing-when-used-page-action +const activatePageAction = (request, sender, sendResponse) => { + if (request.message === "activate_icon") { + chrome.pageAction.show(sender.tab.id); + } +} + +chrome.extension.onMessage.addListener(activatePageAction); diff --git a/icons/no-16.png b/chrome/icons/no-16.png similarity index 100% rename from icons/no-16.png rename to chrome/icons/no-16.png diff --git a/icons/no-32.png b/chrome/icons/no-32.png similarity index 100% rename from icons/no-32.png rename to chrome/icons/no-32.png diff --git a/icons/no-64.png b/chrome/icons/no-64.png similarity index 100% rename from icons/no-64.png rename to chrome/icons/no-64.png diff --git a/chrome/manifest.json b/chrome/manifest.json new file mode 100644 index 0000000..281d274 --- /dev/null +++ b/chrome/manifest.json @@ -0,0 +1,44 @@ +{ + "name": "Remove Youtube Suggestions", + "description": "Removes all suggestions from Youtube.", + "homepage_url": "https://github.com/emumoo/remove-youtube-suggestions", + "manifest_version": 2, + "version": "1.0.0", + + "content_scripts": [ + { + "matches": ["*://*.youtube.com/*"], + "js": ["activate-page-action.js"], + "run_at": "document_start" + }, + { + "matches": ["*://*.youtube.com/*"], + "js": ["remove-homepage-suggestions.js"], + "run_at": "document_start" + }, + { + "matches": ["*://*.youtube.com/*"], + "js": ["remove-other-suggestions.js"], + "run_at": "document_end" + } + ], + + "background": { + "scripts": ["background.js"], + "persistent": false + }, + + "page_action": { + "browser_style": true, + "show_matches": ["*youtube*"], + "default_popup": "options.html", + "default_title": "Options", + "default_icon": { + "16": "icons/no-16.png", + "32": "icons/no-32.png", + "64": "icons/no-64.png" + } + }, + + "permissions": ["storage"] +} diff --git a/chrome/options.css b/chrome/options.css new file mode 100644 index 0000000..30a1341 --- /dev/null +++ b/chrome/options.css @@ -0,0 +1,20 @@ +body { + width: 300px; +} + +#button_container { + text-align: center; +} + +#help-box { + text-align: left; + padding: 8px; + border-style: ridge; + border-color: gray; + + margin: 4px 2px 1px 2px; +} + +#help-header { + text-align: center; +} \ No newline at end of file diff --git a/options.html b/chrome/options.html similarity index 100% rename from options.html rename to chrome/options.html diff --git a/chrome/options.js b/chrome/options.js new file mode 100644 index 0000000..598345f --- /dev/null +++ b/chrome/options.js @@ -0,0 +1,48 @@ +// Chrome +if (typeof(chrome) !== 'undefined') { + browser = chrome; +} + +// document.querySelectorAll("RYS_setting_checkbox"); +const homepageCheckbox = document.getElementById('homepage'); +const sidebarCheckbox = document.getElementById('sidebar'); +const videoEndCheckbox = document.getElementById('videoEnd'); +const commentsCheckbox = document.getElementById('comments'); +homepageCheckbox.addEventListener("change", saveHomepageSetting); +sidebarCheckbox.addEventListener("change", saveSidebarSetting); +videoEndCheckbox.addEventListener("change", saveVideoEndSetting); +commentsCheckbox.addEventListener("change", saveCommentsSetting); + +document.addEventListener("DOMContentLoaded", restoreOptions); + +function saveHomepageSetting(e) { + browser.storage.local.set({ homepage: e.target.checked }); +} + +function saveSidebarSetting(e) { + browser.storage.local.set({ sidebar: e.target.checked }); +} + +function saveVideoEndSetting(e) { + browser.storage.local.set({ videoEnd: e.target.checked }); +} + +function saveCommentsSetting(e) { + browser.storage.local.set({ comments: e.target.checked }); +} + +function restoreOptions() { + + function setCurrentChoice(result) { + for (let key in result) { + document.getElementById(key).checked = result[key]; + } + } + + function onError(error) { + console.log(`Error: ${error}`); + } + + const settings = ["homepage", "sidebar", "videoEnd", "comments"]; + settings.forEach(setting => browser.storage.local.get(setting, setCurrentChoice)); +} diff --git a/chrome/reload.js b/chrome/reload.js new file mode 100644 index 0000000..ec45360 --- /dev/null +++ b/chrome/reload.js @@ -0,0 +1,10 @@ +const reloadButton = document.getElementById("reload_button"); +reloadButton.addEventListener("click", reload); + +console.log("hi"); + +async function reload() { + chrome.tabs.query({ active: true, currentWindow: true }, tabs => { + console.log(tabs); + }); +} diff --git a/remove-homepage-suggestions.js b/chrome/remove-homepage-suggestions.js similarity index 100% rename from remove-homepage-suggestions.js rename to chrome/remove-homepage-suggestions.js diff --git a/remove-other-suggestions.js b/chrome/remove-other-suggestions.js similarity index 100% rename from remove-other-suggestions.js rename to chrome/remove-other-suggestions.js diff --git a/firefox/icons/no-16.png b/firefox/icons/no-16.png new file mode 100644 index 0000000..59ecf71 Binary files /dev/null and b/firefox/icons/no-16.png differ diff --git a/firefox/icons/no-32.png b/firefox/icons/no-32.png new file mode 100644 index 0000000..ae8f21f Binary files /dev/null and b/firefox/icons/no-32.png differ diff --git a/firefox/icons/no-64.png b/firefox/icons/no-64.png new file mode 100644 index 0000000..868a2ac Binary files /dev/null and b/firefox/icons/no-64.png differ diff --git a/manifest.json b/firefox/manifest.json similarity index 91% rename from manifest.json rename to firefox/manifest.json index f8a15b4..f2c28de 100644 --- a/manifest.json +++ b/firefox/manifest.json @@ -19,13 +19,15 @@ ], "options_ui": { - "page": "options.html" + "page": "options.html", + "open_in_tab": false }, "page_action": { "browser_style": true, "show_matches": ["*://*.youtube.com/*"], "default_popup": "options.html", + "default_title": "Options", "default_icon": { "16": "icons/no-16.png", "32": "icons/no-32.png", diff --git a/options.css b/firefox/options.css similarity index 100% rename from options.css rename to firefox/options.css diff --git a/firefox/options.html b/firefox/options.html new file mode 100644 index 0000000..9b19135 --- /dev/null +++ b/firefox/options.html @@ -0,0 +1,60 @@ + + + + + + + + + +
+ Choose which suggestions to remove + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ +
+ +
+
+ +
+
+ Help us make this addon better! +
+
+ Submit feedback here. +
+ Donate here. +
+ + + + + + \ No newline at end of file diff --git a/options.js b/firefox/options.js similarity index 100% rename from options.js rename to firefox/options.js diff --git a/reload.js b/firefox/reload.js similarity index 100% rename from reload.js rename to firefox/reload.js diff --git a/firefox/remove-homepage-suggestions.js b/firefox/remove-homepage-suggestions.js new file mode 100644 index 0000000..5b0da9b --- /dev/null +++ b/firefox/remove-homepage-suggestions.js @@ -0,0 +1,108 @@ +// Chrome +if (typeof(chrome) !== 'undefined') { + browser = chrome; + + browser.storage.local.get("homepage", onGotHomepageSetting); + +// Firefox +} else { + + const gettingHomepage = browser.storage.local.get("homepage"); + gettingHomepage.then(onGotHomepageSetting, onError); + +} + + +/*********** + * Homepage + ***********/ +const homepageSelectors = [ + "ytd-page-manager", + "ytd-browse", + "#feed", + "ytd-rich-grid-renderer.style-scope", + "#masthead-ad", + "#spinner-container", + "#home-container-skeleton", + "#rich-shelves", + ".shelf-skeleton", + ".rich-shelf-videos" +]; +const hompageSuggestionsSelector = homepageSelectors.join(', '); +const hideHomepageNodes = node => { + if (node.matches(hompageSuggestionsSelector)) { + node.style.setProperty("display", "none"); + } +}; +const removeHomepageObserver = new MutationObserver(mutations => { + mutations.forEach(mutation => { + hideHomepageNodes(mutation.target); + }); +}); +removeHomepageObserver.observe(document, { + childList: true, + subtree: true, +}); +function onGotHomepageSetting(result) { + const homepage = result.homepage === undefined ? true : result.homepage; + browser.storage.local.set({ homepage }); + updateHomepageStyle(homepage); + + // watch for url changes + const updateHomepageObserver = new MutationObserver(mutations => updateHomepageStyle(homepage)); + updateHomepageObserver.observe(document, { + childList: true, + subtree: true, + }); + + removeHomepageObserver.disconnect(); +} + +function updateHomepageStyle(homepage) { + updateHomepage(homepage && onHomepage() ? "none" : "flex"); +} + +function onHomepage() { + try { + // Test using path + const pathIsHomepage = window.location.pathname.length <= 1; + + // Test using right-sidebar icons + const iconSelector = "span.title.style-scope.ytd-mini-guide-entry-renderer"; + const icons = document.querySelectorAll(iconSelector); + const iconColors = Array.from(icons).map(icon => window.getComputedStyle(icon, null).getPropertyValue("color")); + const iconColorCounts = getCounts(iconColors); + const homeIcon = icons.length > 0 && icons[0]; + const homeIconColor = homeIcon && window.getComputedStyle(homeIcon, null).getPropertyValue("color"); + const iconColorIsHomepage = homeIconColor && iconColorCounts[homeIconColor] === 1; + + return pathIsHomepage || iconColorIsHomepage; + } catch (e) { + console.log(e); + } +} + +function updateHomepage(property) { + const updateDisplay = elt => elt.style.setProperty("display", property); + const suggestions = document.querySelectorAll(hompageSuggestionsSelector) || []; + suggestions.forEach(updateDisplay); +} + + +function removeSelectors(selectors) { + const cssSheet = document.styleSheets.length > 0 && document.styleSheets[0]; + const displayNoneRule = " { display: none !important; }"; + const compoundSelector = selectors.join(", "); + cssSheet && cssSheet.insertRule(compoundSelector + displayNoneRule); +} + +function onError(error) { + console.log(`Error: ${error}`); +} + +function getCounts(arr) { + return arr.reduce((acc, curr) => { + acc[curr] = curr in acc ? acc[curr] + 1 : 1; + return acc; + }, {}); +} \ No newline at end of file diff --git a/firefox/remove-other-suggestions.js b/firefox/remove-other-suggestions.js new file mode 100644 index 0000000..9c7fbc8 --- /dev/null +++ b/firefox/remove-other-suggestions.js @@ -0,0 +1,109 @@ +// Chrome +if (typeof(chrome) !== 'undefined') { + browser = chrome; + + try { + browser.storage.local.get("sidebar", onGotSidebarSetting); + browser.storage.local.get("videoEnd", onGotVideoEndSetting); + browser.storage.local.get("comments", onGotCommentsSetting); + } catch (e) { + console.log(e); + } + +// Firefox +} else { + + try { + const gettingSidebar = browser.storage.local.get("sidebar"); + gettingSidebar.then(onGotSidebarSetting, onError); + + const gettingVideoEnd = browser.storage.local.get("videoEnd"); + gettingVideoEnd.then(onGotVideoEndSetting, onError); + + const gettingComments = browser.storage.local.get("comments"); + gettingComments.then(onGotCommentsSetting, onError); + } catch (e) { + console.log(e); + } + +} + + +/********** + * Sidebar + **********/ +function onGotSidebarSetting(result) { + const sidebar = result.sidebar === undefined ? true : result.sidebar; + browser.storage.local.set({ sidebar }); + + if (sidebar) { + removeSelectors([ + // video recommendations + "#upnext", + "ytd-compact-video-renderer.style-scope", + "ytd-compact-radio-renderer.style-scope", + + // ads + "#companion", + "ytd-compact-promoted-video-renderer", + "#container.ytd-iframe-companion-renderer", + "#dismissable.ytd-compact-movie-renderer", // movie recommendations + "ytd-movie-offer-module-renderer", // movie recommendations + "ytd-image-companion-renderer.style-scope", + "ytd-compact-playlist-renderer.style-scope", + "a.ytd-action-companion-renderer", + "#google_companion_ad_div", + "ytd-promoted-sparkles-web-renderer", + "ytd-player-legacy-desktop-watch-ads-renderer", + + "paper-button.yt-next-continuation", // show more button + + // disable_polymer=true + "li.video-list-item.related-list-item", + "h4.watch-sidebar-head", + "hr.watch-sidebar-separation-line", + "button#watch-more-related-button", + ]); + } +} + +/*************** + * End of Video + ***************/ +function onGotVideoEndSetting(result) { + const videoEnd = result.videoEnd === undefined ? true : result.videoEnd; + browser.storage.local.set({ videoEnd }); + if (videoEnd) { + removeSelectors([ + ".html5-endscreen" + ]); + } +} + +/*********** + * Comments + ***********/ +function onGotCommentsSetting(result) { + const comments = result.comments === undefined ? false : result.comments; + browser.storage.local.set({ comments }); + if (comments) { + removeSelectors([ + // standard + "ytd-comment-thread-renderer", + + // for disable_polymer=true + "#comment-secyoutubetion-renderer-items" + ]); + } +} + +function removeSelectors(selectors) { + const cssSheet = document.styleSheets.length > 0 && document.styleSheets[0]; + const displayNoneRule = " { display: none !important; }"; + const compoundSelector = selectors.join(", "); + cssSheet && cssSheet.insertRule(compoundSelector + displayNoneRule); +} + +function onError(error) { + console.log(`Error: ${error}`); +}