@oxymoron.13/tizentube-s95b 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +42 -49
  2. package/S95B-CHANGES.txt +3 -9
  3. package/dist/userScript.js +2 -2
  4. package/package.json +5 -11
  5. package/UPSTREAM-README.md +0 -42
  6. package/mods/config.js +0 -117
  7. package/mods/domrect-polyfill.js +0 -104
  8. package/mods/features/adblock.js +0 -584
  9. package/mods/features/autoFrameRate.js +0 -42
  10. package/mods/features/enableFeatures.js +0 -19
  11. package/mods/features/moreSubtitles.js +0 -378
  12. package/mods/features/pictureInPicture.js +0 -198
  13. package/mods/features/preferredVideoQuality.js +0 -101
  14. package/mods/features/s95bCodec.js +0 -45
  15. package/mods/features/s95bI18n.js +0 -36
  16. package/mods/features/s95bMenu.js +0 -246
  17. package/mods/features/sponsorblock.js +0 -437
  18. package/mods/features/standaloneUserscript.js +0 -107
  19. package/mods/features/updater.js +0 -119
  20. package/mods/features/userAgentSpoofing.js +0 -73
  21. package/mods/features/videoQueuing.js +0 -64
  22. package/mods/package-lock.json +0 -2321
  23. package/mods/package.json +0 -33
  24. package/mods/resolveCommand.js +0 -376
  25. package/mods/rollup.config.js +0 -41
  26. package/mods/spatial-navigation-polyfill.js +0 -1756
  27. package/mods/tiny-sha256.js +0 -99
  28. package/mods/translations/i18nResources.js +0 -11
  29. package/mods/translations/index.js +0 -18
  30. package/mods/translations/language-names.js +0 -981
  31. package/mods/translations/resources/ar.json +0 -308
  32. package/mods/translations/resources/az.json +0 -308
  33. package/mods/translations/resources/bs.json +0 -308
  34. package/mods/translations/resources/cs.json +0 -308
  35. package/mods/translations/resources/de.json +0 -308
  36. package/mods/translations/resources/dk.json +0 -308
  37. package/mods/translations/resources/el.json +0 -308
  38. package/mods/translations/resources/en.json +0 -308
  39. package/mods/translations/resources/es-419.json +0 -308
  40. package/mods/translations/resources/es.json +0 -308
  41. package/mods/translations/resources/fi-FI.json +0 -308
  42. package/mods/translations/resources/fr.json +0 -311
  43. package/mods/translations/resources/hr.json +0 -308
  44. package/mods/translations/resources/hu.json +0 -308
  45. package/mods/translations/resources/id.json +0 -308
  46. package/mods/translations/resources/it.json +0 -308
  47. package/mods/translations/resources/ja.json +0 -308
  48. package/mods/translations/resources/lt.json +0 -308
  49. package/mods/translations/resources/lv.json +0 -308
  50. package/mods/translations/resources/pl.json +0 -308
  51. package/mods/translations/resources/pt-BR.json +0 -308
  52. package/mods/translations/resources/pt-PT.json +0 -308
  53. package/mods/translations/resources/ro.json +0 -310
  54. package/mods/translations/resources/ru.json +0 -308
  55. package/mods/translations/resources/sr-Cyrl.json +0 -308
  56. package/mods/translations/resources/sr-Latn.json +0 -308
  57. package/mods/translations/resources/tlh.json +0 -308
  58. package/mods/translations/resources/tr.json +0 -308
  59. package/mods/translations/resources/uk.json +0 -308
  60. package/mods/translations/resources/vi.json +0 -309
  61. package/mods/translations/resources/zh-CN.json +0 -308
  62. package/mods/translations/resources/zh-TW.json +0 -308
  63. package/mods/translations/update-translations.js +0 -55
  64. package/mods/ui/chapters.js +0 -137
  65. package/mods/ui/clock.js +0 -71
  66. package/mods/ui/customCommandExecution.js +0 -49
  67. package/mods/ui/customGuideAction.js +0 -119
  68. package/mods/ui/customUI.js +0 -192
  69. package/mods/ui/customYTSettings.js +0 -29
  70. package/mods/ui/disableWhosWatching.js +0 -48
  71. package/mods/ui/settings.js +0 -1024
  72. package/mods/ui/sidebarModification.js +0 -179
  73. package/mods/ui/speedUI.js +0 -119
  74. package/mods/ui/theme.js +0 -26
  75. package/mods/ui/ui.css +0 -157
  76. package/mods/ui/ui.js +0 -261
  77. package/mods/ui/ytUI.js +0 -510
  78. package/mods/userScript.js +0 -28
  79. package/mods/utils/ASTParser.js +0 -67
  80. package/mods/utils/innerTubeCalls.js +0 -138
  81. package/service/package-lock.json +0 -4193
  82. package/service/package.json +0 -29
  83. package/service/rollup.config.js +0 -47
  84. package/service/service.js +0 -118
