@oxymoron.13/tizentube-s95b 0.2.2 → 0.3.0

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 (87) hide show
  1. package/README.md +51 -42
  2. package/S95B-CHANGES.txt +12 -4
  3. package/UPSTREAM-README.md +42 -0
  4. package/dist/service.js +3 -0
  5. package/dist/userScript.js +2 -2
  6. package/mods/config.js +117 -0
  7. package/mods/domrect-polyfill.js +104 -0
  8. package/mods/features/adblock.js +584 -0
  9. package/mods/features/autoFrameRate.js +42 -0
  10. package/mods/features/enableFeatures.js +19 -0
  11. package/mods/features/moreSubtitles.js +378 -0
  12. package/mods/features/pictureInPicture.js +198 -0
  13. package/mods/features/preferredVideoQuality.js +101 -0
  14. package/mods/features/s95bCodec.js +45 -0
  15. package/mods/features/s95bI18n.js +28 -0
  16. package/mods/features/s95bMenu.js +137 -0
  17. package/mods/features/s95bOptions.js +30 -0
  18. package/mods/features/s95bPlayback.js +29 -0
  19. package/mods/features/sponsorblock.js +437 -0
  20. package/mods/features/standaloneUserscript.js +107 -0
  21. package/mods/features/updater.js +119 -0
  22. package/mods/features/userAgentSpoofing.js +73 -0
  23. package/mods/features/videoQueuing.js +64 -0
  24. package/mods/package-lock.json +2321 -0
  25. package/mods/package.json +33 -0
  26. package/mods/resolveCommand.js +381 -0
  27. package/mods/rollup.config.js +41 -0
  28. package/mods/spatial-navigation-polyfill.js +1756 -0
  29. package/mods/tiny-sha256.js +99 -0
  30. package/mods/translations/i18nResources.js +11 -0
  31. package/mods/translations/index.js +18 -0
  32. package/mods/translations/language-names.js +981 -0
  33. package/mods/translations/resources/ar.json +308 -0
  34. package/mods/translations/resources/az.json +308 -0
  35. package/mods/translations/resources/bs.json +308 -0
  36. package/mods/translations/resources/cs.json +308 -0
  37. package/mods/translations/resources/de.json +308 -0
  38. package/mods/translations/resources/dk.json +308 -0
  39. package/mods/translations/resources/el.json +308 -0
  40. package/mods/translations/resources/en.json +308 -0
  41. package/mods/translations/resources/es-419.json +308 -0
  42. package/mods/translations/resources/es.json +308 -0
  43. package/mods/translations/resources/fi-FI.json +308 -0
  44. package/mods/translations/resources/fr.json +311 -0
  45. package/mods/translations/resources/hr.json +308 -0
  46. package/mods/translations/resources/hu.json +308 -0
  47. package/mods/translations/resources/id.json +308 -0
  48. package/mods/translations/resources/it.json +308 -0
  49. package/mods/translations/resources/ja.json +308 -0
  50. package/mods/translations/resources/lt.json +308 -0
  51. package/mods/translations/resources/lv.json +308 -0
  52. package/mods/translations/resources/pl.json +308 -0
  53. package/mods/translations/resources/pt-BR.json +308 -0
  54. package/mods/translations/resources/pt-PT.json +308 -0
  55. package/mods/translations/resources/ro.json +310 -0
  56. package/mods/translations/resources/ru.json +308 -0
  57. package/mods/translations/resources/sr-Cyrl.json +308 -0
  58. package/mods/translations/resources/sr-Latn.json +308 -0
  59. package/mods/translations/resources/tlh.json +308 -0
  60. package/mods/translations/resources/tr.json +308 -0
  61. package/mods/translations/resources/uk.json +308 -0
  62. package/mods/translations/resources/vi.json +309 -0
  63. package/mods/translations/resources/zh-CN.json +308 -0
  64. package/mods/translations/resources/zh-TW.json +308 -0
  65. package/mods/translations/update-translations.js +55 -0
  66. package/mods/ui/chapters.js +137 -0
  67. package/mods/ui/clock.js +71 -0
  68. package/mods/ui/customCommandExecution.js +49 -0
  69. package/mods/ui/customGuideAction.js +119 -0
  70. package/mods/ui/customUI.js +192 -0
  71. package/mods/ui/customYTSettings.js +29 -0
  72. package/mods/ui/disableWhosWatching.js +48 -0
  73. package/mods/ui/settings.js +1024 -0
  74. package/mods/ui/sidebarModification.js +179 -0
  75. package/mods/ui/speedUI.js +119 -0
  76. package/mods/ui/theme.js +26 -0
  77. package/mods/ui/ui.css +157 -0
  78. package/mods/ui/ui.js +261 -0
  79. package/mods/ui/ytUI.js +510 -0
  80. package/mods/userScript.js +28 -0
  81. package/mods/utils/ASTParser.js +67 -0
  82. package/mods/utils/innerTubeCalls.js +138 -0
  83. package/package.json +11 -5
  84. package/service/package-lock.json +4193 -0
  85. package/service/package.json +29 -0
  86. package/service/rollup.config.js +47 -0
  87. package/service/service.js +121 -0
