diff --git a/js&css/extension/init.js b/js&css/extension/init.js
index ac9a91a25..b6f5e5116 100644
--- a/js&css/extension/init.js
+++ b/js&css/extension/init.js
@@ -176,18 +176,19 @@ document.addEventListener('it-message-from-youtube', function () {
}
} else if (message.action === 'blocklist') {
if (!extension.storage.data.blocklist || typeof extension.storage.data.blocklist !== 'object') {
- extension.storage.data.blocklist = {};
+ extension.storage.data.blocklist = {videos: {}, channels: {}};
}
switch(message.type) {
case 'channel':
- if (!extension.storage.data.blocklist.channels) {
+ if (!extension.storage.data.blocklist.channels || typeof extension.storage.data.blocklist.channels !== 'object') {
extension.storage.data.blocklist.channels = {};
}
if (message.added) {
extension.storage.data.blocklist.channels[message.id] = {
title: message.title,
- preview: message.preview
+ preview: message.preview,
+ when: message.when
}
} else {
delete extension.storage.data.blocklist.channels[message.id];
@@ -195,12 +196,13 @@ document.addEventListener('it-message-from-youtube', function () {
break
case 'video':
- if (!extension.storage.data.blocklist.videos) {
+ if (!extension.storage.data.blocklist.videos || typeof extension.storage.data.blocklist.videos !== 'object') {
extension.storage.data.blocklist.videos = {};
}
if (message.added) {
extension.storage.data.blocklist.videos[message.id] = {
- title: message.title
+ title: message.title,
+ when: message.when
}
} else {
delete extension.storage.data.blocklist.videos[message.id];
diff --git a/js&css/extension/www.youtube.com/styles.css b/js&css/extension/www.youtube.com/styles.css
index d017b09e7..49e4ea1ef 100644
--- a/js&css/extension/www.youtube.com/styles.css
+++ b/js&css/extension/www.youtube.com/styles.css
@@ -365,9 +365,9 @@ html[it-channel-hide-featured-content=true] #secondary ytd-browse-secondary-cont
content: '';
border-radius: 50%;
background: #f00;
- /* background-image: url('/stuff/icons/48.png');
- background-size: cover;
- background-position: center; */
+ /* background-image: url('/stuff/icons/48.png');
+ background-size: cover;
+ background-position: center; */
}
.it-button::after {
@@ -463,7 +463,7 @@ ytd-guide-section-renderer .it-button::after {
position: absolute;
top: 4px;
left: 4px;
- z-index: 999;
+ z-index: 2400;
visibility: hidden;
width: 28px;
height: 28px;
@@ -530,6 +530,8 @@ ytd-guide-section-renderer .it-button::after {
cursor: pointer;
}
+ytd-video-preview.it-blocklisted-video:hover .it-add-to-blocklist,
+ytd-video-preview.it-blocklisted-channel:hover .it-add-to-blocklist,
*:hover>.it-add-to-blocklist {
visibility: visible;
}
@@ -545,48 +547,66 @@ ytd-guide-section-renderer .it-button::after {
}
.it-blocklisted-video .it-add-to-blocklist::after {
- content: "Unblock Video";
+ content: "Unblock Video";
}
.it-blocklisted-channel .it-add-to-blocklist::after {
- content: "Unblock Channel"!important;
+ content: "Unblock Channel"!important;
}
.it-blocklisted-video,
-.it-blocklisted-channel {
+html:not([data-page-type=channel]) .it-blocklisted-channel {
opacity: .15;
max-height: 4rem;
overflow: hidden;
transition: max-height 0.4s ease 0.1s;
}
+.it-blocklisted-video.ytd-vertical-list-renderer,
+.it-blocklisted-video.ytd-item-section-renderer,
+html:not([data-page-type=channel]) .it-blocklisted-channel.ytd-vertical-list-renderer,
+html:not([data-page-type=channel]) .it-blocklisted-channel.ytd-item-section-renderer {
+ max-height: 120px;
+}
+
ytd-grid-video-renderer .it-blocklisted-video,
-ytd-grid-video-renderer .it-blocklisted-channel,
+html:not([data-page-type=channel]) ytd-grid-video-renderer .it-blocklisted-channel,
ytd-rich-grid-media .it-blocklisted-video,
-ytd-rich-grid-media .it-blocklisted-channel {
+html:not([data-page-type=channel]) ytd-rich-grid-media .it-blocklisted-channel {
overflow: visible;
+ max-height: 120px;
+}
+
+ytd-video-preview.it-blocklisted-video:hover .it-add-to-blocklist,
+html:not([data-page-type=channel]) ytd-video-preview.it-blocklisted-channel:hover .it-add-to-blocklist,
+body:has(.it-blocklisted-video:hover) ytd-video-preview.it-blocklisted-video,
+html:not([data-page-type=channel]) body:has(.it-blocklisted-channel:hover) ytd-video-preview.it-blocklisted-channel {
+ max-height: var(--ytd-video-preview-height);
+ opacity: 1;
}
.it-blocklisted-video ytd-thumbnail,
-.it-blocklisted-channel ytd-thumbnail {
+html:not([data-page-type=channel]) .it-blocklisted-channel ytd-thumbnail {
visibility: hidden;
max-width: 0;
transition: max-width 0.4s ease 0.1s;
}
.it-blocklisted-video:hover,
-.it-blocklisted-channel:hover {
+html:not([data-page-type=channel]) .it-blocklisted-channel:hover {
opacity: 1;
overflow: visible;
- max-height: 120px;
+ height: auto;
+ max-height: fit-content;
transition: max-height 0.4s ease 1.1s;
}
.it-blocklisted-video:hover ytd-thumbnail,
-.it-blocklisted-channel:hover ytd-thumbnail {
+html:not([data-page-type=channel]) .it-blocklisted-channel:hover ytd-thumbnail {
visibility: visible;
max-width: 220px;
transition: max-width 0.4s ease 1.1s;
+ height: auto;
}
/*------------NEW---------------*/
@@ -1975,7 +1995,7 @@ html[it-theme=sunset][data-system-color-scheme=light][it-schedule=system_peferen
--ytd-simple-badge-color: hsla(0, 0%, 100%, .6);
--ytd-ad-badge-text-color: hsl(0, 0%, 7%);
--ytd-shopping-product-info: hsla(0, 100%, 100%, .74);
- --ytd-toggle-color: hsl(0, 0%, 93.3%);
+ --ytd-toggle-color: hsl(0, 0%, 93.3%);
--ytd-survey-button-color: var(--yt-primary-text-color);
--ytd-transcript-cue-hover-background-color: hsla(0, 0%, 53.3%, .4);
--ytd-transcript-toolbar-background-color: hsla(0, 0%, 53.3%, .4);
@@ -2076,7 +2096,7 @@ Need HTML in front to make CSS rule more specific than one they are overiding
/*possible fix:
#hover-overlays .yt-spec-icon-shape,
-ytd-thumbnail-overlay-toggle-button-renderer .yt-spec-icon-shape {color:white;}
+ytd-thumbnail-overlay-toggle-button-renderer .yt-spec-icon-shape {color:white;}
*/
/* html .yt-spec-icon-shape, */
@@ -2084,12 +2104,12 @@ html .yt-spec-icon-badge-shape--style-overlay .yt-spec-icon-badge-shape__icon,
html .yt-spec-button-shape-next--mono.yt-spec-button-shape-next--text,
html .yt-spec-button-shape-next--mono.yt-spec-button-shape-next--tonal,
html .yt-video-attribute-view-model__title {
- color: var(--yt-spec-text-primary);
+ color: var(--yt-spec-text-primary);
}
/*Dark colors get highlight*/
html .yt-spec-button-shape-next--mono.yt-spec-button-shape-next--tonal {
- background-color: rgba(255, 255, 255, 0.1);
+ background-color: rgba(255, 255, 255, 0.1);
}
/*Light colors get shadow, overrides above highlight*/
html[it-theme=desert] .yt-spec-button-shape-next--mono.yt-spec-button-shape-next--tonal,
@@ -2100,8 +2120,8 @@ html:not([dark]):not([it-theme=black]):not([it-theme=sunset]):not([it-theme=nigh
/*subscribe button when not subscribed*/
html .yt-spec-button-shape-next--mono.yt-spec-button-shape-next--filled {
- color: var(--yt-spec-base-background);
- background-color: var(--yt-spec-text-primary);
+ color: var(--yt-spec-base-background);
+ background-color: var(--yt-spec-text-primary);
}
/*override bell and thumbs up icons hardcoded colors inside SVG data*/
diff --git a/js&css/web-accessible/core.js b/js&css/web-accessible/core.js
index 1cf120ddf..681bf7dc5 100644
--- a/js&css/web-accessible/core.js
+++ b/js&css/web-accessible/core.js
@@ -40,6 +40,12 @@ var ImprovedTube = {
playlist_id: /[?&]list=([^&]+)/,
channel_link: /https:\/\/www.youtube.com\/@|((channel|user|c)\/)/
},
+ button_icons: {
+ blocklist:{
+ svg: [['viewBox', '0 0 24 24']],
+ path: [['d', 'M12 2a10 10 0 100 20 10 10 0 000-20zm0 18A8 8 0 015.69 7.1L16.9 18.31A7.9 7.9 0 0112 20zm6.31-3.1L7.1 5.69A8 8 0 0118.31 16.9z']]
+ }
+ },
video_src: false,
initialVideoUpdateDone: false,
latestVideoDuration: 0,
@@ -174,8 +180,7 @@ document.addEventListener('it-message-from-extension', function () {
}
ImprovedTube.init();
- // need to run blocklist once just after page load to catch initial nodes
- ImprovedTube.blocklist();
+ ImprovedTube.blocklistInit();
// REACTION OR VISUAL FEEDBACK WHEN THE USER CHANGES A SETTING (already automated for our CSS features):
} else if (message.action === 'storage-changed') {
@@ -199,8 +204,9 @@ document.addEventListener('it-message-from-extension', function () {
}
switch(camelized_key) {
+ case 'blocklist':
case 'blocklistActivate':
- camelized_key = 'blocklist';
+ ImprovedTube.blocklistInit();
break
case 'playerPlaybackSpeed':
diff --git a/js&css/web-accessible/functions.js b/js&css/web-accessible/functions.js
index 02530b626..458b3d1d4 100644
--- a/js&css/web-accessible/functions.js
+++ b/js&css/web-accessible/functions.js
@@ -33,43 +33,21 @@ ImprovedTube.childHandler = function (node) { //console.log(node.nodeName);
}; */
ImprovedTube.ytElementsHandler = function (node) {
- var name = node.nodeName,
+ const name = node.nodeName,
id = node.id;
if (name === 'A') {
if (node.href) {
this.channelDefaultTab(node);
-
- if (this.storage.blocklist_activate && node.classList.contains('ytd-thumbnail')) {
- this.blocklist('video', node);
- }
}
- } /* else if (name === 'META') { // infos are not updated when clicking related videos...
- if(node.getAttribute('name')) {
- //if(node.getAttribute('name') === 'title') {ImprovedTube.title = node.content;} //duplicate
- //if(node.getAttribute('name') === 'description') {ImprovedTube.description = node.content;} //duplicate
- //if node.getAttribute('name') === 'themeColor') {ImprovedTube.themeColor = node.content;} //might help our darkmode/themes
-//Do we need any of these here before the player starts?
- //if(node.getAttribute('name') === 'keywords') {ImprovedTube.keywords = node.content;}
- } else if (node.getAttribute('itemprop')) {
- //if(node.getAttribute('itemprop') === 'name') {ImprovedTube.title = node.content;}
- if(node.getAttribute('itemprop') === 'genre') {ImprovedTube.category = node.content;}
- //if(node.getAttribute('itemprop') === 'channelId') {ImprovedTube.channelId = node.content;}
- //if(node.getAttribute('itemprop') === 'videoId') {ImprovedTube.videoId = node.content;}
-//The following infos will enable awesome, smart features. Some of which everyone should use.
- //if(node.getAttribute('itemprop') === 'description') {ImprovedTube.description = node.content;}
- //if(node.getAttribute('itemprop') === 'duration') {ImprovedTube.duration = node.content;}
- //if(node.getAttribute('itemprop') === 'interactionCount'){ImprovedTube.views = node.content;}
- //if(node.getAttribute('itemprop') === 'isFamilyFriendly'){ImprovedTube.isFamilyFriendly = node.content;}
- //if(node.getAttribute('itemprop') === 'unlisted') {ImprovedTube.unlisted = node.content;}
- //if(node.getAttribute('itemprop') === 'regionsAllowed'){ImprovedTube.regionsAllowed = node.content;}
- //if(node.getAttribute('itemprop') === 'paid') {ImprovedTube.paid = node.content;}
- // if(node.getAttribute('itemprop') === 'datePublished' ){ImprovedTube.datePublished = node.content;}
- //to use in the "how long ago"-feature, not to fail without API key? just like the "day-of-week"-feature above
- // if(node.getAttribute('itemprop') === 'uploadDate') {ImprovedTube.uploadDate = node.content;}
+ if (this.storage.blocklist_activate) {
+ // we are interested in thumbnails and video-previews, skip ones with 'button.it-add-to-blocklist' already
+ if (((node.href && node.classList.contains('ytd-thumbnail')) || node.classList.contains('ytd-video-preview'))
+ && !node.querySelector("button.it-add-to-blocklist")) {
+ this.blocklistNode(node);
+ }
}
- } */
- else if (name === 'YTD-TOGGLE-BUTTON-RENDERER' || name === 'YTD-PLAYLIST-LOOP-BUTTON-RENDERER') {
+ } else if (name === 'YTD-TOGGLE-BUTTON-RENDERER' || name === 'YTD-PLAYLIST-LOOP-BUTTON-RENDERER') {
//can be precise previously node.parentComponent & node.parentComponent.parentComponent
if (node.closest("YTD-MENU-RENDERER")
&& node.closest("YTD-PLAYLIST-PANEL-RENDERER")) {
@@ -137,10 +115,7 @@ ImprovedTube.ytElementsHandler = function (node) {
else if (name === 'YTD-PLAYLIST-HEADER-RENDERER' || (name === 'YTD-MENU-RENDERER' && node.classList.contains('ytd-playlist-panel-renderer'))) {
this.playlistPopupUpdate();
} else if (name === 'YTD-SUBSCRIBE-BUTTON-RENDERER' || name === 'YT-SUBSCRIBE-BUTTON-VIEW-MODEL') {
- if (this.storage.blocklist_activate && location.href.match(ImprovedTube.regex.channel)) {
- ImprovedTube.blocklist('channel', node);
- }
-
+ ImprovedTube.blocklistChannel(node);
ImprovedTube.elements.subscribe_button = node;
} else if (id === 'chat-messages') {
this.elements.livechat.button = document.querySelector('[aria-label="Close"]');
@@ -553,6 +528,35 @@ ImprovedTube.setCookie = function (name, value) {
document.cookie = name + '=' + value + '; path=/; domain=.youtube.com; expires=' + date.toGMTString();
};
+ImprovedTube.createIconButton = function (options) {
+ const button = document.createElement('button'),
+ svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
+ path = document.createElementNS('http://www.w3.org/2000/svg', 'path'),
+ type = this.button_icons[options.type];
+
+ for(const attr of type.svg) svg.setAttribute(attr[0], attr[1]);
+ for(const attr of type.path) path.setAttribute(attr[0], attr[1]);
+
+ svg.appendChild(path);
+ button.appendChild(svg);
+
+ if (options.className) button.className = options.className;
+ if (options.id) button.id = options.id;
+ if (options.onclick) {
+ if (!options.propagate) {
+ //we fully own all click events landing on this button
+ button.onclick = function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+ options.onclick.apply(this, arguments);
+ }
+ } else {
+ button.onclick = options.onclick;
+ }
+ }
+ return button;
+};
+
ImprovedTube.createPlayerButton = function (options) {
var controls = options.position == "right" ? this.elements.player_right_controls : this.elements.player_left_controls;
if (controls) {
diff --git a/js&css/web-accessible/init.js b/js&css/web-accessible/init.js
index b4f5e01a8..66c09bcd9 100644
--- a/js&css/web-accessible/init.js
+++ b/js&css/web-accessible/init.js
@@ -121,6 +121,30 @@ ImprovedTube.init = function () {
};
document.addEventListener('yt-navigate-finish', function () {
+/* if (name === 'META') { // infos are not updated when clicking related videos...
+ if(node.getAttribute('name')) {
+ //if(node.getAttribute('name') === 'title') {ImprovedTube.title = node.content;} //duplicate
+ //if(node.getAttribute('name') === 'description') {ImprovedTube.description = node.content;} //duplicate
+ //if node.getAttribute('name') === 'themeColor') {ImprovedTube.themeColor = node.content;} //might help our darkmode/themes
+//Do we need any of these here before the player starts?
+ //if(node.getAttribute('name') === 'keywords') {ImprovedTube.keywords = node.content;}
+ } else if (node.getAttribute('itemprop')) {
+ //if(node.getAttribute('itemprop') === 'name') {ImprovedTube.title = node.content;}
+ if(node.getAttribute('itemprop') === 'genre') {ImprovedTube.category = node.content;}
+ //if(node.getAttribute('itemprop') === 'channelId') {ImprovedTube.channelId = node.content;}
+ //if(node.getAttribute('itemprop') === 'videoId') {ImprovedTube.videoId = node.content;}
+//The following infos will enable awesome, smart features. Some of which everyone should use.
+ //if(node.getAttribute('itemprop') === 'description') {ImprovedTube.description = node.content;}
+ //if(node.getAttribute('itemprop') === 'duration') {ImprovedTube.duration = node.content;}
+ //if(node.getAttribute('itemprop') === 'interactionCount'){ImprovedTube.views = node.content;}
+ //if(node.getAttribute('itemprop') === 'isFamilyFriendly'){ImprovedTube.isFamilyFriendly = node.content;}
+ //if(node.getAttribute('itemprop') === 'unlisted') {ImprovedTube.unlisted = node.content;}
+ //if(node.getAttribute('itemprop') === 'regionsAllowed'){ImprovedTube.regionsAllowed = node.content;}
+ //if(node.getAttribute('itemprop') === 'paid') {ImprovedTube.paid = node.content;}
+ // if(node.getAttribute('itemprop') === 'datePublished' ){ImprovedTube.datePublished = node.content;}
+ //to use in the "how long ago"-feature, not to fail without API key? just like the "day-of-week"-feature above
+ // if(node.getAttribute('itemprop') === 'uploadDate') {ImprovedTube.uploadDate = node.content;}
+*/
ImprovedTube.pageType();
if(ImprovedTube.storage.undo_the_new_sidebar === true){ImprovedTube.undoTheNewSidebar();}
ImprovedTube.commentsSidebar();
diff --git a/js&css/web-accessible/www.youtube.com/blocklist.js b/js&css/web-accessible/www.youtube.com/blocklist.js
index 9a7cef9e4..c66886e1c 100644
--- a/js&css/web-accessible/www.youtube.com/blocklist.js
+++ b/js&css/web-accessible/www.youtube.com/blocklist.js
@@ -1,201 +1,170 @@
/*------------------------------------------------------------------------------
4.8.0 BLOCKLIST
------------------------------------------------------------------------------*/
-// usage:
-// () called only to turn On (rescans all elements on page)/Off
-// ('video', node) called only for 'a#thumbnail.ytd-thumbnail[href]'
-// ('channel', node) called only for 'ytd-subscribe-button-renderer.ytd-c4-tabbed-header-renderer'
+ImprovedTube.blocklistNode = function (node) {
+ if (!this.storage.blocklist_activate || !node) return;
-ImprovedTube.blocklist = function (type, node) {
- if (this.storage.blocklist_activate) {
- if (type === 'video') {
- if (node.nodeName !== 'A' || !node.href) { alert(1) };
- const video = node.href.match(ImprovedTube.regex.video_id)?.[1],
- channel = node.parentNode.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url ? node.parentNode.__dataHost.__data.data.shortBylineText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url.match(ImprovedTube.regex.channel).groups.name : undefined;
- let mode = 'video',
- blockedElement;
- if (!video) return; // no video ID, something went horribly wrong, bail
-
- // YT reuses VIDEO elements dynamically, need to monitor and also dynamically readjust BLOCK style
- if (!this.elements.observerList.includes(node)) {
- // YT reuses VIDEO elements dynamically, need to monitor and also dynamically readjust BLOCK style whenever href is modified
- this.blocklistObserver.observe(node, {attributes: true,
- attributeFilter: ['href']});
- // keep track to only attach one observer per element
- this.elements.observerList.push(node);
- }
+ const video = node.href?.match(ImprovedTube.regex.video_id)?.[1] || (node.classList?.contains('ytd-video-preview') ? 'video-preview' : null),
+ channel = node.parentNode?.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url?.match(ImprovedTube.regex.channel)?.groups?.name,
+ blockedElement = node.blockedElement || this.blockedElementTypeHelper(node);
- switch(node.parentNode.className.replace('style-scope ','')) {
- case 'ytd-compact-video-renderer':
- // list next to player
- // node.parentNode.__dataHost.$.dismissible;
- case 'ytd-rich-item-renderer':
- // short reel
- case 'ytd-rich-grid-media':
- // grid reel
- case 'ytd-rich-grid-slim-media':
- // short grid reel
- case 'ytd-playlist-video-renderer':
- // playlist page
- case 'ytd-playlist-panel-video-renderer':
- // playlist next to player
- // node.parentNode.closest('ytd-playlist-panel-video-renderer')
- case 'ytd-structured-description-video-lockup-renderer':
- // list under the player
- // node.parentNode.closest('ytd-structured-description-video-lockup-renderer')
- // or even node.parentNode.closest('ytd-compact-infocard-renderer') === node.parentNode.parentNode.parentNode.parentNode
- blockedElement = node.parentNode.parentNode.parentNode;
- break;
- case 'ytd-grid-video-renderer':
- // channel home screen grid
- case 'ytd-reel-item-renderer':
- // reel
- blockedElement = node.parentNode.parentNode;
- break;
- }
+ if (!video) return; // not interested in nodes without one
- if (!blockedElement) return; // couldnt find valid enveloping element, bail
+ // YT reuses Thumbnail cells dynamically, need to monitor all created Thumbnail links and dynamically apply/remove 'it-blocklisted-*' classes
+ if (!this.elements.observerList.includes(node)) {
+ this.blocklistObserver.observe(node, {attributes: true,
+ attributeFilter: ['href']});
+ // keeping a list to attach only one observer per tracked element
+ this.elements.observerList.push(node);
+ }
- node.blockedElement = blockedElement;
- if (this.storage.blocklist) {
- if (this.storage.blocklist.videos) {
- if (this.storage.blocklist.videos[video] && !blockedElement.classList.contains('it-blocklisted-video')) {
- // blocklisted video
- blockedElement.classList.add('it-blocklisted-video');
- } else if (!this.storage.blocklist.videos[video] && blockedElement.classList.contains('it-blocklisted-video')) {
- // video not blocklisted, show it
- blockedElement.classList.remove('it-blocklisted-video');
- }
- }
- if (channel && this.storage.blocklist.channels ) {
- // this thumbnail has channel information, can try channel blocklist
- if (this.storage.blocklist.channels[channel] && !blockedElement.classList.contains('it-blocklisted-channel')) {
- // blocked channel? = block all videos from that channel
- blockedElement.classList.add('it-blocklisted-channel');
- } else if (!this.storage.blocklist.channels[channel] && blockedElement.classList.contains('it-blocklisted-channel')) {
- // channel not blocked, show it
- blockedElement.classList.remove('it-blocklisted-channel');
- }
- }
+ if (!blockedElement) return; // unknown thumbnail cell type, bail out
+
+ if (this.storage.blocklist) {
+ if (this.storage.blocklist.videos && ImprovedTube.storage.blocklist.videos[video]) {
+ // blocklisted video
+ blockedElement.classList.add('it-blocklisted-video');
+ } else {
+ // video not blocklisted, show it. classList.remove() directly as there is no speed benefit to .has() before
+ blockedElement.classList.remove('it-blocklisted-video');
+ }
+ if (this.storage.blocklist.channels && channel && ImprovedTube.storage.blocklist.channels[channel]) {
+ // blocked channel
+ blockedElement.classList.add('it-blocklisted-channel');
+ } else {
+ // channel not blocked, show it.
+ blockedElement.classList.remove('it-blocklisted-channel');
+ }
}
- if (node.querySelector("button.it-add-to-blocklist")) return; // skip blocklist button creation if one already exists
-
- let button = document.createElement('button'),
- svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
- path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
-
- button.className = 'it-add-to-blocklist';
- button.addEventListener('click', function (event) {
- if (this.parentNode.href) {
-
- const video = node.href.match(ImprovedTube.regex.video_id)?.[1],
- channel = node.parentNode.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url ? node.parentNode.__dataHost.__data.data.shortBylineText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url.match(ImprovedTube.regex.channel).groups.name : undefined,
- data = this.parentNode.__dataHost.__data?.data?.title,
- blockedElement = node.blockedElement;
- let title,
- added = false,
- type = 'video';
-
- if (!video || !blockedElement) return; // need both video ID and blockedElement, otherwise bail
-
- if (data?.runs?.[0]?.text) {
- title = data.runs[0].text;
- } else if (data?.simpleText) {
- title = data.simpleText;
- }
-
- if (channel && blockedElement.classList.contains('it-blocklisted-channel')) {
- // unblocking channel
- type = 'channel';
- } else if (blockedElement.classList.contains('it-blocklisted-video')) {
- // unblocking blocklisted video
- } else {
- // nothing blocked, clicking should block this video
- added = true;
- }
- ImprovedTube.messages.send({action: 'blocklist',
- added: added,
- type: type,
- id: type == 'channel' ? channel : video,
- title: title});
- event.preventDefault();
- event.stopPropagation();
- }
- }, true);
-
- svg.setAttributeNS(null, 'viewBox', '0 0 24 24');
- path.setAttributeNS(null, 'd', 'M12 2a10 10 0 100 20 10 10 0 000-20zm0 18A8 8 0 015.69 7.1L16.9 18.31A7.9 7.9 0 0112 20zm6.31-3.1L7.1 5.69A8 8 0 0118.31 16.9z');
-
- svg.appendChild(path);
- button.appendChild(svg);
-
- node.appendChild(button);
- this.elements.blocklist_buttons.push(button);
- } else if (type === 'channel') {
- let button = node.parentNode.parentNode.querySelector("button.it-add-channel-to-blocklist"),
- id = location.href.match(ImprovedTube.regex.channel).groups.name;
-
- // skip channel blocklist button creation if one already exists
- if (button) {
- if (this.storage.blocklist.channels[id] && button.added) {
- button.innerText = 'Remove from blocklist';
- button.added = false;
- } else if (!this.storage.blocklist.channels[id] && !button.added) {
- button.innerText = 'Add to blocklist';
- button.added = true;
- }
+
+ // skip blocklist button creation if one already exists, in theory this never happens due to check in functions.js
+ if (node.querySelector("button.it-add-to-blocklist")) return;
+
+ node.blockedElement = blockedElement;
+
+ const button = this.createIconButton({
+ type: 'blocklist',
+ className: 'it-add-to-blocklist',
+ onclick: function (event) {
+ if (!this.parentNode.href) return; // no href no action
+ const video = this.parentNode.href?.match(ImprovedTube.regex.video_id)?.[1],
+ channel = this.parentNode.parentNode?.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url?.match(ImprovedTube.regex.channel)?.groups?.name
+ // video-preview doesnt have Channel info, extract from source thumbnail
+ || ((video && this.parentNode?.classList.contains('ytd-video-preview')) ? ImprovedTube.elements.observerList.find(a => a.id == 'thumbnail' && a.href?.match(ImprovedTube.regex.video_id)?.[1] === video).parentNode?.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url?.match(ImprovedTube.regex.channel)?.groups?.name : null),
+ blockedElement = node.blockedElement,
+
+ // Yes, this is horrible. Cant find better way of extracting title :(
+ title = this.parentNode?.__dataHost?.__data?.data?.title?.runs?.[0]?.text
+ || this.parentNode.__dataHost?.__data?.data?.title?.simpleText
+ || this.parentNode.__dataHost?.__data?.videoPreviewData?.accessibilityText
+ || this.parentNode.blockedElement?.querySelector('[title]')?.title;
+ let added = false,
+ type = 'video';
+
+ if (!video || !blockedElement || !title) {
+ console.error('blocklist: need video ID, blockedElement and title');
return;
}
- button = document.createElement('button');
- button.className = 'it-add-channel-to-blocklist';
-
- if (this.storage.blocklist.channels[id]) {
- button.innerText = 'Remove from blocklist';
- button.added = false;
+ // this button can perform three functions:
+ if (channel && blockedElement.classList.contains('it-blocklisted-channel')) {
+ // unblocking whole channel
+ type = 'channel';
+ } else if (blockedElement.classList.contains('it-blocklisted-video')) {
+ // unblocking blocklisted video
} else {
- button.innerText = 'Add to blocklist';
- button.added = true;
+ // block this video
+ added = true;
}
- button.addEventListener('click', function (event) {
- const data = ytInitialData.metadata.channelMetadataRenderer,
- //let data = this.parentNode.__dataHost.__data.data,
- id = location.href.match(ImprovedTube.regex.channel).groups.name;
-
- if (this.added) { // adding
- ImprovedTube.storage.blocklist.channels[id] = {title: data.title,
- preview: data.avatar.thumbnails[0].url};
- button.innerText = 'Remove from blocklist';
- } else { // removing
- delete ImprovedTube.storage.blocklist.channels[id];
- button.innerText = 'Add to blocklist';
- }
- ImprovedTube.messages.send({action: 'blocklist',
- added: this.added,
- type: 'channel',
- id: id,
- title: data.title,
- preview: data.avatar.thumbnails[0].url});
- this.added = !this.added;
-
- event.preventDefault();
- event.stopPropagation();
- }, true);
-
- node.parentNode.parentNode.appendChild(button);
- this.elements.blocklist_buttons.push(button);
- } else if (arguments.length == 0) {
- // scan whole page
- for (let thumbnails of document.querySelectorAll('a.ytd-thumbnail[href]')) {
- this.blocklist('video', thumbnails);
- }
- if (document.querySelector('YT-SUBSCRIBE-BUTTON-VIEW-MODEL')) {
- this.blocklist('channel', document.querySelector('YT-SUBSCRIBE-BUTTON-VIEW-MODEL'));
- }
+ // this message will trigger 'storage-changed' event and eventually blocklistInit() full rescan
+ ImprovedTube.messages.send({action: 'blocklist',
+ added: added,
+ type: type,
+ id: type == 'channel' ? channel : video,
+ title: title,
+ when: Date.parse(new Date().toDateString()) / 100000
+ });
+ }
+ });
+
+ node.appendChild(button);
+ this.elements.blocklist_buttons.push(button);
+};
+
+ImprovedTube.blocklistChannel = function (node) {
+ if (!this.storage.blocklist_activate || !node) return;
+
+ const id = location.pathname.match(ImprovedTube.regex.channel)?.groups?.name;
+ let button = node.parentNode?.parentNode?.querySelector("button.it-add-channel-to-blocklist");
+
+ if (!id) return; // not on channel page
+
+ // skip button.it-add-channel-to-blocklist creation if one already exists, adjust text only
+ if (button) {
+ button.innerText = this.storage.blocklist.channels[id] ? 'Remove from blocklist' : 'Add to blocklist';
+ return;
+ }
+
+ button = document.createElement('button');
+
+ button.className = 'it-add-channel-to-blocklist';
+ button.innerText = this.storage.blocklist.channels[id] ? 'Remove from blocklist' : 'Add to blocklist';
+ button.onclick = function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // no longer working:
+ //data = ytInitialData?.metadata?.channelMetadataRenderer,
+ //data = this.parentNode.__dataHost.__data.data,
+ //avatar= data?.avatar?.thumbnails[0]?.url
+ const id = location.pathname.match(ImprovedTube.regex.channel)?.groups?.name,
+ title = document.querySelector('yt-dynamic-text-view-model .yt-core-attributed-string')?.innerText,
+ preview = document.querySelector('yt-decorated-avatar-view-model img')?.src;
+ let added = false;
+
+ if (!id || !title) {
+ console.error('blocklist click: no channel ID or metadata');
+ return;
+ }
+
+ // this message will trigger 'storage-changed' event and eventually blocklistInit() full rescan
+ ImprovedTube.messages.send({action: 'blocklist',
+ added: !ImprovedTube.storage.blocklist.channels[id],
+ type: 'channel',
+ id: id,
+ title: title,
+ preview: preview,
+ when: Date.parse(new Date().toDateString()) / 100000
+ });
+ };
+
+ node.parentNode.parentNode.appendChild(button);
+ this.elements.blocklist_buttons.push(button);
+};
+
+ImprovedTube.blocklistInit = function () {
+ if (this.storage.blocklist_activate) {
+ // initialize and (re)scan whole page. Called on load after 'storage-loaded'
+ // and blocklist 'storage-changed' event (adding/removing blocks)
+ if (!this.storage.blocklist || typeof this.storage.blocklist !== 'object') {
+ this.storage.blocklist = {videos: {}, channels: {}};
+ }
+ if (!this.storage.blocklist.videos || typeof this.storage.blocklist.channels !== 'object') {
+ this.storage.blocklist.videos = {};
+ }
+ if (!this.storage.blocklist.channels || typeof this.storage.blocklist.channels !== 'object') {
+ this.storage.blocklist.channels = {};
+ }
+ for (const thumbnail of document.querySelectorAll('a.ytd-thumbnail[href], a.ytd-video-preview')) {
+ this.blocklistNode(thumbnail);
+ }
+ if (document.querySelector('YT-SUBSCRIBE-BUTTON-VIEW-MODEL')) {
+ this.blocklistChannel(document.querySelector('YT-SUBSCRIBE-BUTTON-VIEW-MODEL'));
}
} else {
- // remove blocklist buttons
+ // Disable and unload Blocklist
+ // remove all 'it-add-to-blocklist' buttons
for (let blocked of this.elements.blocklist_buttons) {
blocked.remove();
}
@@ -206,40 +175,85 @@ ImprovedTube.blocklist = function (type, node) {
// release observer
ImprovedTube.blocklistObserver.disconnect();
}
- // remove all blocks from videos\channels
- for (let blocked of document.querySelectorAll('.it-blocklisted-video, .it-blocklisted-channel')) {
+ // remove all video/channel blocks from thumbnails on current page
+ for (let blocked of document.querySelectorAll('.it-blocklisted-video')) {
blocked.classList.remove('it-blocklisted-video');
+ }
+ for (let blocked of document.querySelectorAll('.it-blocklisted-channel')) {
blocked.classList.remove('it-blocklisted-channel');
}
}
};
ImprovedTube.blocklistObserver = new MutationObserver(function (mutationList) {
- for (var mutation of mutationList) {
- const video = mutation.target.href.match(ImprovedTube.regex.video_id)?.[1],
- channel = mutation.target.parentNode.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url ? mutation.target.parentNode.__dataHost.__data.data.shortBylineText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url.match(ImprovedTube.regex.channel).groups.name : undefined,
- blockedElement = mutation.target.blockedElement;
+ for (const mutation of mutationList) {
+ const video = mutation.target.href?.match(ImprovedTube.regex.video_id)?.[1],
+ channel = mutation.target.parentNode?.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url?.match(ImprovedTube.regex.channel)?.groups?.name
+ // video-preview doesnt have Channel info, extract from source thumbnail
+ || ((video && mutation.target?.classList.contains('ytd-video-preview')) ? ImprovedTube.elements.observerList.find(a => a.id == 'thumbnail' && a.href?.match(ImprovedTube.regex.video_id)?.[1] === video).parentNode?.__dataHost?.__data?.data?.shortBylineText?.runs?.[0]?.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url?.match(ImprovedTube.regex.channel)?.groups?.name : null),
+ blockedElement = ImprovedTube.blockedElementTypeHelper(mutation.target);
- if (!video || !blockedElement) return; // need both video ID and blockedElement, otherwise bail
+ if (!blockedElement) return; // unknown thumbnail cell type, bail out
+ mutation.target.blockedElement = blockedElement;
- if (ImprovedTube.storage.blocklist.videos[video]) {
- if (!blockedElement.classList.contains('it-blocklisted-video')) {
+ if (!video) {
+ // no video ID means monitored thumbnail/video-preview node went inactive
+ blockedElement.classList.remove('it-blocklisted-video');
+ blockedElement.classList.remove('it-blocklisted-channel');
+ return;
+ }
+
+ if (ImprovedTube.storage.blocklist) {
+ if (ImprovedTube.storage.blocklist.videos && ImprovedTube.storage.blocklist.videos[video]) {
blockedElement.classList.add('it-blocklisted-video');
- }
- } else {
- if (blockedElement.classList.contains('it-blocklisted-video')) {
+ } else {
blockedElement.classList.remove('it-blocklisted-video');
}
+ if (ImprovedTube.storage.blocklist.channels && channel && ImprovedTube.storage.blocklist.channels[channel]) {
+ blockedElement.classList.add('it-blocklisted-channel');
+ } else {
+ blockedElement.classList.remove('it-blocklisted-channel');
+ }
}
+ }
+});
+ImprovedTube.blockedElementTypeHelper = function (node) {
+ switch(node.parentNode.className.replace('style-scope ','')) {
+ case 'ytd-compact-video-renderer':
+ // list next to player
+ // node.parentNode.__dataHost.$.dismissible;
+ case 'ytd-rich-item-renderer':
+ // short reel
+ case 'ytd-rich-grid-media':
+ // grid reel
+ case 'ytd-rich-grid-slim-media':
+ // short grid reel
+ case 'ytd-playlist-video-renderer':
+ // playlist page
+ case 'ytd-playlist-panel-video-renderer':
+ // playlist next to player
+ // node.parentNode.closest('ytd-playlist-panel-video-renderer')
+ case 'ytd-structured-description-video-lockup-renderer':
+ // list under the player
+ // node.parentNode.closest('ytd-structured-description-video-lockup-renderer')
+ // or even node.parentNode.closest('ytd-compact-infocard-renderer') === node.parentNode.parentNode.parentNode.parentNode
+ case 'ytd-video-renderer':
+ // search results
+ case 'ytd-video-preview':
+ // subscriptions/search thumbnail video-preview
+ return node.parentNode.parentNode.parentNode;
+ break;
- if (channel && ImprovedTube.storage.blocklist.channels[channel] && !blockedElement.classList.contains('it-blocklisted-channel')) {
- // blocked channel? = block all videos from that channel
- blockedElement.classList.add('it-blocklisted-channel');
- } else if ((!channel || !ImprovedTube.storage.blocklist.channels[channel]) && blockedElement.classList.contains('it-blocklisted-channel')) {
- // channel not blocked, show it
- blockedElement.classList.remove('it-blocklisted-channel');
- }
+ case 'ytd-grid-video-renderer':
+ // channel home screen grid
+ case 'ytd-reel-item-renderer':
+ // reel
+ return node.parentNode.parentNode;
+ break;
+ default:
+ // unknown ones land here
+ break;
}
-});
+};