@@ -1,584 +0,0 @@
1
- import { selectCodecFormats } from './s95bCodec.js';
2
- import { configRead } from '../config.js';
3
- import Chapters from '../ui/chapters.js';
4
- import resolveCommand from '../resolveCommand.js';
5
- import { timelyAction, longPressData, MenuServiceItemRenderer, ShelfRenderer, TileRenderer, ButtonRenderer } from '../ui/ytUI.js';
6
- import { findFeedbackToken } from '../utils/innerTubeCalls.js';
7
- import { PatchSettings } from '../ui/customYTSettings.js';
8
- import { t } from 'i18next';
9
-
10
- /**
11
- * This is a minimal reimplementation of the following uBlock Origin rule:
12
- * https://github.com/uBlockOrigin/uAssets/blob/3497eebd440f4871830b9b45af0afc406c6eb593/filters/filters.txt#L116
13
- *
14
- * This in turn calls the following snippet:
15
- * https://github.com/gorhill/uBlock/blob/bfdc81e9e400f7b78b2abc97576c3d7bf3a11a0b/assets/resources/scriptlets.js#L365-L470
16
- *
17
- * Seems like for now dropping just the adPlacements is enough for YouTube TV
18
- */
19
- const origParse = JSON.parse;
20
- JSON.parse = function () {
21
- const r = origParse.apply(this, arguments);
22
- try {
23
- const adBlockEnabled = configRead('enableAdBlock');
24
- const signinReminderEnabled = configRead('enableSigninReminder');
25
-
26
- if (r?.playbackContext?.contentPlaybackContext) {
27
- // Handle inline playback without ads
28
- // Avoid hot-path console logging (especially on older TV engines).
29
- }
30
-
31
- if (r.adPlacements && adBlockEnabled) {
32
- r.adPlacements = [];
33
- }
34
-
35
- // Also set playerAds to false, just incase.
36
- if (r.playerAds && adBlockEnabled) {
37
- r.playerAds = false;
38
- }
39
-
40
- // Also set adSlots to an empty array, emptying only the adPlacements won't work.
41
- if (r.adSlots && adBlockEnabled) {
42
- r.adSlots = [];
43
- }
44
-
45
- if (r.paidContentOverlay && !configRead('enablePaidPromotionOverlay')) {
46
- r.paidContentOverlay = null;
47
- }
48
-
49
- selectCodecFormats(r, configRead('preferredVideoCodec'));
50
-
51
- // Drop "masthead" ad from home screen
52
- if (
53
- r?.contents?.tvBrowseRenderer?.content?.tvSurfaceContentRenderer?.content
54
- ?.sectionListRenderer?.contents
55
- ) {
56
- if (!signinReminderEnabled) {
57
- r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.content.sectionListRenderer.contents =
58
- r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.content.sectionListRenderer.contents.filter(
59
- (elm) => !elm.feedNudgeRenderer
60
- );
61
- }
62
-
63
- if (adBlockEnabled) {
64
- r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.content.sectionListRenderer.contents =
65
- r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.content.sectionListRenderer.contents.filter(
66
- (elm) => !elm.adSlotRenderer
67
- );
68
-
69
- for (const shelve of r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.content.sectionListRenderer.contents) {
70
- if (shelve.shelfRenderer && shelve.shelfRenderer.content?.horizontalListRenderer?.items) {
71
- shelve.shelfRenderer.content.horizontalListRenderer.items =
72
- shelve.shelfRenderer.content.horizontalListRenderer.items.filter(
73
- (item) => !item.adSlotRenderer
74
- );
75
- }
76
- }
77
- }
78
-
79
- processShelves(r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.content.sectionListRenderer.contents);
80
- }
81
-
82
- if (
83
- r?.contents?.tvBrowseRenderer?.content?.tvSurfaceContentRenderer?.content
84
- ?.gridRenderer?.items
85
- ) {
86
- addLongPress(r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.content.gridRenderer.items);
87
- }
88
-
89
- if (r.endscreen && configRead('enableHideEndScreenCards')) {
90
- r.endscreen = null;
91
- }
92
-
93
- if (r.messages && Array.isArray(r.messages) && !configRead('enableYouThereRenderer')) {
94
- r.messages = r.messages.filter(
95
- (msg) => !msg?.youThereRenderer
96
- );
97
- }
98
-
99
- // Remove shorts ads
100
- if (!Array.isArray(r) && r?.entries && adBlockEnabled) {
101
- r.entries = r.entries?.filter(
102
- (elm) => !elm?.command?.reelWatchEndpoint?.adClientParams?.isAd
103
- );
104
- }
105
-
106
- // Patch settings
107
-
108
- if (r?.title?.runs) {
109
- PatchSettings(r);
110
- }
111
-
112
- // DeArrow Implementation. I think this is the best way to do it. (DOM manipulation would be a pain)
113
-
114
- if (r?.contents?.sectionListRenderer?.contents) {
115
- processShelves(r.contents.sectionListRenderer.contents);
116
- }
117
-
118
- if (r?.continuationContents?.sectionListContinuation?.contents) {
119
- processShelves(r.continuationContents.sectionListContinuation.contents);
120
- }
121
-
122
- if (r?.continuationContents?.horizontalListContinuation?.items) {
123
- deArrowify(r.continuationContents.horizontalListContinuation.items);
124
- hqify(r.continuationContents.horizontalListContinuation.items);
125
- addLongPress(r.continuationContents.horizontalListContinuation.items);
126
- r.continuationContents.horizontalListContinuation.items = hideVideo(r.continuationContents.horizontalListContinuation.items);
127
- }
128
-
129
- if (r?.continuationContents?.gridContinuation?.items) {
130
- addLongPress(r.continuationContents.gridContinuation.items);
131
- }
132
-
133
- if (r?.contents?.tvBrowseRenderer?.content?.tvSecondaryNavRenderer?.sections) {
134
- for (let i = 0; i < r.contents.tvBrowseRenderer.content.tvSecondaryNavRenderer.sections.length; i++) {
135
- const section = r.contents.tvBrowseRenderer.content.tvSecondaryNavRenderer.sections[i].tvSecondaryNavSectionRenderer;
136
- if (!section || !section.tabs) continue;
137
-
138
- if (configRead('sortSubscriptionsByAlphabet')) {
139
- section.tabs.sort((a, b) => {
140
- if (a.tabRenderer.selected && !b.tabRenderer.selected) return -1;
141
- if (!a.tabRenderer.selected && b.tabRenderer.selected) return 1;
142
- return a.tabRenderer.title.localeCompare(b.tabRenderer.title);
143
- });
144
- }
145
-
146
- for (let j = 0; j < section.tabs.length; j++) {
147
- const tab = section.tabs[j];
148
- const content = tab.tabRenderer.content?.tvSurfaceContentRenderer?.content;
149
- if (content?.sectionListRenderer?.contents) {
150
- const index = section.tabs.indexOf(tab);
151
- const clone = content.sectionListRenderer.contents;
152
- processShelves(clone);
153
- section.tabs[index].tabRenderer.content.tvSurfaceContentRenderer.content.sectionListRenderer.contents = clone;
154
- }
155
- if (content?.gridRenderer?.items) {
156
- addLongPress(content.gridRenderer.items);
157
- }
158
- }
159
- }
160
- }
161
-
162
- if (r?.contents?.singleColumnWatchNextResults?.pivot?.sectionListRenderer) {
163
- if (configRead('hideRelatedVideosPlayer')) {
164
- r.contents.singleColumnWatchNextResults.pivot.sectionListRenderer.contents = [{}]
165
- r.contents.singleColumnWatchNextResults.pivot.sectionListRenderer.continuations = []
166
- }
167
- if (!signinReminderEnabled) {
168
- r.contents.singleColumnWatchNextResults.pivot.sectionListRenderer.contents =
169
- r.contents.singleColumnWatchNextResults.pivot.sectionListRenderer.contents.filter(
170
- (elm) => !elm.alertWithActionsRenderer
171
- );
172
- }
173
- processShelves(r.contents.singleColumnWatchNextResults.pivot.sectionListRenderer.contents, false);
174
- if (window.queuedVideos.videos.length > 0) {
175
- const queuedVideosClone = window.queuedVideos.videos.slice();
176
- queuedVideosClone.unshift(TileRenderer(
177
- 'Clear Queue',
178
- {
179
- customAction: {
180
- action: 'CLEAR_QUEUE'
181
- }
182
- }));
183
- r.contents.singleColumnWatchNextResults.pivot.sectionListRenderer.contents.unshift(ShelfRenderer(
184
- 'Queued Videos',
185
- queuedVideosClone,
186
- queuedVideosClone.findIndex(v => v.contentId === window.queuedVideos.lastVideoId) !== -1 ?
187
- queuedVideosClone.findIndex(v => v.contentId === window.queuedVideos.lastVideoId)
188
- : 0
189
- ));
190
- }
191
- }
192
- /*
193
-
194
- Chapters are disabled due to the API removing description data which was used to generate chapters
195
-
196
- if (r?.contents?.singleColumnWatchNextResults?.results?.results?.contents && configRead('enableChapters')) {
197
- const chapterData = Chapters(r);
198
- r.frameworkUpdates.entityBatchUpdate.mutations.push(chapterData);
199
- resolveCommand({
200
- "clickTrackingParams": "null",
201
- "loadMarkersCommand": {
202
- "visibleOnLoadKeys": [
203
- chapterData.entityKey
204
- ],
205
- "entityKeys": [
206
- chapterData.entityKey
207
- ]
208
- }
209
- });
210
- }*/
211
-
212
- // Manual SponsorBlock Skips
213
-
214
- if (r?.playerOverlays?.playerOverlayRenderer) {
215
- if (r.playerOverlays.playerOverlayRenderer.timelyActionRenderers) {
216
- r.playerOverlays.playerOverlayRenderer.timelyActionRenderers =
217
- r.playerOverlays.playerOverlayRenderer.timelyActionRenderers.filter(a => a.timelyActionRenderer.type !== 'TIMELY_ACTION_TYPE_SHOPPING' &&
218
- a.timelyActionRenderer.type !== 'TIMELY_ACTION_TYPE_NFL_WATERMARK');
219
- } else r.playerOverlays.playerOverlayRenderer.timelyActionRenderers = [];
220
- if (configRead('sponsorBlockManualSkips').length > 0) {
221
- const manualSkippedSegments = configRead('sponsorBlockManualSkips');
222
- if (window?.sponsorblock?.segments) {
223
- for (const segment of window.sponsorblock.segments) {
224
- if (manualSkippedSegments.includes(segment.category)) {
225
- const timelyActionData = timelyAction(
226
- t('sponsorblock.toasts.skip', { segment: t(`sponsorblock.segments.${segment.category}`) }),
227
- 'SKIP_NEXT',
228
- {
229
- clickTrackingParams: null,
230
- showEngagementPanelEndpoint: {
231
- customAction: {
232
- action: 'SKIP',
233
- parameters: {
234
- time: segment.segment[1]
235
- }
236
- }
237
- }
238
- },
239
- segment.segment[0] * 1000,
240
- segment.segment[1] * 1000 - segment.segment[0] * 1000
241
- );
242
- r.playerOverlays.playerOverlayRenderer.timelyActionRenderers.push(timelyActionData);
243
- }
244
- }
245
- }
246
- }
247
- }
248
-
249
- if (r?.transportControls?.transportControlsRenderer?.promotedActions && configRead('enableSponsorBlockHighlight')) {
250
- if (window?.sponsorblock?.segments) {
251
- const category = window.sponsorblock.segments.find(seg => seg.category === 'poi_highlight');
252
- if (category) {
253
- r.transportControls.transportControlsRenderer.promotedActions.push({
254
- type: 'TRANSPORT_CONTROLS_BUTTON_TYPE_SPONSORBLOCK_HIGHLIGHT',
255
- button: {
256
- buttonRenderer: ButtonRenderer(
257
- false,
258
- t('sponsorblock.toasts.skipToHighlight'),
259
- 'SKIP_NEXT',
260
- {
261
- clickTrackingParams: null,
262
- customAction: {
263
- action: 'SKIP',
264
- parameters: {
265
- time: category.segment[0]
266
- }
267
- }
268
- })
269
- }
270
- });
271
- }
272
- }
273
- }
274
-
275
- if (r?.contents?.tvBrowseRenderer?.content?.tvSurfaceContentRenderer?.header?.channelHeaderRenderer?.buttons) {
276
- let browseId = null;
277
- const title = r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.header.channelHeaderRenderer.title.simpleText;
278
- for (const service of r.responseContext.serviceTrackingParams) {
279
- for (const param of service.params) {
280
- if (param.key === 'browse_id') {
281
- browseId = param.value;
282
- break;
283
- }
284
- }
285
- if (browseId) break;
286
- }
287
-
288
- const inSidebar = configRead('sidebarContentsOrder')?.some(orderItem =>
289
- (typeof orderItem === 'object' ? orderItem.browseId : orderItem) === browseId);
290
-
291
- r.contents.tvBrowseRenderer.content.tvSurfaceContentRenderer.header.channelHeaderRenderer.buttons.push({
292
- buttonRenderer: ButtonRenderer(
293
- false,
294
- inSidebar ? t('settings.options.uiSettings.options.sortSidebarContents.removeFromSidebar') : t('settings.options.uiSettings.options.sortSidebarContents.addToSidebar'),
295
- inSidebar ? 'REMOVE' : 'ADD',
296
- {
297
- customAction: {
298
- action: 'ADD_OR_REMOVE_CHANNEL_TO_SIDEBAR',
299
- parameters: {
300
- browseId,
301
- title
302
- }
303
- }
304
- }
305
- )
306
- })
307
- }
308
- } catch (e) {
309
- console.error('An error occured while processing the JSON:', e);
310
- }
311
-
312
- return r;
313
- };
314
-
315
- // Fix playback issues
316
-
317
- const origStringify = JSON.stringify;
318
- JSON.stringify = function (value, replacer, space) {
319
- if (value?.playbackContext?.contentPlaybackContext) {
320
- const copiedValue = JSON.parse(origStringify(value));
321
- if (!copiedValue.playbackContext.contentPlaybackContext.isInlinePlaybackNoAd) {
322
- copiedValue.playbackContext.contentPlaybackContext.isInlinePlaybackNoAd = true;
323
- return origStringify.call(this, copiedValue, replacer, space);
324
- }
325
- }
326
- return origStringify.call(this, value, replacer, space);
327
- };
328
-
329
- window.JSON.stringify = JSON.stringify;
330
-
331
- // Patch JSON.parse to use the custom one
332
- window.JSON.parse = JSON.parse;
333
- for (const key in window._yttv) {
334
- if (window._yttv[key] && window._yttv[key].JSON && window._yttv[key].JSON.parse) {
335
- window._yttv[key].JSON.parse = JSON.parse;
336
- }
337
- }
338
-
339
-
340
- function processShelves(shelves, shouldAddPreviews = true) {
341
- for (const shelve of shelves) {
342
- if (shelve.shelfRenderer) {
343
- if (!shelve.shelfRenderer.tvhtml5Style) shelve.shelfRenderer.tvhtml5Style = { effects: {} };
344
- if (configRead('disableEnlargingThumbnails')) shelve.shelfRenderer.tvhtml5Style.effects.enlarge = false;
345
- if (configRead('enableShrinkingThumbnails')) shelve.shelfRenderer.tvhtml5Style.effects.shrink = true;
346
- if (!shelve.shelfRenderer.content?.horizontalListRenderer?.items) continue;
347
- deArrowify(shelve.shelfRenderer.content.horizontalListRenderer.items);
348
- hqify(shelve.shelfRenderer.content.horizontalListRenderer.items);
349
- addLongPress(shelve.shelfRenderer.content.horizontalListRenderer.items);
350
- if (shouldAddPreviews) {
351
- addPreviews(shelve.shelfRenderer.content.horizontalListRenderer.items);
352
- }
353
- shelve.shelfRenderer.content.horizontalListRenderer.items = hideVideo(shelve.shelfRenderer.content.horizontalListRenderer.items);
354
- if (!configRead('enableShorts')) {
355
- if (shelve.shelfRenderer.tvhtml5ShelfRendererType === 'TVHTML5_SHELF_RENDERER_TYPE_SHORTS') {
356
- shelves.splice(shelves.indexOf(shelve), 1);
357
- continue;
358
- }
359
- shelve.shelfRenderer.content.horizontalListRenderer.items = shelve.shelfRenderer.content.horizontalListRenderer.items.filter(item => item.tileRenderer?.tvhtml5ShelfRendererType !== 'TVHTML5_TILE_RENDERER_TYPE_SHORTS');
360
- shelve.shelfRenderer.content.horizontalListRenderer.items = shelve.shelfRenderer.content.horizontalListRenderer.items.filter(item => item.lockupViewModel?.contentType !== 'LOCKUP_CONTENT_TYPE_SHORT');
361
-
362
- shelve.shelfRenderer.content.horizontalListRenderer.items = shelve.shelfRenderer.content.horizontalListRenderer.items.filter(item => !item.tileRenderer?.onSelectCommand?.reelWatchEndpoint);
363
- }
364
- }
365
- }
366
- }
367
-
368
- function addPreviews(items) {
369
- if (!configRead('enablePreviews')) return;
370
- for (const item of items) {
371
- if (item.tileRenderer) {
372
- const watchEndpoint = item.tileRenderer.onSelectCommand;
373
- const copiedEndpoint = JSON.parse(JSON.stringify(watchEndpoint));
374
- if (item.tileRenderer?.onFocusCommand?.playbackEndpoint) continue;
375
- if (item.tileRenderer?.onFocusCommand?.commandExecutorCommand) continue;
376
- item.tileRenderer.onFocusCommand = {
377
- startInlinePlaybackCommand: {
378
- blockAdoption: true,
379
- caption: false,
380
- delayMs: 3000,
381
- durationMs: 40000,
382
- muted: false,
383
- restartPlaybackBeforeSeconds: 10,
384
- resumeVideo: true,
385
- playbackEndpoint: copiedEndpoint
386
- }
387
- };
388
- }
389
- }
390
- }
391
-
392
- function deArrowify(items) {
393
- for (const item of items) {
394
- if (item.adSlotRenderer) {
395
- const index = items.indexOf(item);
396
- items.splice(index, 1);
397
- continue;
398
- }
399
- if (!item.tileRenderer && item.lockupViewModel) continue;
400
- if (!item?.lockupViewModel?.contentType !== 'LOCKUP_CONTENT_TYPE_VIDEO') continue;
401
- if (configRead('enableDeArrow')) {
402
- const videoID = item.tileRenderer.contentId || item.lockupViewModel.contentId;
403
- fetch(`https://sponsor.ajay.app/api/branding?videoID=${videoID}`).then(res => res.json()).then(data => {
404
- if (data.titles.length > 0) {
405
- const mostVoted = data.titles.reduce((max, title) => max.votes > title.votes ? max : title);
406
- item.tileRenderer ?
407
- item.tileRenderer.metadata.tileMetadataRenderer.title.simpleText = mostVoted.title
408
- : item.lockupViewModel.metadata.lockupMetadataViewModel.title.content = mostVoted.title;
409
- }
410
-
411
- if (data.thumbnails.length > 0 && configRead('enableDeArrowThumbnails')) {
412
- const mostVotedThumbnail = data.thumbnails.reduce((max, thumbnail) => max.votes > thumbnail.votes ? max : thumbnail);
413
- if (mostVotedThumbnail.timestamp) {
414
- item.tileRenderer ?
415
- item.tileRenderer.header.tileHeaderRenderer.thumbnail.thumbnails = [
416
- {
417
- url: `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoID}&time=${mostVotedThumbnail.timestamp}`,
418
- width: 1280,
419
- height: 640
420
- }
421
- ] : item.lockupViewModel.contentImage.thumbnailViewModel.image.sources = [
422
- {
423
- url: `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoID}&time=${mostVotedThumbnail.timestamp}`,
424
- width: 1280,
425
- height: 640
426
- }
427
- ]
428
- }
429
- }
430
- }).catch(() => { });
431
- }
432
- }
433
- }
434
-
435
-
436
- function hqify(items) {
437
- for (const item of items) {
438
- if (!item.tileRenderer && !item.lockupViewModel) continue;
439
- if (item?.tileRenderer?.style !== 'TILE_STYLE_YTLR_DEFAULT' && item?.lockupViewModel?.contentType !== 'LOCKUP_CONTENT_TYPE_VIDEO') continue;
440
- if (configRead('enableHqThumbnails')) {
441
- if (!item?.tileRenderer?.onSelectCommand?.watchEndpoint?.videoId && !item?.lockupViewModel?.contentId) continue;
442
- if (!item?.tileRenderer?.header?.tileHeaderRenderer?.thumbnail?.thumbnails?.[0]?.url && !item?.lockupViewModel?.contentImage?.thumbnailViewModel?.image?.sources?.[0]?.url) continue;
443
- const videoID = item.tileRenderer ? item.tileRenderer.onSelectCommand.watchEndpoint.videoId : item.lockupViewModel.contentId;
444
- const queryArgs = item.tileRenderer ? item.tileRenderer.header.tileHeaderRenderer.thumbnail.thumbnails[0].url.split('?')[1] : item.lockupViewModel.contentImage.thumbnailViewModel.image.sources[0].url.split('?')[1];
445
- item.tileRenderer ? item.tileRenderer.header.tileHeaderRenderer.thumbnail.thumbnails = [
446
- {
447
- url: `https://i.ytimg.com/vi/${videoID}/sddefault.jpg${queryArgs ? `?${queryArgs}` : ''}`,
448
- width: 640,
449
- height: 480
450
- }
451
- ] : item.lockupViewModel.contentImage.thumbnailViewModel.image.sources = [
452
- {
453
- url: `https://i.ytimg.com/vi/${videoID}/sddefault.jpg${queryArgs ? `?${queryArgs}` : ''}`,
454
- width: 640,
455
- height: 480
456
- }
457
- ];
458
- }
459
- }
460
- }
461
-
462
- function addLongPress(items) {
463
- for (const item of items) {
464
- if (!item.tileRenderer && !item.lockupViewModel) continue;
465
- if (item?.tileRenderer?.style !== 'TILE_STYLE_YTLR_DEFAULT' && item?.lockupViewModel?.contentType !== 'LOCKUP_CONTENT_TYPE_VIDEO') continue;
466
- if (item?.tileRenderer?.onLongPressCommand?.showMenuCommand?.menu?.menuRenderer?.items
467
- || item?.lockupViewModel?.rendererContext?.commandContext?.onLongPress?.innertubeCommand?.showMenuCommand?.menu?.menuRenderer?.items) {
468
- const copiedItem = JSON.parse(JSON.stringify(item));
469
- const button = MenuServiceItemRenderer('Add to Queue', {
470
- clickTrackingParams: null,
471
- playlistEditEndpoint: {
472
- customAction: {
473
- action: 'ADD_TO_QUEUE',
474
- parameters: copiedItem
475
- }
476
- }
477
- })
478
- const menuItems = item.tileRenderer ? item.tileRenderer.onLongPressCommand.showMenuCommand.menu.menuRenderer.items
479
- : item.lockupViewModel.rendererContext.commandContext.onLongPress.innertubeCommand.showMenuCommand.menu.menuRenderer.items;
480
- menuItems.push(button);
481
- restoreFeedbackMenuItems(item, menuItems);
482
- continue;
483
- }
484
- if (!configRead('enableLongPress')) continue;
485
- if (!item.tileRenderer?.metadata?.tileMetadataRenderer && !item.lockupViewModel?.metadata?.lockupMetadataViewModel) continue;
486
- if (!item.tileRenderer?.header?.tileHeaderRenderer?.thumbnail?.thumbnails &&
487
- !item.lockupViewModel?.contentImage?.thumbnailViewModel?.image?.sources) continue;
488
- if (!item?.tileRenderer?.onSelectCommand?.watchEndpoint &&
489
- !item?.lockupViewModel?.rendererContext?.commandContext?.onTap?.innertubeCommand?.watchEndpoint) continue;
490
- const copiedItem = JSON.parse(JSON.stringify(item));
491
- const subtitleNode = copiedItem?.tileRenderer ? copiedItem.tileRenderer.metadata?.tileMetadataRenderer?.lines?.[0]?.lineRenderer?.items?.[0]?.lineItemRenderer?.text :
492
- copiedItem.lockupViewModel.metadata?.lockupMetadataViewModel?.metadata?.contentMetadataViewModel?.metadataRows?.[0]?.metadataParts?.[0]?.text?.content
493
- ;
494
- if (!subtitleNode) continue;
495
- const subtitle = subtitleNode;
496
- const data = longPressData({
497
- videoId: copiedItem?.tileRenderer?.contentId || copiedItem?.lockupViewModel?.contentId,
498
- thumbnails: copiedItem?.tileRenderer?.header?.tileHeaderRenderer?.thumbnail?.thumbnails || copiedItem?.lockupViewModel?.contentImage?.thumbnailViewModel?.image?.sources,
499
- title: copiedItem?.tileRenderer?.metadata?.tileMetadataRenderer?.title?.simpleText || copiedItem?.lockupViewModel?.metadata?.lockupMetadataViewModel?.title?.content,
500
- subtitle: subtitle.length && subtitle.length > 0 ? subtitle : subtitle.runs ? subtitle.runs[0].text : subtitle.simpleText,
501
- watchEndpointData: copiedItem?.tileRenderer?.onSelectCommand?.watchEndpoint || copiedItem?.lockupViewModel?.rendererContext?.commandContext?.onTap?.innertubeCommand?.watchEndpoint,
502
- item: copiedItem
503
- });
504
- restoreFeedbackMenuItems(item, data.showMenuCommand.menu.menuRenderer.items);
505
- item.tileRenderer ? item.tileRenderer.onLongPressCommand = data
506
- : item.lockupViewModel.rendererContext.commandContext.onLongPress = {
507
- innertubeCommand: data
508
- };
509
- }
510
- }
511
-
512
- // YouTube moved the "Not interested" and "Don't recommend channel" items out of the
513
- // long press menu into an engagement panel (panelId + params) that has to be fetched
514
- // via /youtubei/v1/get_panel before the feedback tokens can be used. If the item
515
- // carries such a panel reference and the menu doesn't already contain feedback items,
516
- // add them back. The actual token fetching / feedback sending happens on click via
517
- // the NOT_INTERESTED / DONT_RECOMMEND_CHANNEL custom actions (see resolveCommand.js).
518
- function getFeedbackPanel(item) {
519
- const onLongPress = item?.tileRenderer
520
- ? item.tileRenderer.onLongPressCommand
521
- : item?.lockupViewModel?.rendererContext?.commandContext?.onLongPress;
522
- const endpoint = onLongPress?.showEngagementPanelEndpoint
523
- ?? onLongPress?.innertubeCommand?.showEngagementPanelEndpoint;
524
- if (endpoint?.identifier?.tag && endpoint?.globalConfiguration?.params) {
525
- return {
526
- panelId: endpoint.identifier.tag,
527
- params: endpoint.globalConfiguration.params
528
- };
529
- }
530
- return null;
531
- }
532
-
533
- function restoreFeedbackMenuItems(item, menuItems) {
534
- const panel = getFeedbackPanel(item);
535
- if (!panel || menuHasFeedbackItems(menuItems)) return;
536
- for (const feedbackItem of feedbackMenuItems(panel)) {
537
- menuItems.push(feedbackItem);
538
- }
539
- }
540
-
541
- function menuHasFeedbackItems(menuItems) {
542
- // Only look at the endpoints of the menu items themselves, not their
543
- // parameters (which may embed a full copy of the video item).
544
- return menuItems?.some((menuItem) =>
545
- !!findFeedbackToken(menuItem?.menuServiceItemRenderer?.serviceEndpoint)
546
- || !!findFeedbackToken(menuItem?.menuNavigationItemRenderer?.navigationEndpoint));
547
- }
548
-
549
- function feedbackMenuItems(panel) {
550
- return [
551
- MenuServiceItemRenderer(t('videoMenu.notInterested'), {
552
- clickTrackingParams: null,
553
- customAction: {
554
- action: 'NOT_INTERESTED',
555
- parameters: panel
556
- }
557
- }),
558
- MenuServiceItemRenderer(t('videoMenu.dontRecommendChannel'), {
559
- clickTrackingParams: null,
560
- customAction: {
561
- action: 'DONT_RECOMMEND_CHANNEL',
562
- parameters: panel
563
- }
564
- })
565
- ];
566
- }
567
-
568
- function hideVideo(items) {
569
- return items.filter(item => {
570
- if (!item.tileRenderer) return true;
571
- const progressBar = item?.tileRenderer ? item.tileRenderer.header?.tileHeaderRenderer?.thumbnailOverlays?.find(overlay => overlay.thumbnailOverlayResumePlaybackRenderer)?.thumbnailOverlayResumePlaybackRenderer
572
- : item.lockupViewModel?.contentImage?.thumbnailViewModel?.overlays?.find(overlay => overlay.thumbnailBottomOverlayViewModel?.progressBar)?.thumbnailBottomOverlayViewModel?.progressBar. thumbnailOverlayProgressBarViewModel;
573
- if (!progressBar) return true;
574
- const pages = configRead('hideWatchedVideosPages');
575
- if (!pages.length) return true;
576
- const hash = location.hash.substring(1);
577
- const pageName = hash === '/' ? 'home' : hash.startsWith('/search') ? 'search' : hash.split('?')[1]?.split('&')[0]?.split('=')[1]?.replace('FE', '')?.replace('topics_', '') ?? '';
578
- if (!pages.includes(pageName)) return true;
579
-
580
- const percentWatched = (progressBar?.percentDurationWatched ||
581
- progressBar?.startPercent || 0);
582
- return percentWatched <= configRead('hideWatchedVideosThreshold');
583
- });
584
- }
@@ -1,42 +0,0 @@
1
- import { configRead } from "../config.js";
2
-
3
- function attachToVideoPlayer() {
4
- const player = document.querySelector('.html5-video-player');
5
- const video = document.querySelector('video');
6
- if (!player) return setTimeout(attachToVideoPlayer, 500);
7
-
8
- player.addEventListener('onPlaybackStartExternal', () => {
9
- try {
10
- if (window.location.href.indexOf('watch') === -1) return;
11
- const statsForNerds = player.getStatsForNerds();
12
-
13
- const resolutionMatch = statsForNerds.resolution.match(/(\d+)x(\d+)@([\d.]+)/);
14
- const pauseFor = configRead('autoFrameRatePauseVideoFor');
15
-
16
- if (resolutionMatch) {
17
- const fps = resolutionMatch[3];
18
- if (configRead('autoFrameRate') && window.h5vcc && window.h5vcc.tizentube && window.h5vcc.tizentube.SetFrameRate) {
19
- if (pauseFor > 0) {
20
- video.pause();
21
- setTimeout(() => {
22
- video.play();
23
- }, pauseFor);
24
- }
25
- window.h5vcc.tizentube.SetFrameRate(parseFloat(fps));
26
- }
27
- }
28
- } catch (e) {
29
- console.error('Error in auto frame rate handling:', e);
30
- }
31
- });
32
-
33
- window.addEventListener('hashchange', () => {
34
- if (window.location.href.indexOf('watch') > 0) {
35
- if (configRead('autoFrameRate') && window.h5vcc && window.h5vcc.tizentube && window.h5vcc.tizentube.SetFrameRate) {
36
- window.h5vcc.tizentube.SetFrameRate(0);
37
- }
38
- }
39
- });
40
- }
41
-
42
- attachToVideoPlayer();
@@ -1,19 +0,0 @@
1
- // Enable features that aren't enabled by default due to YT seeing the TV as a low-end device
2
- import { configRead, configChangeEmitter } from '../config.js';
3
-
4
- configChangeEmitter.addEventListener('configChange', (event) => {
5
- enableFeatures();
6
- });
7
-
8
-
9
- function enableFeatures() {
10
- if (!window._yttv) return setTimeout(enableFeatures, 250);
11
- const yttvValues = Object.values(window._yttv);
12
-
13
- // Enable preview mode
14
- yttvValues.find(a => a instanceof Map && a.has("ENABLE_PREVIEWS_WITH_SOUND"))?.set("ENABLE_PREVIEWS_WITH_SOUND", configRead('enablePreviews'));
15
- }
16
-
17
- if (document.readyState === 'complete') {
18
- enableFeatures();
19
- } else window.addEventListener('load', enableFeatures);