@@ -0,0 +1,510 @@
1
+ import resolveCommand from '../resolveCommand.js';
2
+
3
+ function showToast(title, subtitle, thumbnails) {
4
+ const toastCmd = {
5
+ openPopupAction: {
6
+ popupType: 'TOAST',
7
+ popup: {
8
+ overlayToastRenderer: {
9
+ title: {
10
+ simpleText: title
11
+ },
12
+ subtitle: {
13
+ simpleText: subtitle
14
+ }
15
+ }
16
+ }
17
+ }
18
+ }
19
+
20
+ if (thumbnails) {
21
+ toastCmd.openPopupAction.popup.overlayToastRenderer.image.thumbnails = thumbnails;
22
+ }
23
+ resolveCommand(toastCmd);
24
+ }
25
+
26
+ function OverlayPanelHeaderRenderer(title, subtitle, thumbnails) {
27
+ return {
28
+ overlayPanelHeaderRenderer: {
29
+ title: {
30
+ simpleText: title
31
+ },
32
+ subtitle: {
33
+ simpleText: subtitle
34
+ },
35
+ image: {
36
+ thumbnails: thumbnails
37
+ },
38
+ style: "OVERLAY_PANEL_HEADER_STYLE_VIDEO_THUMBNAIL"
39
+ }
40
+ }
41
+ }
42
+
43
+ function Modal(header, content, id, update) {
44
+ const titleSubtitleObj = typeof header === 'string' ? { title: header, subtitle: '' } : header;
45
+ const overlayPanelHeaderRenderer = header.overlayPanelHeaderRenderer || {
46
+ title: {
47
+ simpleText: titleSubtitleObj.title
48
+ }
49
+ };
50
+ const modalCmd = {
51
+ openPopupAction: {
52
+ popupType: 'MODAL',
53
+ popup: {
54
+ overlaySectionRenderer: {
55
+ overlay: {
56
+ overlayTwoPanelRenderer: {
57
+ actionPanel: {
58
+ overlayPanelRenderer: {
59
+ header: {
60
+ overlayPanelHeaderRenderer
61
+ },
62
+ content
63
+ }
64
+ },
65
+ backButton: {
66
+ buttonRenderer: {
67
+ accessibilityData: {
68
+ accessibilityData: {
69
+ label: 'Back'
70
+ }
71
+ },
72
+ command: {
73
+ signalAction: {
74
+ signal: 'POPUP_BACK'
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ },
81
+ dismissalCommand: {
82
+ signalAction: {
83
+ signal: 'POPUP_BACK'
84
+ }
85
+ }
86
+ }
87
+ },
88
+ uniqueId: id
89
+ }
90
+ }
91
+
92
+ if (titleSubtitleObj.subtitle) {
93
+ modalCmd.openPopupAction.popup.overlaySectionRenderer.overlay.overlayTwoPanelRenderer.actionPanel.overlayPanelRenderer.header.overlayPanelHeaderRenderer.subtitle = {
94
+ simpleText: titleSubtitleObj.subtitle
95
+ };
96
+ }
97
+
98
+ if (update) {
99
+ modalCmd.openPopupAction.shouldMatchUniqueId = true;
100
+ modalCmd.openPopupAction.updateAction = true;
101
+ }
102
+
103
+ return modalCmd;
104
+ }
105
+
106
+ function showModal(header, content, id, update) {
107
+ const modalCmd = Modal(header, content, id, update);
108
+
109
+ resolveCommand(modalCmd);
110
+ }
111
+
112
+ function overlayPanelItemListRenderer(items, selectedIndex) {
113
+ return {
114
+ overlayPanelItemListRenderer: {
115
+ items,
116
+ selectedIndex
117
+ }
118
+ }
119
+ };
120
+
121
+ function buttonItem(title, icon, commands) {
122
+ const button = {
123
+ compactLinkRenderer: {
124
+ serviceEndpoint: {
125
+ commandExecutorCommand: {
126
+ commands
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ if (title) {
133
+ button.compactLinkRenderer.title = {
134
+ simpleText: title.title
135
+ }
136
+ }
137
+
138
+ if (title.subtitle) {
139
+ button.compactLinkRenderer.subtitle = {
140
+ simpleText: title.subtitle
141
+ }
142
+ }
143
+
144
+ if (icon) {
145
+ button.compactLinkRenderer.icon = {
146
+ iconType: icon.icon,
147
+ }
148
+ }
149
+
150
+ if (icon && icon.secondaryIcon) {
151
+ button.compactLinkRenderer.secondaryIcon = {
152
+ iconType: icon.secondaryIcon,
153
+ }
154
+ }
155
+
156
+ return button;
157
+ }
158
+
159
+
160
+ function timelyAction(text, icon, command, triggerTimeMs, timeoutMs) {
161
+ return {
162
+ timelyActionRenderer: {
163
+ actionButtons: [
164
+ {
165
+ buttonRenderer: {
166
+ isDisabled: false,
167
+ text: {
168
+ runs: [
169
+ {
170
+ text: text
171
+ }
172
+ ]
173
+ },
174
+ icon: {
175
+ iconType: icon
176
+ },
177
+ trackingParams: null,
178
+ command
179
+ }
180
+ }
181
+ ],
182
+ triggerTimeMs,
183
+ timeoutMs,
184
+ type: ''
185
+ }
186
+ }
187
+
188
+ }
189
+
190
+ function longPressData(data) {
191
+ const isWatchLaterItem = data.watchEndpointData.playlistId === 'WL';
192
+ const watchLaterAction = isWatchLaterItem ? {
193
+ removedVideoId: data.videoId,
194
+ action: 'ACTION_REMOVE_VIDEO_BY_VIDEO_ID'
195
+ } : {
196
+ addedVideoId: data.videoId,
197
+ action: 'ACTION_ADD_VIDEO'
198
+ };
199
+
200
+ return {
201
+ clickTrackingParams: null,
202
+ showMenuCommand: {
203
+ contentId: data.videoId,
204
+ thumbnail: {
205
+ thumbnails: data.thumbnails
206
+ },
207
+ title: {
208
+ simpleText: data.title
209
+ },
210
+ subtitle: {
211
+ simpleText: data.subtitle
212
+ },
213
+ menu: {
214
+ menuRenderer: {
215
+ items: [
216
+ MenuNavigationItemRenderer('Play', {
217
+ clickTrackingParams: null,
218
+ watchEndpoint: data.watchEndpointData
219
+ }),
220
+ MenuServiceItemRenderer(isWatchLaterItem ? 'Remove from Watch Later' : 'Save to Watch Later', {
221
+ clickTrackingParams: null,
222
+ commandMetadata: {
223
+ webCommandMetadata: {
224
+ sendPost: true,
225
+ apiUrl: '/youtubei/v1/browse/edit_playlist'
226
+ }
227
+ },
228
+ playlistEditEndpoint: {
229
+ playlistId: 'WL',
230
+ actions: [watchLaterAction]
231
+ }
232
+ }),
233
+ MenuNavigationItemRenderer('Save to Playlist', {
234
+ clickTrackingParams: null,
235
+ addToPlaylistEndpoint: {
236
+ videoId: data.videoId
237
+ }
238
+ }),
239
+ MenuServiceItemRenderer('Add to Queue', {
240
+ clickTrackingParams: null,
241
+ playlistEditEndpoint: {
242
+ customAction: {
243
+ action: 'ADD_TO_QUEUE',
244
+ parameters: data.item
245
+ }
246
+ }
247
+ }),
248
+ MenuServiceItemRenderer('Go To Channel', {
249
+ clickTrackingParams: null,
250
+ playlistEditEndpoint: {
251
+ customAction: {
252
+ action: 'GO_TO_CHANNEL',
253
+ parameters: data.item
254
+ }
255
+ }
256
+ }),
257
+ ],
258
+ trackingParams: null,
259
+ accessibility: {
260
+ accessibilityData: {
261
+ label: 'Video options'
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ function MenuServiceItemRenderer(text, serviceEndpoint) {
271
+ return {
272
+ menuServiceItemRenderer: {
273
+ text: {
274
+ runs: [
275
+ {
276
+ text
277
+ }
278
+ ]
279
+ },
280
+ serviceEndpoint,
281
+ trackingParams: null
282
+ }
283
+ };
284
+ }
285
+
286
+ function MenuNavigationItemRenderer(text, navigateEndpoint) {
287
+ return {
288
+ menuNavigationItemRenderer: {
289
+ text: {
290
+ runs: [
291
+ {
292
+ text
293
+ }
294
+ ]
295
+ },
296
+ navigationEndpoint: navigateEndpoint,
297
+ trackingParams: null
298
+ }
299
+ }
300
+ }
301
+
302
+ function SettingsCategory(categoryId, items, title) {
303
+ const category = {
304
+ settingCategoryCollectionRenderer: {
305
+ items,
306
+ categoryId,
307
+ focused: false,
308
+ trackingParams: "null"
309
+ }
310
+ }
311
+
312
+ if (title) {
313
+ category.settingCategoryCollectionRenderer.title = {
314
+ runs: [
315
+ {
316
+ text: title
317
+ }
318
+ ]
319
+ };
320
+ }
321
+
322
+ return category;
323
+ }
324
+
325
+ function SettingActionRenderer(title, itemId, serviceEndpoint, summary, thumbnail) {
326
+ return {
327
+ settingActionRenderer: {
328
+ title: {
329
+ runs: [
330
+ {
331
+ text: title
332
+ }
333
+ ]
334
+ },
335
+ serviceEndpoint,
336
+ summary: {
337
+ runs: [
338
+ {
339
+ text: summary
340
+ }
341
+ ]
342
+ },
343
+ trackingParams: "null",
344
+ actionLabel: {
345
+ runs: [
346
+ {
347
+ text: title
348
+ }
349
+ ]
350
+ },
351
+ itemId,
352
+ thumbnail: {
353
+ thumbnails: [
354
+ {
355
+ url: thumbnail
356
+ }
357
+ ]
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ function scrollPaneRenderer(items) {
364
+ return {
365
+ scrollPaneRenderer: {
366
+ content: scrollPaneItemListRenderer(items)
367
+ }
368
+ }
369
+ }
370
+
371
+ function scrollPaneItemListRenderer(items) {
372
+ return {
373
+ scrollPaneItemListRenderer: {
374
+ items
375
+ }
376
+ }
377
+ }
378
+
379
+ function overlayMessageRenderer(simpleText) {
380
+ return {
381
+ overlayMessageRenderer: {
382
+ title: {
383
+ simpleText
384
+ }
385
+ }
386
+ }
387
+ }
388
+
389
+ function ShelfRenderer(simpleText, items, selectedIndex = 0) {
390
+ return {
391
+ shelfRenderer: {
392
+ shelfHeaderRenderer: {
393
+ title: {
394
+ simpleText
395
+ }
396
+ },
397
+ tvhtml5ShelfRendererType: "TVHTML5_SHELF_RENDERER_TYPE_GRID",
398
+ content: {
399
+ horizontalListRenderer: {
400
+ items,
401
+ selectedIndex,
402
+ visibleItemCount: 3
403
+ }
404
+ }
405
+ }
406
+ }
407
+ }
408
+
409
+ function TileRenderer(simpleText, onSelectCommand) {
410
+ return {
411
+ tileRenderer: {
412
+ contentType: "TILE_CONTENT_TYPE_VIDEO",
413
+ metadata: {
414
+ tileMetadataRenderer: {
415
+ title: {
416
+ simpleText
417
+ }
418
+ }
419
+ },
420
+ onSelectCommand,
421
+ style: "TILE_STYLE_YTLR_DEFAULT"
422
+ }
423
+ }
424
+ }
425
+
426
+ function QrCodeRenderer(url) {
427
+ return {
428
+ qrCodeRenderer: {
429
+ qrCodeImage: {
430
+ thumbnails: [
431
+ {
432
+ url
433
+ }
434
+ ]
435
+ },
436
+ style: "QR_CODE_RENDERER_STYLE_ATA_SIDESHEET",
437
+ trackingParams: null
438
+ }
439
+ }
440
+ }
441
+
442
+ function ButtonRenderer(disabled, text, iconType, command) {
443
+ return {
444
+ isDisabled: disabled,
445
+ text: {
446
+ runs: [
447
+ {
448
+ text: text
449
+ }
450
+ ]
451
+ },
452
+ icon: {
453
+ iconType
454
+ },
455
+ command: command,
456
+ trackingParams: null
457
+ };
458
+ }
459
+
460
+ function GuideEntryRenderer(simpleText, navigationEndpoint, icon, thumbnail) {
461
+ const guideEntryRenderer = {
462
+ guideEntryRenderer: {
463
+ navigationEndpoint,
464
+ trackingParams: null,
465
+ formattedTitle: {
466
+ simpleText
467
+ }
468
+ }
469
+ }
470
+
471
+ if (icon) {
472
+ guideEntryRenderer.guideEntryRenderer.icon = {
473
+ iconType: icon
474
+ }
475
+ }
476
+
477
+ if (thumbnail) {
478
+ guideEntryRenderer.guideEntryRenderer.thumbnail = {
479
+ thumbnails: [
480
+ {
481
+ url: thumbnail
482
+ }
483
+ ]
484
+ }
485
+ }
486
+
487
+ return guideEntryRenderer;
488
+ }
489
+
490
+ export {
491
+ showToast,
492
+ Modal,
493
+ OverlayPanelHeaderRenderer,
494
+ showModal,
495
+ buttonItem,
496
+ overlayPanelItemListRenderer,
497
+ overlayMessageRenderer,
498
+ timelyAction,
499
+ scrollPaneRenderer,
500
+ scrollPaneItemListRenderer,
501
+ longPressData,
502
+ MenuServiceItemRenderer,
503
+ SettingsCategory,
504
+ SettingActionRenderer,
505
+ ShelfRenderer,
506
+ TileRenderer,
507
+ QrCodeRenderer,
508
+ ButtonRenderer,
509
+ GuideEntryRenderer
510
+ }
@@ -0,0 +1,28 @@
1
+ import initPatches from "./features/standaloneUserscript.js";
2
+ if (window.location.hostname === 'localhost') {
3
+ initPatches();
4
+ }
5
+ import "./features/userAgentSpoofing.js";
6
+ import "whatwg-fetch";
7
+ import "core-js/proposals/object-getownpropertydescriptors";
8
+
9
+ import './translations/index.js'
10
+ import "./domrect-polyfill";
11
+ import "./features/adblock.js";
12
+ import "./features/s95bMenu.js";
13
+ import "./features/sponsorblock.js";
14
+ import "./ui/ui.js";
15
+ import "./ui/speedUI.js";
16
+ import "./ui/theme.js";
17
+ import "./ui/settings.js";
18
+ import "./ui/disableWhosWatching.js";
19
+ import "./features/moreSubtitles.js";
20
+ import "./features/updater.js";
21
+ import "./features/pictureInPicture.js";
22
+ import "./features/preferredVideoQuality.js";
23
+ import "./features/videoQueuing.js";
24
+ import "./features/enableFeatures.js";
25
+ import "./ui/customUI.js";
26
+ import "./ui/customGuideAction.js";
27
+ import "./features/autoFrameRate.js";
28
+ import "./ui/clock.js";
@@ -0,0 +1,67 @@
1
+ // AST Parser for TizenTube, used for finding code patterns
2
+ // You may call me insane for this.
3
+
4
+ import esprima from 'esprima';
5
+ import estraverse from 'estraverse';
6
+
7
+ // Extract assignment RHS sources and inner returned functions (IIFEs)
8
+ export function extractAssignedFunctions(code) {
9
+ const original = code;
10
+ let wrapOffset = 0;
11
+ let ast;
12
+
13
+ try {
14
+ ast = esprima.parse(code, { range: true, tolerant: true, sourceType: 'script' });
15
+ } catch (e1) {
16
+ try {
17
+ const wrapped = '(' + code + ')';
18
+ ast = esprima.parse(wrapped, { range: true, tolerant: true, sourceType: 'script' });
19
+ wrapOffset = 1;
20
+ } catch (e2) {
21
+ try {
22
+ ast = esprima.parse(code, { range: true, tolerant: true, sourceType: 'module' });
23
+ } catch (e3) {
24
+ try {
25
+ const wrapped2 = '(' + code + ')';
26
+ ast = esprima.parse(wrapped2, { range: true, tolerant: true, sourceType: 'module' });
27
+ wrapOffset = 1;
28
+ } catch (e4) {
29
+ throw e1;
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ const out = [];
36
+
37
+ estraverse.traverse(ast, {
38
+ enter: function (node) {
39
+ if (node.type !== 'AssignmentExpression') return;
40
+ const rhs = node.right;
41
+ if (!rhs || !rhs.range) return;
42
+ const rhsSrc = original.slice(rhs.range[0] - wrapOffset, rhs.range[1] - wrapOffset);
43
+ let inner = null;
44
+
45
+ if (rhs.type === 'CallExpression' && rhs.callee && rhs.callee.type === 'FunctionExpression' && rhs.callee.body) {
46
+ let stm = rhs.callee.body.body || [];
47
+ for (let i = 0; i < stm.length; i++) {
48
+ let s = stm[i];
49
+ if (s.type === 'ReturnStatement' && s.argument && s.argument.range) {
50
+ inner = original.slice(s.argument.range[0] - wrapOffset, s.argument.range[1] - wrapOffset);
51
+ break;
52
+ }
53
+ }
54
+ } else if (rhs.type === 'FunctionExpression' || rhs.type === 'ArrowFunctionExpression') {
55
+ inner = rhsSrc;
56
+ }
57
+
58
+ out.push({
59
+ left: node.left && node.left.range ? original.slice(node.left.range[0] - wrapOffset, node.left.range[1] - wrapOffset) : null,
60
+ rhs: rhsSrc,
61
+ returned: inner
62
+ });
63
+ }
64
+ });
65
+
66
+ return out;
67
+ }