diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 686f493a0..fcc9cc86e 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -672,9 +672,18 @@ "Hide_YouTube_Logo": { "message": "Hide YouTube Logo" }, + "hideAIContent": { + "message": "Hide AI-labeled videos" + }, "hideAISummary": { "message": "Hide AI Summaries" }, + "deleteAIContent": { + "message": "Clear remembered AI videos" + }, + "thisWillRemoveAllAIContent": { + "message": "This will clear all remembered AI-labeled video IDs. Videos may reappear until they are identified again." + }, "hideAnimatedThumbnails": { "message": "Hide animated thumbnails" }, diff --git a/build/manifest2.json b/build/manifest2.json index 677284a29..d0399b973 100644 --- a/build/manifest2.json +++ b/build/manifest2.json @@ -77,6 +77,7 @@ "js&css/web-accessible/www.youtube.com/channel.js", "js&css/web-accessible/www.youtube.com/shortcuts.js", "js&css/web-accessible/www.youtube.com/blocklist.js", + "js&css/web-accessible/www.youtube.com/ai-content.js", "js&css/web-accessible/www.youtube.com/settings.js", "js&css/web-accessible/init.js" ] diff --git a/build/manifest3Firefox.json b/build/manifest3Firefox.json index 58f47b2b3..2c033d9cb 100644 --- a/build/manifest3Firefox.json +++ b/build/manifest3Firefox.json @@ -80,6 +80,7 @@ "js&css/web-accessible/www.youtube.com/channel.js", "js&css/web-accessible/www.youtube.com/shortcuts.js", "js&css/web-accessible/www.youtube.com/blocklist.js", + "js&css/web-accessible/www.youtube.com/ai-content.js", "js&css/web-accessible/www.youtube.com/settings.js", "js&css/web-accessible/init.js", "menu/icons/48.png" diff --git a/js&css/extension/init.js b/js&css/extension/init.js index 9f5e272e1..a3ad0ee38 100644 --- a/js&css/extension/init.js +++ b/js&css/extension/init.js @@ -85,6 +85,7 @@ const pageWorldFiles = [ '/js&css/web-accessible/www.youtube.com/channel.js', '/js&css/web-accessible/www.youtube.com/shortcuts.js', '/js&css/web-accessible/www.youtube.com/blocklist.js', + '/js&css/web-accessible/www.youtube.com/ai-content.js', '/js&css/web-accessible/www.youtube.com/settings.js', '/js&css/web-accessible/www.youtube.com/last-watched-overlay.js', // Neue Zeile hinzufügen '/js&css/web-accessible/www.youtube.com/return-youtube-dislike.js', @@ -272,6 +273,25 @@ document.addEventListener('it-message-from-youtube', function () { chrome.storage.local.set({ watched: extension.storage.data.watched }); + } else if (message.action === 'ai_content') { + if (!extension.storage.data.ai_content || typeof extension.storage.data.ai_content !== 'object') { + extension.storage.data.ai_content = {}; + } + + if (message.type === 'add' && message.id) { + extension.storage.data.ai_content[message.id] = { + title: message.title, + when: message.when + }; + } + + if (message.type === 'remove' && message.id) { + delete extension.storage.data.ai_content[message.id]; + } + + chrome.storage.local.set({ + ai_content: extension.storage.data.ai_content + }); } else if (message.action === 'set') { if (message.value) { chrome.storage.local.set({ [message.key]: message.value }); diff --git a/js&css/extension/www.youtube.com/appearance/comments/comments.css b/js&css/extension/www.youtube.com/appearance/comments/comments.css index 8ae229247..c71bbe199 100644 --- a/js&css/extension/www.youtube.com/appearance/comments/comments.css +++ b/js&css/extension/www.youtube.com/appearance/comments/comments.css @@ -143,7 +143,9 @@ html[data-theme='black'][it-comments='collapsed'][data-page-type=video] ytd-comm html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #primary, html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #secondary{margin-top: 0 !important; padding-top: 0 !important; padding-right: 0; flex-grow: 1;} html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #secondary::before {content: ""; height: 24px; display: block;} - html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #primary {flex: 1 0 auto;padding: 0 24px; margin: 0;} + /* Allow primary to shrink so #secondary is not pushed off-screen (playlist/chat). */ + html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #primary {flex: 1 1 auto; min-width: 0; padding: 0 24px; margin: 0;} + html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #secondary {flex: 0 0 auto; min-width: 300px !important;} html[data-page-type=video][it-comments-sidebar='true'][it-player-size="do_not_change"] ytd-watch-flexy #primary-inner {padding-top: 24px;} html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #player {margin: 0 auto;} @@ -174,7 +176,7 @@ html[data-theme='black'][it-comments='collapsed'][data-page-type=video] ytd-comm overflow-x: hidden !important; } html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy:not([theater]):not([fullscreen]) #columns {overflow: hidden;} - html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy[theater] #columns + html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy[theater] #columns, html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy[fullscreen] #columns { height: auto; } @@ -190,6 +192,11 @@ html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy #donation min-width: unset !important; } +/* Keep live chat from consuming the entire secondary column (comments stay reachable). */ +html[data-page-type=video][it-comments-sidebar='true'] ytd-watch-flexy:not([theater]):not([fullscreen]) ytd-live-chat-frame#chat { + max-height: min(40vh, 480px) !important; +} + /* GRID RELATED */ html[data-page-type=video][it-comments-sidebar='true'] #items.ytd-watch-next-secondary-results-renderer ytd-compact-video-renderer, html[data-page-type=video][it-comments-sidebar='true'] #items.ytd-watch-next-secondary-results-renderer ytd-compact-radio-renderer, diff --git a/js&css/extension/www.youtube.com/general/general.css b/js&css/extension/www.youtube.com/general/general.css index 3a3fef248..4d781edc0 100644 --- a/js&css/extension/www.youtube.com/general/general.css +++ b/js&css/extension/www.youtube.com/general/general.css @@ -114,6 +114,20 @@ html[it-hide-thumbnail-icon='true'] :is(ytd-video-renderer,ytd-promoted-video-re # HIDE AI SUMMARY ------------------------------------------------------------------------------*/ html[it-hide-ai-summary='true'] ytd-expandable-metadata-renderer[has-video-summary], +/*------------------------------------------------------------------------------ +# HIDE AI-LABELED VIDEOS (remembered IDs + visible badges) +------------------------------------------------------------------------------*/ +html[it-hide-ai-content='true'] .it-ai-content, +html[it-hide-ai-content='true'] ytd-rich-item-renderer:has([aria-label*="Made with AI" i]), +html[it-hide-ai-content='true'] ytd-rich-item-renderer:has([aria-label*="Altered or synthetic" i]), +html[it-hide-ai-content='true'] ytd-video-renderer:has([aria-label*="Made with AI" i]), +html[it-hide-ai-content='true'] ytd-video-renderer:has([aria-label*="Altered or synthetic" i]), +html[it-hide-ai-content='true'] ytd-compact-video-renderer:has([aria-label*="Made with AI" i]), +html[it-hide-ai-content='true'] ytd-compact-video-renderer:has([aria-label*="Altered or synthetic" i]), +html[it-hide-ai-content='true'] ytd-grid-video-renderer:has([aria-label*="Made with AI" i]), +html[it-hide-ai-content='true'] yt-lockup-view-model:has([aria-label*="Made with AI" i]), +html[it-hide-ai-content='true'] yt-lockup-view-model:has([aria-label*="Altered or synthetic" i]), +html[it-hide-ai-content='true'] ytd-reel-item-renderer:has([aria-label*="Made with AI" i]), /*-------------------------------------------------------------- # HIDE DOTS ON THUMBNAILS --------------------------------------------------------------*/ diff --git a/js&css/extension/www.youtube.com/styles.css b/js&css/extension/www.youtube.com/styles.css index 2a0164339..8100ae252 100644 --- a/js&css/extension/www.youtube.com/styles.css +++ b/js&css/extension/www.youtube.com/styles.css @@ -2269,42 +2269,144 @@ html:is([it-thumbnail-size="large"], [it-thumbnail-size="medium"], [it-thumbnail } /* Thumbnail sizes: Search Results & History Lists */ -html[it-thumbnail-size="large"] ytd-video-renderer ytd-thumbnail { +html[it-thumbnail-size="large"] ytd-video-renderer ytd-thumbnail, +html[it-thumbnail-size="large"] ytd-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="large"] ytd-video-renderer #thumbnail, +html[it-thumbnail-size="large"] ytd-playlist-renderer ytd-playlist-thumbnail, +html[it-thumbnail-size="large"] ytd-playlist-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="large"] ytd-item-section-renderer yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="large"] ytd-item-section-renderer yt-lockup-view-model a.ytLockupViewModelContentImage { + width: 360px !important; max-width: 420px !important; - min-width: 360px !important; + min-width: 0 !important; } -html[it-thumbnail-size="medium"] ytd-video-renderer ytd-thumbnail { +html[it-thumbnail-size="medium"] ytd-video-renderer ytd-thumbnail, +html[it-thumbnail-size="medium"] ytd-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="medium"] ytd-video-renderer #thumbnail, +html[it-thumbnail-size="medium"] ytd-playlist-renderer ytd-playlist-thumbnail, +html[it-thumbnail-size="medium"] ytd-playlist-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="medium"] ytd-item-section-renderer yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="medium"] ytd-item-section-renderer yt-lockup-view-model a.ytLockupViewModelContentImage { + width: 280px !important; max-width: 320px !important; - min-width: 280px !important; + min-width: 0 !important; } -html[it-thumbnail-size="small"] ytd-video-renderer ytd-thumbnail { +html[it-thumbnail-size="small"] ytd-video-renderer ytd-thumbnail, +html[it-thumbnail-size="small"] ytd-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="small"] ytd-video-renderer #thumbnail, +html[it-thumbnail-size="small"] ytd-playlist-renderer ytd-playlist-thumbnail, +html[it-thumbnail-size="small"] ytd-playlist-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="small"] ytd-item-section-renderer yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="small"] ytd-item-section-renderer yt-lockup-view-model a.ytLockupViewModelContentImage { + width: 240px !important; max-width: 260px !important; - min-width: 240px !important; + min-width: 0 !important; } -html[it-thumbnail-size="x-small"] ytd-video-renderer ytd-thumbnail { +html[it-thumbnail-size="x-small"] ytd-video-renderer ytd-thumbnail, +html[it-thumbnail-size="x-small"] ytd-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="x-small"] ytd-video-renderer #thumbnail, +html[it-thumbnail-size="x-small"] ytd-playlist-renderer ytd-playlist-thumbnail, +html[it-thumbnail-size="x-small"] ytd-playlist-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="x-small"] ytd-item-section-renderer yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="x-small"] ytd-item-section-renderer yt-lockup-view-model a.ytLockupViewModelContentImage { + width: 160px !important; max-width: 180px !important; - min-width: 160px !important; + min-width: 0 !important; } -html[it-thumbnail-size="xx-small"] ytd-video-renderer ytd-thumbnail { +html[it-thumbnail-size="xx-small"] ytd-video-renderer ytd-thumbnail, +html[it-thumbnail-size="xx-small"] ytd-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="xx-small"] ytd-video-renderer #thumbnail, +html[it-thumbnail-size="xx-small"] ytd-playlist-renderer ytd-playlist-thumbnail, +html[it-thumbnail-size="xx-small"] ytd-playlist-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="xx-small"] ytd-item-section-renderer yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="xx-small"] ytd-item-section-renderer yt-lockup-view-model a.ytLockupViewModelContentImage { + width: 120px !important; max-width: 140px !important; - min-width: 120px !important; + min-width: 0 !important; +} + +/* Thumbnail sizes: Playlist page video list */ +html[it-thumbnail-size="large"] ytd-playlist-video-renderer ytd-thumbnail, +html[it-thumbnail-size="large"] ytd-playlist-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="large"] ytd-playlist-video-renderer #thumbnail, +html[it-thumbnail-size="large"] ytd-playlist-panel-video-renderer ytd-thumbnail, +html[it-thumbnail-size="large"] ytd-playlist-panel-video-renderer yt-thumbnail-view-model { + width: 200px !important; + max-width: 200px !important; + min-width: 0 !important; +} +html[it-thumbnail-size="medium"] ytd-playlist-video-renderer ytd-thumbnail, +html[it-thumbnail-size="medium"] ytd-playlist-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="medium"] ytd-playlist-video-renderer #thumbnail, +html[it-thumbnail-size="medium"] ytd-playlist-panel-video-renderer ytd-thumbnail, +html[it-thumbnail-size="medium"] ytd-playlist-panel-video-renderer yt-thumbnail-view-model { + width: 160px !important; + max-width: 160px !important; + min-width: 0 !important; +} +html[it-thumbnail-size="small"] ytd-playlist-video-renderer ytd-thumbnail, +html[it-thumbnail-size="small"] ytd-playlist-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="small"] ytd-playlist-video-renderer #thumbnail, +html[it-thumbnail-size="small"] ytd-playlist-panel-video-renderer ytd-thumbnail, +html[it-thumbnail-size="small"] ytd-playlist-panel-video-renderer yt-thumbnail-view-model { + width: 120px !important; + max-width: 120px !important; + min-width: 0 !important; +} +html[it-thumbnail-size="x-small"] ytd-playlist-video-renderer ytd-thumbnail, +html[it-thumbnail-size="x-small"] ytd-playlist-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="x-small"] ytd-playlist-video-renderer #thumbnail, +html[it-thumbnail-size="x-small"] ytd-playlist-panel-video-renderer ytd-thumbnail, +html[it-thumbnail-size="x-small"] ytd-playlist-panel-video-renderer yt-thumbnail-view-model { + width: 90px !important; + max-width: 90px !important; + min-width: 0 !important; +} +html[it-thumbnail-size="xx-small"] ytd-playlist-video-renderer ytd-thumbnail, +html[it-thumbnail-size="xx-small"] ytd-playlist-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="xx-small"] ytd-playlist-video-renderer #thumbnail, +html[it-thumbnail-size="xx-small"] ytd-playlist-panel-video-renderer ytd-thumbnail, +html[it-thumbnail-size="xx-small"] ytd-playlist-panel-video-renderer yt-thumbnail-view-model { + width: 70px !important; + max-width: 70px !important; + min-width: 0 !important; } /* Thumbnail sizes: Video Player Sidebar (Related Videos) */ -html[it-thumbnail-size="large"] ytd-compact-video-renderer ytd-thumbnail { +html[it-thumbnail-size="large"] ytd-compact-video-renderer ytd-thumbnail, +html[it-thumbnail-size="large"] ytd-compact-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="large"] #related yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="large"] #related yt-lockup-view-model a.ytLockupViewModelContentImage { width: 200px !important; + max-width: 200px !important; } -html[it-thumbnail-size="medium"] ytd-compact-video-renderer ytd-thumbnail { +html[it-thumbnail-size="medium"] ytd-compact-video-renderer ytd-thumbnail, +html[it-thumbnail-size="medium"] ytd-compact-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="medium"] #related yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="medium"] #related yt-lockup-view-model a.ytLockupViewModelContentImage { width: 160px !important; + max-width: 160px !important; } -html[it-thumbnail-size="small"] ytd-compact-video-renderer ytd-thumbnail { +html[it-thumbnail-size="small"] ytd-compact-video-renderer ytd-thumbnail, +html[it-thumbnail-size="small"] ytd-compact-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="small"] #related yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="small"] #related yt-lockup-view-model a.ytLockupViewModelContentImage { width: 120px !important; + max-width: 120px !important; } -html[it-thumbnail-size="x-small"] ytd-compact-video-renderer ytd-thumbnail { +html[it-thumbnail-size="x-small"] ytd-compact-video-renderer ytd-thumbnail, +html[it-thumbnail-size="x-small"] ytd-compact-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="x-small"] #related yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="x-small"] #related yt-lockup-view-model a.ytLockupViewModelContentImage { width: 90px !important; + max-width: 90px !important; } -html[it-thumbnail-size="xx-small"] ytd-compact-video-renderer ytd-thumbnail { +html[it-thumbnail-size="xx-small"] ytd-compact-video-renderer ytd-thumbnail, +html[it-thumbnail-size="xx-small"] ytd-compact-video-renderer yt-thumbnail-view-model, +html[it-thumbnail-size="xx-small"] #related yt-lockup-view-model .yt-lockup-view-model-wiz__content-image, +html[it-thumbnail-size="xx-small"] #related yt-lockup-view-model a.ytLockupViewModelContentImage { width: 70px !important; + max-width: 70px !important; } /*-------------------------------------------------------------- diff --git a/js&css/web-accessible/core.js b/js&css/web-accessible/core.js index 481d8405b..a036b0ef1 100644 --- a/js&css/web-accessible/core.js +++ b/js&css/web-accessible/core.js @@ -208,6 +208,9 @@ document.addEventListener('it-message-from-extension', function () { ImprovedTube.init(); ImprovedTube.blocklistInit(); + if (typeof ImprovedTube.aiContentInit === 'function') { + ImprovedTube.aiContentInit(); + } /*-------------------------------------------------------------- # Immediate reaction to any change of our extension storage (settings) @@ -243,6 +246,13 @@ document.addEventListener('it-message-from-extension', function () { ImprovedTube.blocklistInit(); break + case 'aiContent': + case 'hideAiContent': + if (typeof ImprovedTube.aiContentInit === 'function') { + ImprovedTube.aiContentInit(); + } + break + case 'playerPlaybackSpeed': case 'playerForcedPlaybackSpeed': if (ImprovedTube.storage.player_forced_playback_speed === true && isFinite(Number(ImprovedTube.storage.player_playback_speed))) { diff --git a/js&css/web-accessible/functions.js b/js&css/web-accessible/functions.js index fe715f8f8..7ddeaf85e 100644 --- a/js&css/web-accessible/functions.js +++ b/js&css/web-accessible/functions.js @@ -47,6 +47,11 @@ ImprovedTube.ytElementsHandler = function (node) { this.blocklistNode(node); } } + if (this.storage.hide_ai_content) { + if ((node.href && node.classList.contains('ytd-thumbnail')) || node.classList.contains('ytd-video-preview')) { + this.aiContentNode(node); + } + } } 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") @@ -380,7 +385,10 @@ ImprovedTube.videoPageUpdate = function () { ImprovedTube.playerCinemaModeButton(); ImprovedTube.playerHamburgerButton(); ImprovedTube.playerControls(); - + if (typeof ImprovedTube.detectAiContent === 'function') { + ImprovedTube.detectAiContent(); + } + // Initialize large playlist handler for playlist videos if (this.getParam(location.href, 'list')) { ImprovedTube.playlistLargePlaylistHandler(); diff --git a/js&css/web-accessible/init.js b/js&css/web-accessible/init.js index 9f763d5e7..15ed6740c 100644 --- a/js&css/web-accessible/init.js +++ b/js&css/web-accessible/init.js @@ -209,6 +209,12 @@ ImprovedTube.init = function () { ImprovedTube.stop_shorts_autoloop(); } ImprovedTube.shortsAutoScroll(); + if (typeof ImprovedTube.detectAiContent === 'function') { + ImprovedTube.detectAiContent(); + } + } + if (typeof ImprovedTube.aiContentInit === 'function') { + ImprovedTube.aiContentInit(); } if (window.matchMedia) { document.documentElement.dataset.systemColorScheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; @@ -273,6 +279,10 @@ document.addEventListener('yt-navigate-finish', function () { } catch (e) { console.error('[RYD] error', e); } } + if (typeof ImprovedTube.aiContentInit === 'function') { + ImprovedTube.aiContentInit(); + } + if (ImprovedTube.elements.player && ImprovedTube.elements.player.setPlaybackRate) { ImprovedTube.videoPageUpdate(); ImprovedTube.initPlayer(); diff --git a/js&css/web-accessible/www.youtube.com/ai-content.js b/js&css/web-accessible/www.youtube.com/ai-content.js new file mode 100644 index 000000000..f7fd00248 --- /dev/null +++ b/js&css/web-accessible/www.youtube.com/ai-content.js @@ -0,0 +1,332 @@ +/*------------------------------------------------------------------------------ +HIDE AI-LABELED CONTENT +Detect YouTube AI disclosures on watch/Shorts, remember video IDs, mark feeds. +------------------------------------------------------------------------------*/ + +ImprovedTube.aiContentLabelPattern = /made with ai|altered or synthetic|ai-generated|generated with ai|generative ai content|synthetic media/i; + +ImprovedTube.aiContentExactLabelPattern = /^(ai|made with ai)$/i; + +ImprovedTube.getCurrentVideoId = function () { + const href = location.href; + + if (location.pathname.startsWith('/shorts/')) { + return location.pathname.split('/')[2]?.slice(0, 11) || null; + } + + return href.match(ImprovedTube.regex.video_id)?.[1] + || this.getParam(new URL(href).search.substr(1), 'v') + || null; +}; + +ImprovedTube.isAiLabeledDom = function (root) { + if (!root) { + return false; + } + + const labeled = root.querySelectorAll('[aria-label], [title]'); + + for (let i = 0, l = labeled.length; i < l; i++) { + const text = (labeled[i].getAttribute('aria-label') || labeled[i].getAttribute('title') || '').trim(); + + // Prefer explicit disclosure wording; bare "AI" only on compact badge-like controls + if (ImprovedTube.aiContentLabelPattern.test(text)) { + return true; + } + + if (ImprovedTube.aiContentExactLabelPattern.test(text)) { + const tag = labeled[i].tagName; + + if (tag === 'BUTTON' || tag === 'A' || labeled[i].closest('badge-shape, button, a, yt-button-shape')) { + return true; + } + } + } + + const candidates = root.querySelectorAll('badge-shape, button, a, yt-formatted-string'); + + for (let i = 0, l = candidates.length; i < l; i++) { + const el = candidates[i]; + const text = (el.textContent || '').replace(/\s+/g, ' ').trim(); + + if (!text || text.length > 64) { + continue; + } + + if (ImprovedTube.aiContentLabelPattern.test(text)) { + return true; + } + + // Bare "AI" label under the player / Shorts overlay only + if (ImprovedTube.aiContentExactLabelPattern.test(text) + && el.closest('ytd-watch-metadata, #below, #meta, ytd-reel-player-overlay-renderer, ytd-reel-video-renderer')) { + return true; + } + } + + return false; +}; + +ImprovedTube.isAiLabeledPlayerResponse = function () { + try { + const currentId = ImprovedTube.getCurrentVideoId(); + const flexy = document.querySelector('ytd-watch-flexy'); + let payload = flexy?.playerData || flexy?.data?.playerResponse; + + // ytInitialPlayerResponse is only reliable on the first load of a watch page + if (!payload && window.ytInitialPlayerResponse) { + const initialId = window.ytInitialPlayerResponse?.videoDetails?.videoId; + + if (!currentId || initialId === currentId) { + payload = window.ytInitialPlayerResponse; + } + } + + if (!payload && window.ytplayer?.config?.args?.player_response) { + payload = window.ytplayer.config.args.player_response; + } + + if (typeof payload === 'string') { + payload = JSON.parse(payload); + } + + if (!payload || typeof payload !== 'object') { + return false; + } + + const responseId = payload.videoDetails?.videoId; + + if (currentId && responseId && responseId !== currentId) { + return false; + } + + const haystack = JSON.stringify(payload); + + return /generativeAi|generatedWithAi|aiGenerated|syntheticContent|alteredOrSynthetic|madeWithAi|made with AI|Altered or synthetic/i.test(haystack); + } catch (error) { + return false; + } +}; + +ImprovedTube.isAiLabeledPage = function () { + if (ImprovedTube.isAiLabeledPlayerResponse()) { + return true; + } + + const roots = [ + document.querySelector('ytd-watch-metadata'), + document.querySelector('#below'), + document.querySelector('#meta'), + document.querySelector('#info-container'), + document.querySelector('ytd-reel-player-overlay-renderer'), + document.querySelector('ytd-reel-video-renderer') + ]; + + for (let i = 0, l = roots.length; i < l; i++) { + if (ImprovedTube.isAiLabeledDom(roots[i])) { + return true; + } + } + + return false; +}; + +ImprovedTube.rememberAiContent = function (videoId, title) { + if (!videoId || !ImprovedTube.storage.hide_ai_content) { + return; + } + + if (!ImprovedTube.storage.ai_content || typeof ImprovedTube.storage.ai_content !== 'object') { + ImprovedTube.storage.ai_content = {}; + } + + if (ImprovedTube.storage.ai_content[videoId]) { + return; + } + + ImprovedTube.storage.ai_content[videoId] = { + title: title || document.title, + when: Date.parse(new Date().toDateString()) / 100000 + }; + + ImprovedTube.messages.send({ + action: 'ai_content', + type: 'add', + id: videoId, + title: ImprovedTube.storage.ai_content[videoId].title, + when: ImprovedTube.storage.ai_content[videoId].when + }); +}; + +ImprovedTube.detectAiContent = function () { + if (!ImprovedTube.storage.hide_ai_content) { + return; + } + + const pageType = document.documentElement.dataset.pageType; + const onWatch = pageType === 'video' || location.pathname === '/watch'; + const onShorts = pageType === 'shorts' || location.pathname.startsWith('/shorts/'); + + if (!onWatch && !onShorts) { + return; + } + + const videoId = ImprovedTube.getCurrentVideoId(); + + if (!videoId) { + return; + } + + if (ImprovedTube.storage.ai_content?.[videoId]) { + return; + } + + const markIfLabeled = function () { + const id = ImprovedTube.getCurrentVideoId(); + + if (!id || !ImprovedTube.storage.hide_ai_content) { + return false; + } + + if (ImprovedTube.storage.ai_content?.[id]) { + return true; + } + + if (ImprovedTube.isAiLabeledPage()) { + ImprovedTube.rememberAiContent(id, document.title); + ImprovedTube.aiContentMarkFeeds(); + if (ImprovedTube.aiContentDetectObserver) { + ImprovedTube.aiContentDetectObserver.disconnect(); + } + return true; + } + + return false; + }; + + if (markIfLabeled()) { + return; + } + + // Label can appear after metadata loads + if (!ImprovedTube.aiContentDetectObserver) { + ImprovedTube.aiContentDetectObserver = new MutationObserver(function () { + clearTimeout(ImprovedTube.aiContentDetectTimer); + ImprovedTube.aiContentDetectTimer = setTimeout(markIfLabeled, 300); + }); + } + + const observeRoot = document.querySelector('ytd-watch-metadata') + || document.querySelector('#below') + || document.querySelector('ytd-reel-player-overlay-renderer') + || document.querySelector('ytd-watch-flexy') + || document.querySelector('ytd-shorts') + || document.documentElement; + + ImprovedTube.aiContentDetectObserver.disconnect(); + ImprovedTube.aiContentDetectObserver.observe(observeRoot, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ['aria-label', 'title', 'hidden'] + }); +}; + +ImprovedTube.aiContentMarkFeeds = function () { + if (!this.storage.hide_ai_content) { + return; + } + + const links = document.querySelectorAll('a.ytd-thumbnail[href], a.ytd-video-preview[href], ytd-video-preview'); + + for (let i = 0, l = links.length; i < l; i++) { + this.aiContentNode(links[i]); + } +}; + +ImprovedTube.aiContentNode = function (node) { + if (!this.storage.hide_ai_content || !node) { + return; + } + + if (typeof this.blocklistElementTypeHelper !== 'function') { + return; + } + + const video = node.href?.match(ImprovedTube.regex.video_id)?.[1]; + const blockedElement = this.blocklistElementTypeHelper(node); + + if (!video || !blockedElement) { + return; + } + + if (!this.elements.aiContentObserverList) { + this.elements.aiContentObserverList = []; + } + + if (!this.elements.aiContentObserverList.includes(node)) { + this.aiContentObserver.observe(node, { + attributes: true, + attributeFilter: ['href'] + }); + this.elements.aiContentObserverList.push(node); + } + + if (ImprovedTube.storage.ai_content?.[video]) { + blockedElement.classList.add('it-ai-content'); + } else { + blockedElement.classList.remove('it-ai-content'); + } +}; + +ImprovedTube.aiContentObserver = new MutationObserver(function (mutationList) { + for (const mutation of mutationList) { + if (typeof ImprovedTube.blocklistElementTypeHelper !== 'function') { + continue; + } + + const video = mutation.target.href?.match(ImprovedTube.regex.video_id)?.[1]; + const blockedElement = ImprovedTube.blocklistElementTypeHelper(mutation.target); + + if (!blockedElement) { + continue; + } + + if (!video) { + blockedElement.classList.remove('it-ai-content'); + continue; + } + + if (ImprovedTube.storage.ai_content?.[video]) { + blockedElement.classList.add('it-ai-content'); + } else { + blockedElement.classList.remove('it-ai-content'); + } + } +}); + +ImprovedTube.aiContentInit = function () { + if (this.storage.hide_ai_content) { + if (!this.storage.ai_content || typeof this.storage.ai_content !== 'object') { + this.storage.ai_content = {}; + } + + this.aiContentMarkFeeds(); + this.detectAiContent(); + } else { + if (this.elements.aiContentObserverList) { + this.elements.aiContentObserverList = []; + this.aiContentObserver.disconnect(); + } + + if (this.aiContentDetectObserver) { + this.aiContentDetectObserver.disconnect(); + } + + clearTimeout(this.aiContentDetectTimer); + + for (const blocked of document.querySelectorAll('.it-ai-content')) { + blocked.classList.remove('it-ai-content'); + } + } +}; diff --git a/js&css/web-accessible/www.youtube.com/appearance.js b/js&css/web-accessible/www.youtube.com/appearance.js index 141a08c39..edd527a61 100644 --- a/js&css/web-accessible/www.youtube.com/appearance.js +++ b/js&css/web-accessible/www.youtube.com/appearance.js @@ -341,6 +341,7 @@ ImprovedTube.commentsSidebar = function () { if (ImprovedTube.storage.comments_s const video = document.querySelector("#player .ytp-chrome-bottom") || document.querySelector("#container .ytp-chrome-bottom"); const watchFlexy = document.querySelector("ytd-watch-flexy"); const primary = document.getElementById("primary"); + const columns = document.getElementById("columns"); // Don't set inline widths in fullscreen or theater mode - let CSS handle it if (watchFlexy && (watchFlexy.hasAttribute("fullscreen") || watchFlexy.hasAttribute("theater"))) { @@ -350,8 +351,13 @@ ImprovedTube.commentsSidebar = function () { if (ImprovedTube.storage.comments_s return; } - const width = video ? video.offsetWidth + 24 : 24; - if (width != 24 && primary && player) { + const rawWidth = video ? video.offsetWidth + 24 : 24; + // Leave room for the secondary column (comments + playlist/chat) so it is not clipped. + const secondaryMin = 300; + const gutters = 48; + const available = (columns?.clientWidth || window.innerWidth) - secondaryMin - gutters; + const width = Math.min(rawWidth, Math.max(320, available)); + if (rawWidth != 24 && primary && player) { primary.style.width = `${width}px`; player.style.width = `${width}px`; } diff --git a/manifest.json b/manifest.json index e12c03656..9ae7a74e4 100644 --- a/manifest.json +++ b/manifest.json @@ -76,6 +76,7 @@ "js&css/web-accessible/www.youtube.com/channel.js", "js&css/web-accessible/www.youtube.com/shortcuts.js", "js&css/web-accessible/www.youtube.com/blocklist.js", + "js&css/web-accessible/www.youtube.com/ai-content.js", "js&css/web-accessible/www.youtube.com/settings.js", "js&css/web-accessible/www.youtube.com/last-watched-overlay.js", "js&css/web-accessible/www.youtube.com/return-youtube-dislike.js", diff --git a/menu/skeleton-parts/general.js b/menu/skeleton-parts/general.js index 0992e4e01..da976899e 100644 --- a/menu/skeleton-parts/general.js +++ b/menu/skeleton-parts/general.js @@ -106,6 +106,46 @@ extension.skeleton.main.layers.section.general = { text: 'hideAISummary', id: 'hide-ai-summary' }, + hide_ai_content: { + component: 'switch', + text: 'hideAIContent', + id: 'hide-ai-content' + }, + delete_ai_content: { + component: 'button', + text: 'deleteAIContent', + style: { + justifyContent: 'space-between' + }, + on: { + click: { + component: 'modal', + variant: 'confirm', + content: 'thisWillRemoveAllAIContent', + buttons: { + cancel: { + component: 'button', + text: 'cancel', + on: { + click: function () { + this.modalProvider.close(); + } + } + }, + reset: { + component: 'button', + text: 'accept', + on: { + click: function () { + satus.storage.remove('ai_content'); + this.parentNode.parentNode.parentNode.close(); + } + } + } + } + } + } + }, remove_subscriptions_most_relevant: { component: 'switch', text: 'removeSubscriptionsMostRelevant',