Skip to content

Commit da4fe88

Browse files
committed
Add hide watched videos setting
1 parent cbe42b6 commit da4fe88

5 files changed

Lines changed: 167 additions & 1 deletion

File tree

_locales/en/messages.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,9 @@
789789
"hideWatchedVideos": {
790790
"message": "Hide watched videos"
791791
},
792+
"hideWatchedVideosThreshold": {
793+
"message": "Hide watched videos at"
794+
},
792795
"high": {
793796
"message": "High"
794797
},

js&css/extension/init.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ window.addEventListener('yt-navigate-finish', function () {
1111
extension.features.trackWatchedVideos();
1212
extension.features.thumbnailsQuality();
1313
extension.features.stickyNavigation();
14+
extension.features.hideWatchedVideos();
1415
extension.features.hideSponsoredVideosOnHome?.();
1516
});
1617

@@ -46,6 +47,7 @@ extension.events.on('init', function () {
4647
extension.features.disableThumbnailPlayback();
4748
extension.features.muteThumbnailPreviews();
4849
extension.features.markWatchedVideos();
50+
extension.features.hideWatchedVideos();
4951
extension.features.relatedVideos();
5052
extension.features.sidePanels();
5153
extension.features.stickyNavigation();

js&css/extension/www.youtube.com/general/general.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,115 @@ extension.features.trackWatchedVideos = function () {
703703
}
704704
};
705705

706+
/*--------------------------------------------------------------
707+
# HIDE WATCHED VIDEOS
708+
--------------------------------------------------------------*/
709+
710+
extension.features.hideWatchedVideos = function (anything) {
711+
var enabled = anything === true || extension.storage.get('hide_watched_videos') === true;
712+
var selector = [
713+
'ytd-compact-video-renderer',
714+
'ytd-rich-item-renderer',
715+
'ytd-video-renderer',
716+
'ytd-grid-video-renderer',
717+
'ytd-playlist-video-renderer',
718+
'ytd-reel-item-renderer'
719+
].join(',');
720+
721+
function getVideoId(renderer) {
722+
var link = renderer.querySelector('a[href*="/watch"], a[href*="/shorts/"]');
723+
var href = link && link.href;
724+
725+
if (!href) {
726+
return '';
727+
}
728+
729+
var queryId = extension.functions.getUrlParameter(href, 'v');
730+
731+
if (queryId) {
732+
return queryId;
733+
}
734+
735+
var shortsMatch = href.match(/\/shorts\/([^?&/]+)/);
736+
737+
return shortsMatch ? shortsMatch[1] : '';
738+
}
739+
740+
function getWatchedPercent(renderer) {
741+
if (renderer.querySelector('ytd-thumbnail-overlay-watched-status-renderer')) {
742+
return 100;
743+
}
744+
745+
var progress = renderer.querySelector('ytd-thumbnail-overlay-resume-playback-renderer #progress, #progress.ytd-thumbnail-overlay-resume-playback-renderer');
746+
747+
if (progress && progress.style && progress.style.width) {
748+
var width = parseFloat(progress.style.width);
749+
750+
if (!Number.isNaN(width)) {
751+
return width;
752+
}
753+
}
754+
755+
var videoId = getVideoId(renderer);
756+
var watched = extension.storage.get('watched');
757+
758+
if (videoId && watched && watched[videoId]) {
759+
return 100;
760+
}
761+
762+
return 0;
763+
}
764+
765+
function apply() {
766+
var threshold = parseInt(extension.storage.get('hide_watched_videos_threshold'), 10);
767+
768+
if (Number.isNaN(threshold)) {
769+
threshold = 90;
770+
}
771+
772+
threshold = Math.max(1, Math.min(100, threshold));
773+
774+
document.querySelectorAll(selector).forEach(function (renderer) {
775+
var hide = enabled && getWatchedPercent(renderer) >= threshold;
776+
777+
renderer.toggleAttribute('it-hide-watched-video', hide);
778+
renderer.style.display = hide ? 'none' : '';
779+
});
780+
}
781+
782+
if (extension.features.hideWatchedVideos.observer) {
783+
extension.features.hideWatchedVideos.observer.disconnect();
784+
extension.features.hideWatchedVideos.observer = null;
785+
}
786+
787+
apply();
788+
789+
if (enabled) {
790+
var scheduled = false;
791+
var schedule = function () {
792+
if (scheduled) {
793+
return;
794+
}
795+
796+
scheduled = true;
797+
requestAnimationFrame(function () {
798+
scheduled = false;
799+
apply();
800+
});
801+
};
802+
803+
extension.features.hideWatchedVideos.observer = new MutationObserver(schedule);
804+
extension.features.hideWatchedVideos.observer.observe(document.documentElement, {
805+
childList: true,
806+
subtree: true,
807+
attributes: true,
808+
attributeFilter: ['style']
809+
});
810+
}
811+
};
812+
813+
extension.features.hideWatchedVideosThreshold = extension.features.hideWatchedVideos;
814+
706815
/*--------------------------------------------------------------
707816
# THUMBNAILS QUALITY
708817
--------------------------------------------------------------*/

menu/skeleton-parts/general.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,27 @@ extension.skeleton.main.layers.section.general = {
468468
},
469469
hide_watched_videos: {
470470
component: 'switch',
471-
text: 'hideWatchedVideos'
471+
text: 'hideWatchedVideos',
472+
on: {
473+
click: function () {
474+
setTimeout(() => {
475+
if (satus.storage.get('hide_watched_videos')) {
476+
if (!satus.storage.get('track_watched_videos')) {
477+
this.previousSibling.click();
478+
}
479+
}
480+
}, 250);
481+
}
482+
}
483+
},
484+
hide_watched_videos_threshold: {
485+
component: 'slider',
486+
variant: 'row',
487+
text: 'hideWatchedVideosThreshold',
488+
min: 1,
489+
max: 100,
490+
step: 1,
491+
value: 90
472492
},
473493
hide_watch_later: {
474494
component: 'switch',
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
describe('Hide watched videos (#4128)', () => {
5+
let initContent;
6+
let generalContent;
7+
let menuContent;
8+
9+
beforeAll(() => {
10+
initContent = fs.readFileSync(path.join(__dirname, '../../js&css/extension/init.js'), 'utf8');
11+
generalContent = fs.readFileSync(path.join(__dirname, '../../js&css/extension/www.youtube.com/general/general.js'), 'utf8');
12+
menuContent = fs.readFileSync(path.join(__dirname, '../../menu/skeleton-parts/general.js'), 'utf8');
13+
});
14+
15+
test('initializes the hide watched videos feature', () => {
16+
expect(initContent).toContain('extension.features.hideWatchedVideos();');
17+
});
18+
19+
test('hides watched thumbnail renderers by threshold', () => {
20+
expect(generalContent).toContain('extension.features.hideWatchedVideos = function');
21+
expect(generalContent).toContain('ytd-thumbnail-overlay-watched-status-renderer');
22+
expect(generalContent).toContain('ytd-thumbnail-overlay-resume-playback-renderer #progress');
23+
expect(generalContent).toContain('hide_watched_videos_threshold');
24+
expect(generalContent).toContain('it-hide-watched-video');
25+
});
26+
27+
test('exposes a settings threshold control', () => {
28+
expect(menuContent).toContain('hide_watched_videos_threshold');
29+
expect(menuContent).toContain("component: 'slider'");
30+
expect(menuContent).toContain("text: 'hideWatchedVideosThreshold'");
31+
});
32+
});

0 commit comments

Comments
 (0)