@oxymoron.13/tizentube-s95b 0.2.0 → 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.
- package/README.md +42 -49
- package/S95B-CHANGES.txt +3 -9
- package/dist/userScript.js +2 -2
- package/package.json +5 -11
- package/UPSTREAM-README.md +0 -42
- package/mods/config.js +0 -117
- package/mods/domrect-polyfill.js +0 -104
- package/mods/features/adblock.js +0 -584
- package/mods/features/autoFrameRate.js +0 -42
- package/mods/features/enableFeatures.js +0 -19
- package/mods/features/moreSubtitles.js +0 -378
- package/mods/features/pictureInPicture.js +0 -198
- package/mods/features/preferredVideoQuality.js +0 -101
- package/mods/features/s95bCodec.js +0 -45
- package/mods/features/s95bI18n.js +0 -34
- package/mods/features/s95bMenu.js +0 -223
- package/mods/features/sponsorblock.js +0 -437
- package/mods/features/standaloneUserscript.js +0 -107
- package/mods/features/updater.js +0 -119
- package/mods/features/userAgentSpoofing.js +0 -73
- package/mods/features/videoQueuing.js +0 -64
- package/mods/package-lock.json +0 -2321
- package/mods/package.json +0 -33
- package/mods/resolveCommand.js +0 -376
- package/mods/rollup.config.js +0 -41
- package/mods/spatial-navigation-polyfill.js +0 -1756
- package/mods/tiny-sha256.js +0 -99
- package/mods/translations/i18nResources.js +0 -11
- package/mods/translations/index.js +0 -18
- package/mods/translations/language-names.js +0 -981
- package/mods/translations/resources/ar.json +0 -308
- package/mods/translations/resources/az.json +0 -308
- package/mods/translations/resources/bs.json +0 -308
- package/mods/translations/resources/cs.json +0 -308
- package/mods/translations/resources/de.json +0 -308
- package/mods/translations/resources/dk.json +0 -308
- package/mods/translations/resources/el.json +0 -308
- package/mods/translations/resources/en.json +0 -308
- package/mods/translations/resources/es-419.json +0 -308
- package/mods/translations/resources/es.json +0 -308
- package/mods/translations/resources/fi-FI.json +0 -308
- package/mods/translations/resources/fr.json +0 -311
- package/mods/translations/resources/hr.json +0 -308
- package/mods/translations/resources/hu.json +0 -308
- package/mods/translations/resources/id.json +0 -308
- package/mods/translations/resources/it.json +0 -308
- package/mods/translations/resources/ja.json +0 -308
- package/mods/translations/resources/lt.json +0 -308
- package/mods/translations/resources/lv.json +0 -308
- package/mods/translations/resources/pl.json +0 -308
- package/mods/translations/resources/pt-BR.json +0 -308
- package/mods/translations/resources/pt-PT.json +0 -308
- package/mods/translations/resources/ro.json +0 -310
- package/mods/translations/resources/ru.json +0 -308
- package/mods/translations/resources/sr-Cyrl.json +0 -308
- package/mods/translations/resources/sr-Latn.json +0 -308
- package/mods/translations/resources/tlh.json +0 -308
- package/mods/translations/resources/tr.json +0 -308
- package/mods/translations/resources/uk.json +0 -308
- package/mods/translations/resources/vi.json +0 -309
- package/mods/translations/resources/zh-CN.json +0 -308
- package/mods/translations/resources/zh-TW.json +0 -308
- package/mods/translations/update-translations.js +0 -55
- package/mods/ui/chapters.js +0 -137
- package/mods/ui/clock.js +0 -71
- package/mods/ui/customCommandExecution.js +0 -49
- package/mods/ui/customGuideAction.js +0 -119
- package/mods/ui/customUI.js +0 -192
- package/mods/ui/customYTSettings.js +0 -29
- package/mods/ui/disableWhosWatching.js +0 -48
- package/mods/ui/settings.js +0 -1024
- package/mods/ui/sidebarModification.js +0 -179
- package/mods/ui/speedUI.js +0 -119
- package/mods/ui/theme.js +0 -26
- package/mods/ui/ui.css +0 -157
- package/mods/ui/ui.js +0 -261
- package/mods/ui/ytUI.js +0 -510
- package/mods/userScript.js +0 -28
- package/mods/utils/ASTParser.js +0 -67
- package/mods/utils/innerTubeCalls.js +0 -138
- package/service/package-lock.json +0 -4193
- package/service/package.json +0 -29
- package/service/rollup.config.js +0 -47
- package/service/service.js +0 -118
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
import sha256 from '../tiny-sha256.js';
|
|
2
|
-
import { configRead } from '../config.js';
|
|
3
|
-
import { showToast } from '../ui/ytUI.js';
|
|
4
|
-
import { t } from 'i18next';
|
|
5
|
-
|
|
6
|
-
// Copied from https://github.com/ajayyy/SponsorBlock/blob/da1a535de784540ee10166a75a3eb8537073838c/src/config.ts#L113-L134
|
|
7
|
-
const barTypes = {
|
|
8
|
-
sponsor: {
|
|
9
|
-
color: '#00d400',
|
|
10
|
-
opacity: '0.7',
|
|
11
|
-
name: t('sponsorblock.segments.sponsor') || 'sponsored segment'
|
|
12
|
-
},
|
|
13
|
-
intro: {
|
|
14
|
-
color: '#00ffff',
|
|
15
|
-
opacity: '0.7',
|
|
16
|
-
name: t('sponsorblock.segments.intro') || 'intro'
|
|
17
|
-
},
|
|
18
|
-
outro: {
|
|
19
|
-
color: '#0202ed',
|
|
20
|
-
opacity: '0.7',
|
|
21
|
-
name: t('sponsorblock.segments.outro') || 'outro'
|
|
22
|
-
},
|
|
23
|
-
interaction: {
|
|
24
|
-
color: '#cc00ff',
|
|
25
|
-
opacity: '0.7',
|
|
26
|
-
name: t('sponsorblock.segments.interaction') || 'interaction reminder'
|
|
27
|
-
},
|
|
28
|
-
selfpromo: {
|
|
29
|
-
color: '#ffff00',
|
|
30
|
-
opacity: '0.7',
|
|
31
|
-
name: t('sponsorblock.segments.selfpromo') || 'self-promotion'
|
|
32
|
-
},
|
|
33
|
-
preview: {
|
|
34
|
-
color: '#008fd6',
|
|
35
|
-
opacity: '0.7',
|
|
36
|
-
name: t('sponsorblock.segments.preview') || 'recap or preview'
|
|
37
|
-
},
|
|
38
|
-
filler: {
|
|
39
|
-
color: "#7300FF",
|
|
40
|
-
opacity: "0.9",
|
|
41
|
-
name: t('sponsorblock.segments.filler') || 'tangents'
|
|
42
|
-
},
|
|
43
|
-
music_offtopic: {
|
|
44
|
-
color: '#ff9900',
|
|
45
|
-
opacity: '0.7',
|
|
46
|
-
name: t('sponsorblock.segments.music_offtopic') || 'non-music part'
|
|
47
|
-
},
|
|
48
|
-
poi_highlight: {
|
|
49
|
-
color: '#9b044c',
|
|
50
|
-
opacity: '0.7',
|
|
51
|
-
name: t('sponsorblock.segments.poi_highlight') || 'highlight'
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const sponsorblockAPI = 'https://sponsor.ajay.app/api';
|
|
56
|
-
|
|
57
|
-
class SponsorBlockHandler {
|
|
58
|
-
video = null;
|
|
59
|
-
active = true;
|
|
60
|
-
|
|
61
|
-
attachVideoTimeout = null;
|
|
62
|
-
nextSkipTimeout = null;
|
|
63
|
-
sliderInterval = null;
|
|
64
|
-
|
|
65
|
-
observer = null;
|
|
66
|
-
scheduleSkipHandler = null;
|
|
67
|
-
durationChangeHandler = null;
|
|
68
|
-
segments = null;
|
|
69
|
-
skippableCategories = [];
|
|
70
|
-
manualSkippableCategories = [];
|
|
71
|
-
skippedCategories = new Map();
|
|
72
|
-
|
|
73
|
-
constructor(videoID) {
|
|
74
|
-
this.videoID = videoID;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async init() {
|
|
78
|
-
const videoHash = sha256(this.videoID).substring(0, 4);
|
|
79
|
-
const categories = [
|
|
80
|
-
'sponsor',
|
|
81
|
-
'intro',
|
|
82
|
-
'outro',
|
|
83
|
-
'interaction',
|
|
84
|
-
'selfpromo',
|
|
85
|
-
'preview',
|
|
86
|
-
'filler',
|
|
87
|
-
'music_offtopic',
|
|
88
|
-
'poi_highlight'
|
|
89
|
-
];
|
|
90
|
-
const resp = await fetch(
|
|
91
|
-
`${sponsorblockAPI}/skipSegments/${videoHash}?categories=${encodeURIComponent(
|
|
92
|
-
JSON.stringify(categories)
|
|
93
|
-
)}`
|
|
94
|
-
);
|
|
95
|
-
const results = await resp.json();
|
|
96
|
-
|
|
97
|
-
const result = results.find((v) => v.videoID === this.videoID);
|
|
98
|
-
console.info(this.videoID, 'Got it:', result);
|
|
99
|
-
|
|
100
|
-
if (!result || !result.segments || !result.segments.length) {
|
|
101
|
-
console.info(this.videoID, 'No segments found.');
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
this.segments = result.segments;
|
|
106
|
-
this.manualSkippableCategories = configRead('sponsorBlockManualSkips');
|
|
107
|
-
this.skippableCategories = this.getSkippableCategories();
|
|
108
|
-
|
|
109
|
-
this.scheduleSkipHandler = () => {
|
|
110
|
-
const slider = document.querySelector('div[idomkey="slider"]');
|
|
111
|
-
const sliderRect = slider?.getBoundingClientRect();
|
|
112
|
-
const isOldUI = !document.querySelector('div[idomkey="Metadata-Section"]');
|
|
113
|
-
if (isOldUI && sliderRect) {
|
|
114
|
-
this.segmentsoverlay.style.setProperty('top', `${sliderRect.top}px`, 'important');
|
|
115
|
-
}
|
|
116
|
-
this.scheduleSkip();
|
|
117
|
-
}
|
|
118
|
-
this.durationChangeHandler = () => this.buildOverlay();
|
|
119
|
-
|
|
120
|
-
this.attachVideo();
|
|
121
|
-
this.buildOverlay();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
getSkippableCategories() {
|
|
125
|
-
const skippableCategories = [];
|
|
126
|
-
if (configRead('enableSponsorBlockSponsor')) {
|
|
127
|
-
skippableCategories.push('sponsor');
|
|
128
|
-
}
|
|
129
|
-
if (configRead('enableSponsorBlockIntro')) {
|
|
130
|
-
skippableCategories.push('intro');
|
|
131
|
-
}
|
|
132
|
-
if (configRead('enableSponsorBlockOutro')) {
|
|
133
|
-
skippableCategories.push('outro');
|
|
134
|
-
}
|
|
135
|
-
if (configRead('enableSponsorBlockInteraction')) {
|
|
136
|
-
skippableCategories.push('interaction');
|
|
137
|
-
}
|
|
138
|
-
if (configRead('enableSponsorBlockSelfPromo')) {
|
|
139
|
-
skippableCategories.push('selfpromo');
|
|
140
|
-
}
|
|
141
|
-
if (configRead('enableSponsorBlockPreview')) {
|
|
142
|
-
skippableCategories.push('preview');
|
|
143
|
-
}
|
|
144
|
-
if (configRead('enableSponsorBlockFiller')) {
|
|
145
|
-
skippableCategories.push('filler');
|
|
146
|
-
}
|
|
147
|
-
if (configRead('enableSponsorBlockMusicOfftopic')) {
|
|
148
|
-
skippableCategories.push('music_offtopic');
|
|
149
|
-
}
|
|
150
|
-
return skippableCategories;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
attachVideo() {
|
|
154
|
-
clearTimeout(this.attachVideoTimeout);
|
|
155
|
-
this.attachVideoTimeout = null;
|
|
156
|
-
|
|
157
|
-
this.video = document.querySelector('video');
|
|
158
|
-
if (!this.video) {
|
|
159
|
-
console.info(this.videoID, 'No video yet...');
|
|
160
|
-
this.attachVideoTimeout = setTimeout(() => this.attachVideo(), 100);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
console.info(this.videoID, 'Video found, binding...');
|
|
165
|
-
|
|
166
|
-
this.video.addEventListener('play', this.scheduleSkipHandler);
|
|
167
|
-
this.video.addEventListener('pause', this.scheduleSkipHandler);
|
|
168
|
-
this.video.addEventListener('timeupdate', this.scheduleSkipHandler);
|
|
169
|
-
this.video.addEventListener('durationchange', this.durationChangeHandler);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
buildOverlay() {
|
|
173
|
-
if (this.segmentsoverlay) {
|
|
174
|
-
console.info('Overlay already built');
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (!this.video || !this.video.duration) {
|
|
179
|
-
console.info('No video duration yet');
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const videoDuration = this.video.duration;
|
|
184
|
-
const slider = document.querySelector('div[idomkey="slider"]');
|
|
185
|
-
if (!slider) return setTimeout(() => this.buildOverlay(), 100);
|
|
186
|
-
|
|
187
|
-
this.segmentsoverlay = document.createElement('div');
|
|
188
|
-
|
|
189
|
-
this.segmentsoverlay.classList.add('ytLrProgressBarSlider', 'ytLrProgressBarSliderRectangularProgressBar');
|
|
190
|
-
this.segmentsoverlay.style.setProperty('z-index', '10', 'important');
|
|
191
|
-
this.segmentsoverlay.style.setProperty('background-color', 'rgba(0, 0, 0, 0)', 'important');
|
|
192
|
-
this.segmentsoverlay.style.setProperty('width', '72rem', 'important');
|
|
193
|
-
this.segmentsoverlay.style.setProperty('left', '4rem', 'important');
|
|
194
|
-
const sliderRect = slider.getBoundingClientRect();
|
|
195
|
-
if (!slider.classList.contains('ytLrProgressBarSlider')) {
|
|
196
|
-
for (let i = 0; i < slider.classList.length; i++) {
|
|
197
|
-
this.segmentsoverlay.classList.add(slider.classList[i]);
|
|
198
|
-
}
|
|
199
|
-
this.segmentsoverlay.style.setProperty('height', `${sliderRect.height}px`, 'important');
|
|
200
|
-
this.segmentsoverlay.style.setProperty('bottom', `${sliderRect.bottom - sliderRect.top}px`, 'important');
|
|
201
|
-
}
|
|
202
|
-
this.segments.forEach((segment) => {
|
|
203
|
-
const [start, end] = segment.segment;
|
|
204
|
-
const barType = barTypes[segment.category] || {
|
|
205
|
-
color: 'blue',
|
|
206
|
-
opacity: 0.7
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const leftPercent = videoDuration ? (100.0 * start) / videoDuration : 0;
|
|
210
|
-
const widthPercent = videoDuration ? (100.0 * (end - start)) / videoDuration : 0;
|
|
211
|
-
|
|
212
|
-
const elm = document.createElement('div');
|
|
213
|
-
elm.style.setProperty('background-color', barType.color, 'important');
|
|
214
|
-
elm.style.setProperty('opacity', barType.opacity, 'important');
|
|
215
|
-
elm.style.setProperty('height', '100%', 'important');
|
|
216
|
-
elm.style.setProperty('width', `${segment.category === 'poi_highlight' ? 1 : widthPercent}%`, 'important');
|
|
217
|
-
elm.style.setProperty('left', `${leftPercent}%`, 'important');
|
|
218
|
-
elm.style.setProperty('position', 'absolute', 'important');
|
|
219
|
-
console.info('Generated element', elm, 'from', segment);
|
|
220
|
-
this.segmentsoverlay.appendChild(elm);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
this.observer = new MutationObserver((mutations) => {
|
|
224
|
-
mutations.forEach((m) => {
|
|
225
|
-
if (m.removedNodes) {
|
|
226
|
-
for (const node of m.removedNodes) {
|
|
227
|
-
if (node === this.segmentsoverlay) {
|
|
228
|
-
console.info('bringing back segments overlay');
|
|
229
|
-
this.slider.appendChild(this.segmentsoverlay);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (document.querySelector('ytlr-progress-bar').getAttribute('hybridnavfocusable') === 'false') {
|
|
235
|
-
this.segmentsoverlay.style.setProperty('display', 'none', 'important');
|
|
236
|
-
} else {
|
|
237
|
-
this.segmentsoverlay.style.setProperty('display', 'block', 'important');
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
this.sliderInterval = setInterval(() => {
|
|
243
|
-
this.slider = document.querySelector('ytlr-redux-connect-ytlr-progress-bar');
|
|
244
|
-
if (this.slider) {
|
|
245
|
-
clearInterval(this.sliderInterval);
|
|
246
|
-
this.sliderInterval = null;
|
|
247
|
-
this.observer.observe(this.slider, {
|
|
248
|
-
childList: true,
|
|
249
|
-
subtree: true
|
|
250
|
-
});
|
|
251
|
-
this.slider.appendChild(this.segmentsoverlay);
|
|
252
|
-
}
|
|
253
|
-
}, 500);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
scheduleSkip() {
|
|
257
|
-
clearTimeout(this.nextSkipTimeout);
|
|
258
|
-
this.nextSkipTimeout = null;
|
|
259
|
-
|
|
260
|
-
if (!this.active) {
|
|
261
|
-
console.info(this.videoID, 'No longer active, ignoring...');
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (this.video.paused) {
|
|
266
|
-
console.info(this.videoID, 'Currently paused, ignoring...');
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Sometimes timeupdate event (that calls scheduleSkip) gets fired right before
|
|
271
|
-
// already scheduled skip routine below. Let's just look back a little bit
|
|
272
|
-
// and, in worst case, perform a skip at negative interval (immediately)...
|
|
273
|
-
const nextSegments = this.segments.filter(
|
|
274
|
-
(seg) =>
|
|
275
|
-
seg.segment[0] > this.video.currentTime - 0.3 &&
|
|
276
|
-
seg.segment[1] > this.video.currentTime - 0.3
|
|
277
|
-
);
|
|
278
|
-
nextSegments.sort((s1, s2) => s1.segment[0] - s2.segment[0]);
|
|
279
|
-
|
|
280
|
-
if (!nextSegments.length) {
|
|
281
|
-
console.info(this.videoID, 'No more segments');
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const [segment] = nextSegments;
|
|
286
|
-
const [start, end] = segment.segment;
|
|
287
|
-
console.info(
|
|
288
|
-
this.videoID,
|
|
289
|
-
'Scheduling skip of',
|
|
290
|
-
segment,
|
|
291
|
-
'in',
|
|
292
|
-
start - this.video.currentTime
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
this.nextSkipTimeout = setTimeout(() => {
|
|
296
|
-
if (this.video.paused) {
|
|
297
|
-
console.info(this.videoID, 'Currently paused, ignoring...');
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
if (!this.skippableCategories.includes(segment.category)) {
|
|
301
|
-
console.info(
|
|
302
|
-
this.videoID,
|
|
303
|
-
'Segment',
|
|
304
|
-
segment.category,
|
|
305
|
-
'is not skippable, ignoring...'
|
|
306
|
-
);
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const skipName = barTypes[segment.category]?.name || segment.category;
|
|
311
|
-
console.info(this.videoID, 'Skipping', segment);
|
|
312
|
-
if (!this.manualSkippableCategories.includes(segment.category)) {
|
|
313
|
-
const wasSkippedBefore = this.skippedCategories.get(segment.UUID)
|
|
314
|
-
if (wasSkippedBefore) {
|
|
315
|
-
wasSkippedBefore.count++;
|
|
316
|
-
wasSkippedBefore.lastSkipped = Date.now();
|
|
317
|
-
this.skippedCategories.set(segment.UUID, wasSkippedBefore);
|
|
318
|
-
|
|
319
|
-
if (wasSkippedBefore.lastSkipped - wasSkippedBefore.firstSkipped < 1000) {
|
|
320
|
-
if (!wasSkippedBefore.hasShownToast) {
|
|
321
|
-
if (configRead('enableSponsorBlockToasts')) {
|
|
322
|
-
showToast('SponsorBlock', t('sponsorblock.toasts.notSkipping', { segment: skipName, count: wasSkippedBefore.count }));
|
|
323
|
-
}
|
|
324
|
-
wasSkippedBefore.hasShownToast = true;
|
|
325
|
-
this.skippedCategories.set(segment.UUID, wasSkippedBefore);
|
|
326
|
-
}
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
this.skippedCategories.set(segment.UUID, {
|
|
331
|
-
count: 1,
|
|
332
|
-
firstSkipped: Date.now(),
|
|
333
|
-
lastSkipped: Date.now(),
|
|
334
|
-
hasShownToast: false
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
if (configRead('enableSponsorBlockToasts')) {
|
|
338
|
-
showToast('SponsorBlock', t('sponsorblock.toasts.skipping', { segment: skipName }));
|
|
339
|
-
}
|
|
340
|
-
if (this.video.duration - end < 1) {
|
|
341
|
-
this.video.currentTime = end - 1;
|
|
342
|
-
} else this.video.currentTime = end;
|
|
343
|
-
this.scheduleSkip();
|
|
344
|
-
}
|
|
345
|
-
}, (start - this.video.currentTime) * 1000);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
destroy() {
|
|
349
|
-
console.info(this.videoID, 'Destroying');
|
|
350
|
-
|
|
351
|
-
this.active = false;
|
|
352
|
-
|
|
353
|
-
if (this.nextSkipTimeout) {
|
|
354
|
-
clearTimeout(this.nextSkipTimeout);
|
|
355
|
-
this.nextSkipTimeout = null;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (this.attachVideoTimeout) {
|
|
359
|
-
clearTimeout(this.attachVideoTimeout);
|
|
360
|
-
this.attachVideoTimeout = null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (this.sliderInterval) {
|
|
364
|
-
clearInterval(this.sliderInterval);
|
|
365
|
-
this.sliderInterval = null;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (this.observer) {
|
|
369
|
-
this.observer.disconnect();
|
|
370
|
-
this.observer = null;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (this.segmentsoverlay) {
|
|
374
|
-
this.segmentsoverlay.remove();
|
|
375
|
-
this.segmentsoverlay = null;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (this.video) {
|
|
379
|
-
this.video.removeEventListener('play', this.scheduleSkipHandler);
|
|
380
|
-
this.video.removeEventListener('pause', this.scheduleSkipHandler);
|
|
381
|
-
this.video.removeEventListener('timeupdate', this.scheduleSkipHandler);
|
|
382
|
-
this.video.removeEventListener(
|
|
383
|
-
'durationchange',
|
|
384
|
-
this.durationChangeHandler
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
this.skippedCategories.clear();
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// When this global variable was declared using let and two consecutive hashchange
|
|
393
|
-
// events were fired (due to bubbling? not sure...) the second call handled below
|
|
394
|
-
// would not see the value change from first call, and that would cause multiple
|
|
395
|
-
// SponsorBlockHandler initializations... This has been noticed on Chromium 38.
|
|
396
|
-
// This either reveals some bug in chromium/webpack/babel scope handling, or
|
|
397
|
-
// shows my lack of understanding of javascript. (or both)
|
|
398
|
-
window.sponsorblock = null;
|
|
399
|
-
|
|
400
|
-
window.addEventListener(
|
|
401
|
-
'hashchange',
|
|
402
|
-
() => {
|
|
403
|
-
const newURL = new URL(location.hash.substring(1), location.href);
|
|
404
|
-
// A hack, but it works, so...
|
|
405
|
-
const videoID = newURL.search.replace('?v=', '').split('&')[0];
|
|
406
|
-
const needsReload =
|
|
407
|
-
videoID &&
|
|
408
|
-
(!window.sponsorblock || window.sponsorblock.videoID != videoID);
|
|
409
|
-
|
|
410
|
-
console.info(
|
|
411
|
-
'hashchange',
|
|
412
|
-
videoID,
|
|
413
|
-
window.sponsorblock,
|
|
414
|
-
window.sponsorblock ? window.sponsorblock.videoID : null,
|
|
415
|
-
needsReload
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
if (needsReload) {
|
|
419
|
-
if (window.sponsorblock) {
|
|
420
|
-
try {
|
|
421
|
-
window.sponsorblock.destroy();
|
|
422
|
-
} catch (err) {
|
|
423
|
-
console.warn('window.sponsorblock.destroy() failed!', err);
|
|
424
|
-
}
|
|
425
|
-
window.sponsorblock = null;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (configRead('enableSponsorBlock')) {
|
|
429
|
-
window.sponsorblock = new SponsorBlockHandler(videoID);
|
|
430
|
-
window.sponsorblock.init();
|
|
431
|
-
} else {
|
|
432
|
-
console.info('SponsorBlock disabled, not loading');
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
},
|
|
436
|
-
false
|
|
437
|
-
);
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
function redirectUrl(originalUrl) {
|
|
2
|
-
if (!originalUrl) return originalUrl;
|
|
3
|
-
|
|
4
|
-
try {
|
|
5
|
-
if (typeof originalUrl === 'string' && originalUrl.startsWith('//')) originalUrl = originalUrl.replace('//', 'https://')
|
|
6
|
-
const url = new URL(originalUrl, window.location.origin);
|
|
7
|
-
const hostname = url.hostname;
|
|
8
|
-
|
|
9
|
-
if (hostname === 'youtube.com' || hostname === 'www.youtube.com') {
|
|
10
|
-
url.protocol = 'http:';
|
|
11
|
-
url.host = 'localhost:8099';
|
|
12
|
-
return url.toString();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (hostname.endsWith('googlevideo.com') || hostname.endsWith('youtube.com')
|
|
16
|
-
|| hostname.endsWith('gstatic.com') || hostname.endsWith('.google.com')
|
|
17
|
-
|| hostname.endsWith('.googleapis.com') || hostname.endsWith('googleusercontent.com')
|
|
18
|
-
|| hostname.endsWith('.ggpht.com')) {
|
|
19
|
-
return 'http://localhost:8099/cors-bypass/' + url.toString();
|
|
20
|
-
}
|
|
21
|
-
} catch (e) {
|
|
22
|
-
console.error('Failed to parse URL during interception:', e);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return originalUrl;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default function () {
|
|
29
|
-
const originalFetch = window.fetch;
|
|
30
|
-
if (originalFetch) {
|
|
31
|
-
window.fetch = function (input, init) {
|
|
32
|
-
let targetUrl = '';
|
|
33
|
-
let isRequestObject = false;
|
|
34
|
-
|
|
35
|
-
if (typeof input === 'string') {
|
|
36
|
-
targetUrl = redirectUrl(input);
|
|
37
|
-
} else if (input instanceof URL) {
|
|
38
|
-
targetUrl = redirectUrl(input.toString());
|
|
39
|
-
input = new URL(targetUrl);
|
|
40
|
-
} else if (input instanceof Request) {
|
|
41
|
-
isRequestObject = true;
|
|
42
|
-
targetUrl = redirectUrl(input.url);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (isRequestObject) {
|
|
46
|
-
if (input.method === 'POST' && targetUrl.indexOf('localhost') !== -1) {
|
|
47
|
-
const modifiedOptions = {
|
|
48
|
-
method: input.method,
|
|
49
|
-
headers: new Headers(input.headers),
|
|
50
|
-
mode: input.mode,
|
|
51
|
-
credentials: input.credentials,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
if (input.body && !input.bodyUsed) {
|
|
55
|
-
const requestClone = input.clone();
|
|
56
|
-
return input.clone().arrayBuffer().then(function (buffer) {
|
|
57
|
-
modifiedOptions.body = buffer;
|
|
58
|
-
|
|
59
|
-
return originalFetch(targetUrl, modifiedOptions);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return originalFetch(targetUrl, modifiedOptions);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
input = new Request(targetUrl, input);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return originalFetch.apply(this, [targetUrl, init]);
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const originalOpen = XMLHttpRequest.prototype.open;
|
|
74
|
-
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
|
|
75
|
-
const redirectedUrl = redirectUrl(url);
|
|
76
|
-
if (redirectedUrl !== url) {
|
|
77
|
-
async = true;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (async === undefined) {
|
|
81
|
-
async = true;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return originalOpen.apply(this, [method, redirectedUrl, async, user, password]);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
if (navigator.sendBeacon) {
|
|
88
|
-
const originalSendBeacon = navigator.sendBeacon;
|
|
89
|
-
navigator.sendBeacon = function (url, data) {
|
|
90
|
-
console.log("Beacon data:", data);
|
|
91
|
-
return originalSendBeacon.apply(this, [redirectUrl(url), data]);
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
Object.defineProperty(HTMLImageElement.prototype, 'src', {
|
|
96
|
-
set: function(value) {
|
|
97
|
-
const descriptor = Object.getOwnPropertyDescriptor(Element.prototype, 'setAttribute');
|
|
98
|
-
descriptor.value.call(this, 'src', redirectUrl(value));
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
Object.defineProperty(HTMLScriptElement.prototype, 'src', {
|
|
102
|
-
set: function(value) {
|
|
103
|
-
const descriptor = Object.getOwnPropertyDescriptor(Element.prototype, 'setAttribute');
|
|
104
|
-
descriptor.value.call(this, 'src', redirectUrl(value));
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
package/mods/features/updater.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// TizenTube Cobalt Update Checker
|
|
2
|
-
|
|
3
|
-
import { buttonItem, showModal, showToast, overlayPanelItemListRenderer, scrollPaneRenderer, overlayMessageRenderer } from '../ui/ytUI.js';
|
|
4
|
-
import { configRead } from '../config.js';
|
|
5
|
-
import { t } from 'i18next';
|
|
6
|
-
|
|
7
|
-
// If TizenTube is not running on Cobalt, do nothing
|
|
8
|
-
// Add a timeout since reloading the home page while the updater pop up is shown causes the pop up to instantly disappear.
|
|
9
|
-
setTimeout(() => {
|
|
10
|
-
if (window.h5vcc && window.h5vcc.tizentube && configRead('enableUpdater')) {
|
|
11
|
-
const currentEpoch = Math.floor(Date.now() / 1000);
|
|
12
|
-
if (configRead('dontCheckUpdateUntil') > currentEpoch) {
|
|
13
|
-
console.info('Skipping update check until', new Date(configRead('dontCheckUpdateUntil') * 1000).toLocaleString());
|
|
14
|
-
} else checkForUpdates();
|
|
15
|
-
}
|
|
16
|
-
}, 2500);
|
|
17
|
-
|
|
18
|
-
function getLatestRelease() {
|
|
19
|
-
return fetch('https://api.github.com/repos/reisxd/TizenTubeCobalt/releases/latest')
|
|
20
|
-
.then(response => {
|
|
21
|
-
if (!response.ok) {
|
|
22
|
-
throw new Error('Network response was not ok');
|
|
23
|
-
}
|
|
24
|
-
return response.json();
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function checkForUpdates(showNoUpdateToast) {
|
|
29
|
-
const currentAppVersion = window.h5vcc.tizentube.GetVersion();
|
|
30
|
-
const currentEpoch = Math.floor(Date.now() / 1000);
|
|
31
|
-
|
|
32
|
-
getLatestRelease()
|
|
33
|
-
.then(release => {
|
|
34
|
-
const latestVersion = release.tag_name.replace('v', '');
|
|
35
|
-
const releaseDate = new Date(release.published_at).getTime() / 1000;
|
|
36
|
-
|
|
37
|
-
let architecture;
|
|
38
|
-
let downloadUrl;
|
|
39
|
-
|
|
40
|
-
if (window.h5vcc.tizentube.GetArchitecture) {
|
|
41
|
-
architecture = window.h5vcc.tizentube.GetArchitecture();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (architecture) {
|
|
45
|
-
if (architecture === 'arm64-v8a') {
|
|
46
|
-
downloadUrl = release.assets.find(asset => asset.name.includes('arm64.apk')).browser_download_url;
|
|
47
|
-
} else {
|
|
48
|
-
downloadUrl = release.assets.find(asset => asset.name.includes('arm.apk')).browser_download_url;
|
|
49
|
-
}
|
|
50
|
-
} else downloadUrl = release.assets[0].browser_download_url;
|
|
51
|
-
|
|
52
|
-
if (latestVersion !== currentAppVersion) {
|
|
53
|
-
console.info(`New version available: ${latestVersion} (current: ${currentAppVersion})`);
|
|
54
|
-
const msg = `${t('settings.options.updater.releaseDate', { date: new Date(releaseDate * 1000).toLocaleString() })}\n${release.body}`.replace(/#/g, '').replace(/\*/g, '').trim();
|
|
55
|
-
|
|
56
|
-
const buttons = [
|
|
57
|
-
buttonItem(
|
|
58
|
-
{ title: t('settings.options.updater.updateNow.title'), subtitle: t('settings.options.updater.updateNow.subtitle') },
|
|
59
|
-
{ icon: 'DOWN_ARROW' },
|
|
60
|
-
[
|
|
61
|
-
{
|
|
62
|
-
customAction: {
|
|
63
|
-
action: 'UPDATE_DOWNLOAD',
|
|
64
|
-
parameters: downloadUrl
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
signalAction: {
|
|
69
|
-
signal: 'POPUP_BACK'
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
]
|
|
73
|
-
),
|
|
74
|
-
buttonItem(
|
|
75
|
-
{ title: t('settings.options.updater.remindLater.title'), subtitle: t('settings.options.updater.remindLater.subtitle') },
|
|
76
|
-
{ icon: 'SEARCH_HISTORY' },
|
|
77
|
-
[
|
|
78
|
-
{
|
|
79
|
-
customAction: {
|
|
80
|
-
action: 'UPDATE_REMIND_LATER',
|
|
81
|
-
parameters: currentEpoch + 86400
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
signalAction: {
|
|
86
|
-
signal: 'POPUP_BACK'
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
)
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
// Add an empty message so the CSS doesn't get screwed after user input
|
|
94
|
-
buttons.push(overlayMessageRenderer(' '));
|
|
95
|
-
buttons.push(overlayMessageRenderer(msg));
|
|
96
|
-
|
|
97
|
-
showModal(
|
|
98
|
-
{
|
|
99
|
-
title: t('settings.options.updater.updateAvailable.title'),
|
|
100
|
-
subtitle: t('settings.options.updater.updateAvailable.subtitle', { latestVersion, currentVersion: currentAppVersion })
|
|
101
|
-
},
|
|
102
|
-
overlayPanelItemListRenderer(buttons),
|
|
103
|
-
'tt-update-modal',
|
|
104
|
-
false
|
|
105
|
-
)
|
|
106
|
-
} else {
|
|
107
|
-
console.info('You are using the latest version of TizenTube.');
|
|
108
|
-
if (showNoUpdateToast) {
|
|
109
|
-
showToast(t('settings.options.updater.upToDate.title'), t('settings.options.updater.upToDate.subtitle', { version: currentAppVersion }), null);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
.catch(error => {
|
|
114
|
-
console.error('Error fetching the latest release:', error);
|
|
115
|
-
showToast(t('settings.options.updater.checkFailed.title'), t('settings.options.updater.checkFailed.subtitle'), null);
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export default checkForUpdates;
|