@@ -15,6 +15,7 @@ import { findValidElement } from "../../maze-utils/src/dom";
15
15
import { addCleanupListener } from "../../maze-utils/src/cleanup" ;
16
16
import { hasAutogeneratedChapters , isVisible } from "../utils/pageUtils" ;
17
17
import { isVorapisInstalled } from "../utils/compatibility" ;
18
+ import { isOnYTTV } from "../../maze-utils/src/video" ;
18
19
19
20
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible' ;
20
21
const MIN_CHAPTER_SIZE = 0.003 ;
@@ -41,6 +42,12 @@ class PreviewBar {
41
42
categoryTooltip ?: HTMLDivElement ;
42
43
categoryTooltipContainer ?: HTMLElement ;
43
44
chapterTooltip ?: HTMLDivElement ;
45
+
46
+ // ScrubTooltips for YTTV only
47
+ categoryScrubTooltip ?: HTMLDivElement ;
48
+ categoryScrubTooltipContainer ?: HTMLElement ;
49
+ chapterScrubTooltip ?: HTMLDivElement ;
50
+
44
51
lastSmallestSegment : Record < string , {
45
52
index : number ;
46
53
segment : PreviewBarSegment ;
@@ -49,6 +56,7 @@ class PreviewBar {
49
56
parent : HTMLElement ;
50
57
onMobileYouTube : boolean ;
51
58
onInvidious : boolean ;
59
+ onYTTV : boolean ;
52
60
progressBar : HTMLElement ;
53
61
54
62
segments : PreviewBarSegment [ ] = [ ] ;
@@ -70,14 +78,19 @@ class PreviewBar {
70
78
unfilteredChapterGroups : ChapterGroup [ ] ;
71
79
chapterGroups : ChapterGroup [ ] ;
72
80
73
- constructor ( parent : HTMLElement , onMobileYouTube : boolean , onInvidious : boolean , chapterVote : ChapterVote , updateExistingChapters : ( ) => void , test = false ) {
81
+ constructor ( parent : HTMLElement , onMobileYouTube : boolean , onInvidious : boolean , onYTTV : boolean , chapterVote : ChapterVote , updateExistingChapters : ( ) => void , test = false ) {
74
82
if ( test ) return ;
75
83
this . container = document . createElement ( 'ul' ) ;
76
84
this . container . id = 'previewbar' ;
77
85
86
+ if ( onYTTV ) {
87
+ this . container . classList . add ( "sponsorblock-yttv-container" ) ;
88
+ }
89
+
78
90
this . parent = parent ;
79
91
this . onMobileYouTube = onMobileYouTube ;
80
92
this . onInvidious = onInvidious ;
93
+ this . onYTTV = onYTTV ;
81
94
this . chapterVote = chapterVote ;
82
95
this . updateExistingChapters = updateExistingChapters ;
83
96
@@ -97,26 +110,49 @@ class PreviewBar {
97
110
98
111
// Create label placeholder
99
112
this . categoryTooltip = document . createElement ( "div" ) ;
100
- this . categoryTooltip . className = "ytp-tooltip-title sponsorCategoryTooltip" ;
113
+ if ( isOnYTTV ( ) ) {
114
+ this . categoryTooltip . className = "sponsorCategoryTooltip" ;
115
+ } else {
116
+ this . categoryTooltip . className = "ytp-tooltip-title sponsorCategoryTooltip" ;
117
+ }
101
118
this . chapterTooltip = document . createElement ( "div" ) ;
102
- this . chapterTooltip . className = "ytp-tooltip-title sponsorCategoryTooltip" ;
119
+ if ( isOnYTTV ( ) ) {
120
+ this . chapterTooltip . className = "sponsorCategoryTooltip" ;
121
+ } else {
122
+ this . chapterTooltip . className = "ytp-tooltip-title sponsorCategoryTooltip" ;
123
+ }
124
+
125
+ if ( isOnYTTV ( ) ) {
126
+ this . categoryScrubTooltip = document . createElement ( "div" ) ;
127
+ this . categoryScrubTooltip . className = "sponsorCategoryTooltip" ;
128
+ this . chapterScrubTooltip = document . createElement ( "div" ) ;
129
+ this . chapterScrubTooltip . className = "sponsorCategoryTooltip" ;
130
+ }
103
131
104
- // global chaper tooltip or duration tooltip
105
- // YT, Vorapis, unknown
106
- const tooltipTextWrapper = document . querySelector ( ".ytp-tooltip-text-wrapper, .ytp-progress-tooltip-text-container" ) ?? document . querySelector ( "#progress-bar-container.ytk-player > #hover-time-info" ) ;
107
- const originalTooltip = tooltipTextWrapper . querySelector ( ".ytp-tooltip-title:not(.sponsorCategoryTooltip), .ytp-progress-tooltip-text:not(.sponsorCategoryTooltip)" ) as HTMLElement ;
132
+ // global chapter tooltip or duration tooltip
133
+ // YT, Vorapis, unknown, YTTV
134
+ const tooltipTextWrapper = document . querySelector ( ".ytp-tooltip-text-wrapper, .ytp-progress-tooltip-text-container, .yssi-slider .ys-seek-details .time-info-bar " ) ?? document . querySelector ( "#progress-bar-container.ytk-player > #hover-time-info" ) ;
135
+ const originalTooltip = tooltipTextWrapper . querySelector ( ".ytp-tooltip-title:not(.sponsorCategoryTooltip), .ytp-progress-tooltip-text:not(.sponsorCategoryTooltip), .current-time:not(.sponsorCategoryTooltip) " ) as HTMLElement ;
108
136
if ( ! tooltipTextWrapper || ! tooltipTextWrapper . parentElement ) return ;
109
137
110
138
// Grab the tooltip from the text wrapper as the tooltip doesn't have its classes on init
111
139
this . categoryTooltipContainer = tooltipTextWrapper . parentElement ;
112
- // YT, Vorapis
113
- const titleTooltip = tooltipTextWrapper . querySelector ( ".ytp-tooltip-title, .ytp-progress-tooltip-text" ) as HTMLElement ;
140
+ // YT, Vorapis, YTTV
141
+ const titleTooltip = tooltipTextWrapper . querySelector ( ".ytp-tooltip-title, .ytp-progress-tooltip-text, .current-time " ) as HTMLElement ;
114
142
if ( ! this . categoryTooltipContainer || ! titleTooltip ) return ;
115
143
116
144
tooltipTextWrapper . insertBefore ( this . categoryTooltip , titleTooltip . nextSibling ) ;
117
145
tooltipTextWrapper . insertBefore ( this . chapterTooltip , titleTooltip . nextSibling ) ;
118
146
119
- const seekBar = document . querySelector ( ".ytp-progress-bar-container" ) ;
147
+ if ( isOnYTTV ( ) ) {
148
+ const scrubTooltipTextWrapper = document . querySelector ( ".yssi-slider .ysl-filmstrip-lens .time-info-bar" )
149
+ if ( ! this . categoryTooltipContainer ) return ;
150
+
151
+ scrubTooltipTextWrapper . appendChild ( this . categoryScrubTooltip ) ;
152
+ scrubTooltipTextWrapper . appendChild ( this . chapterScrubTooltip ) ;
153
+ }
154
+
155
+ const seekBar = ( document . querySelector ( ".ytp-progress-bar-container, .ypcs-scrub-slider-slot.ytu-player-controls" ) ) ;
120
156
if ( ! seekBar ) return ;
121
157
122
158
let mouseOnSeekBar = false ;
@@ -163,6 +199,12 @@ class PreviewBar {
163
199
this . categoryTooltipContainer . classList . remove ( TOOLTIP_VISIBLE_CLASS ) ;
164
200
originalTooltip . style . removeProperty ( "display" ) ;
165
201
}
202
+ if ( this . onYTTV ) {
203
+ this . setTooltipTitle ( mainSegment , this . categoryTooltip ) ;
204
+ this . setTooltipTitle ( secondarySegment , this . chapterTooltip ) ;
205
+ this . setTooltipTitle ( mainSegment , this . categoryScrubTooltip ) ;
206
+ this . setTooltipTitle ( secondarySegment , this . chapterScrubTooltip ) ;
207
+ }
166
208
} else {
167
209
this . categoryTooltipContainer . classList . add ( TOOLTIP_VISIBLE_CLASS ) ;
168
210
if ( mainSegment !== null && secondarySegment !== null ) {
@@ -174,6 +216,10 @@ class PreviewBar {
174
216
175
217
this . setTooltipTitle ( mainSegment , this . categoryTooltip ) ;
176
218
this . setTooltipTitle ( secondarySegment , this . chapterTooltip ) ;
219
+ if ( this . onYTTV ) {
220
+ this . setTooltipTitle ( mainSegment , this . categoryScrubTooltip ) ;
221
+ this . setTooltipTitle ( secondarySegment , this . chapterScrubTooltip ) ;
222
+ }
177
223
178
224
if ( isVorapisInstalled ( ) ) {
179
225
const tooltipParent = tooltipTextWrapper . parentElement ! ;
@@ -226,7 +272,12 @@ class PreviewBar {
226
272
}
227
273
228
274
// On the seek bar
229
- this . parent . prepend ( this . container ) ;
275
+ if ( this . onYTTV ) {
276
+ // order of sibling elements matters on YTTV
277
+ this . parent . insertBefore ( this . container , this . parent . firstChild . nextSibling . nextSibling ) ;
278
+ } else {
279
+ this . parent . prepend ( this . container ) ;
280
+ }
230
281
}
231
282
232
283
clear ( ) : void {
@@ -362,6 +413,10 @@ class PreviewBar {
362
413
bar . style . marginRight = `${ this . chapterMargin } px` ;
363
414
}
364
415
416
+ if ( this . onYTTV ) {
417
+ bar . classList . add ( "previewbar-yttv" ) ;
418
+ }
419
+
365
420
return bar ;
366
421
}
367
422
@@ -868,8 +923,10 @@ class PreviewBar {
868
923
} ) [ 0 ] ;
869
924
870
925
const chapterButton = this . getChapterButton ( chaptersContainer ) ;
871
- chapterButton . classList . remove ( "ytp-chapter-container-disabled" ) ;
872
- chapterButton . disabled = false ;
926
+ if ( chapterButton ) {
927
+ chapterButton . classList . remove ( "ytp-chapter-container-disabled" ) ;
928
+ chapterButton . disabled = false ;
929
+ }
873
930
874
931
const chapterTitle = chaptersContainer . querySelector ( ".ytp-chapter-title-content" ) as HTMLDivElement ;
875
932
chapterTitle . style . display = "none" ;
@@ -878,6 +935,9 @@ class PreviewBar {
878
935
const elem = document . createElement ( "div" ) ;
879
936
chapterTitle . parentElement . insertBefore ( elem , chapterTitle ) ;
880
937
elem . classList . add ( "sponsorChapterText" ) ;
938
+ if ( document . location . host === "tv.youtube.com" ) {
939
+ elem . style . lineHeight = "initial" ;
940
+ }
881
941
return elem ;
882
942
} ) ( ) ) as HTMLDivElement ;
883
943
chapterCustomText . innerText = chosenSegment . description || shortCategoryName ( chosenSegment . category ) ;
@@ -890,7 +950,15 @@ class PreviewBar {
890
950
891
951
if ( chosenSegment . source === SponsorSourceType . Server ) {
892
952
const chapterVoteContainer = this . chapterVote . getContainer ( ) ;
893
- if ( ! chapterButton . contains ( chapterVoteContainer ) ) {
953
+ if ( document . location . host === "tv.youtube.com" ) {
954
+ if ( ! chaptersContainer . contains ( chapterVoteContainer ) ) {
955
+ const oldVoteContainers = document . querySelectorAll ( "#chapterVote" ) ;
956
+ if ( oldVoteContainers . length > 0 ) {
957
+ oldVoteContainers . forEach ( ( oldVoteContainer ) => oldVoteContainer . remove ( ) ) ;
958
+ }
959
+ chaptersContainer . appendChild ( chapterVoteContainer ) ;
960
+ }
961
+ } else if ( ! chapterButton . contains ( chapterVoteContainer ) ) {
894
962
const oldVoteContainers = document . querySelectorAll ( "#chapterVote" ) ;
895
963
if ( oldVoteContainers . length > 0 ) {
896
964
oldVoteContainers . forEach ( ( oldVoteContainer ) => oldVoteContainer . remove ( ) ) ;
@@ -929,6 +997,18 @@ class PreviewBar {
929
997
}
930
998
931
999
private getChaptersContainer ( ) : HTMLElement {
1000
+ if ( document . location . host === "tv.youtube.com" ) {
1001
+ if ( ! document . querySelector ( ".ytp-chapter-container" ) ) {
1002
+ const dest = document . querySelector ( ".ypcs-control-buttons-left" ) ;
1003
+ if ( ! dest ) return null ;
1004
+ const sbChapterContainer = document . createElement ( "div" ) ;
1005
+ sbChapterContainer . className = "ytp-chapter-container" ;
1006
+ const sbChapterTitleContent = document . createElement ( "div" ) ;
1007
+ sbChapterTitleContent . className = "ytp-chapter-title-content" ;
1008
+ sbChapterContainer . appendChild ( sbChapterTitleContent ) ;
1009
+ dest . appendChild ( sbChapterContainer ) ;
1010
+ }
1011
+ }
932
1012
return document . querySelector ( ".ytp-chapter-container" ) as HTMLElement ;
933
1013
}
934
1014
0 commit comments