@oxymoron.13/tizentube-s95b 0.2.3 → 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.
- package/README.md +4 -2
- package/S95B-CHANGES.txt +6 -4
- package/dist/service.js +3 -0
- package/dist/userScript.js +1 -1
- package/mods/features/s95bI18n.js +4 -4
- package/mods/features/s95bMenu.js +15 -20
- package/mods/features/s95bOptions.js +30 -0
- package/mods/features/s95bPlayback.js +29 -0
- package/mods/resolveCommand.js +5 -0
- package/package.json +1 -1
- package/service/service.js +3 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/* S95B Performance Edition – GPL-3.0-only */
|
|
2
2
|
export const STRINGS = Object.freeze({
|
|
3
3
|
de: Object.freeze({
|
|
4
|
-
title: 'S95B · Performance', language: 'Sprache', codec: 'Videocodec', quality: '
|
|
4
|
+
title: 'S95B · Performance', language: 'Sprache', codec: 'Videocodec', quality: 'Standardqualität (alle Videos)',
|
|
5
5
|
automatic: 'Automatisch', light: 'Ressourcenschonender Modus',
|
|
6
6
|
lightHint: 'Schaltet Vorschauen und DeArrow ab; vorherige Werte werden beim Ausschalten wiederhergestellt.',
|
|
7
7
|
codecHint: 'H.264 liefert bei YouTube in der Regel höchstens 1080p; 4K gibt es nur als VP9 oder AV1. Ein Codec wird nur erzwungen, wenn dadurch keine Qualität verloren geht. Gilt ab dem nächsten Video.',
|
|
8
|
-
qualityHint: 'Eine feste Qualität gilt sofort. „Automatisch“ wirkt eventuell erst nach einem Neustart von YouTube.',
|
|
8
|
+
qualityHint: 'Gilt für alle Videos (YouTubes eigene Zeile „Qualität“ ändert nur das laufende Video). Eine feste Qualität gilt sofort. „Automatisch“ wirkt eventuell erst nach einem Neustart von YouTube.',
|
|
9
9
|
languageHint: 'Gilt für dieses Menü, beim nächsten Öffnen.',
|
|
10
10
|
statVideo: 'Video', statFrames: 'Verlorene Bilder', statCodec: 'Codec-Wunsch',
|
|
11
11
|
statUnavailable: 'nicht verfügbar', statWait: 'wartet auf Video',
|
|
12
12
|
languageDE: 'Deutsch', languageEN: 'English',
|
|
13
13
|
}),
|
|
14
14
|
en: Object.freeze({
|
|
15
|
-
title: 'S95B · Performance', language: 'Language', codec: 'Video codec', quality: '
|
|
15
|
+
title: 'S95B · Performance', language: 'Language', codec: 'Video codec', quality: 'Default quality (all videos)',
|
|
16
16
|
automatic: 'Automatic', light: 'Low-overhead mode',
|
|
17
17
|
lightHint: 'Disables previews and DeArrow; the previous values are restored when switched off.',
|
|
18
18
|
codecHint: 'On YouTube, H.264 usually tops out at 1080p; 4K is only available as VP9 or AV1. A codec is only forced if no quality is lost. Applies from the next video.',
|
|
19
|
-
qualityHint: 'A fixed quality applies immediately. “Automatic” may only take effect after restarting YouTube.',
|
|
19
|
+
qualityHint: 'Applies to all videos (YouTube’s own “Quality” row only changes the current video). A fixed quality applies immediately. “Automatic” may only take effect after restarting YouTube.',
|
|
20
20
|
languageHint: 'Applies to this menu, the next time it is opened.',
|
|
21
21
|
statVideo: 'Video', statFrames: 'Dropped frames', statCodec: 'Codec preference',
|
|
22
22
|
statUnavailable: 'not available', statWait: 'waiting for video',
|
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
import { configRead, configWrite, configChangeEmitter } from '../config.js';
|
|
7
7
|
import { optionShow } from '../ui/settings.js';
|
|
8
8
|
import { translate } from './s95bI18n.js';
|
|
9
|
+
import { codecMenu, qualityMenu, codecLabel, currentCodec } from './s95bOptions.js';
|
|
9
10
|
|
|
10
11
|
const STATE_KEY = 's95b-perf-edition-v1';
|
|
11
12
|
const EDITED = ['enablePreviews', 'enableDeArrow', 'enableDeArrowThumbnails'];
|
|
12
|
-
const CODECS = ['any', 'vp9', 'av01', 'avc1'];
|
|
13
|
-
const QUALITIES = ['auto', '2160p', '1440p', '1080p', '720p'];
|
|
14
13
|
const defaultState = { lang: 'de', light: false, previous: null };
|
|
15
14
|
let state = { ...defaultState };
|
|
16
15
|
|
|
@@ -29,16 +28,6 @@ function save() {
|
|
|
29
28
|
}
|
|
30
29
|
function t(key) { return translate(state.lang, key); }
|
|
31
30
|
|
|
32
|
-
function codecLabel(value) {
|
|
33
|
-
return value === 'any' ? t('automatic') : value === 'vp9' ? 'VP9' : value === 'av01' ? 'AV1' : 'H.264';
|
|
34
|
-
}
|
|
35
|
-
function qualityLabel(value) {
|
|
36
|
-
return value === 'auto' ? t('automatic') : value === '2160p' ? '2160p (4K)' : value;
|
|
37
|
-
}
|
|
38
|
-
function currentCodec() {
|
|
39
|
-
const value = configRead('preferredVideoCodec');
|
|
40
|
-
return CODECS.indexOf(value) >= 0 ? value : 'any';
|
|
41
|
-
}
|
|
42
31
|
|
|
43
32
|
// Low-overhead mode: switches off a few TizenTube features and remembers the previous values.
|
|
44
33
|
function turnLight(enable) {
|
|
@@ -61,6 +50,16 @@ function turnLight(enable) {
|
|
|
61
50
|
save();
|
|
62
51
|
}
|
|
63
52
|
|
|
53
|
+
// Source frame rate as YouTube's own stats report it ("3840x2160@24 / ..."), same source as upstream autoFrameRate.js.
|
|
54
|
+
function frameRate() {
|
|
55
|
+
try {
|
|
56
|
+
const player = document.querySelector('.html5-video-player');
|
|
57
|
+
if (!player || typeof player.getStatsForNerds !== 'function') return '';
|
|
58
|
+
const match = String(player.getStatsForNerds().resolution || '').match(/@([\d.]+)/);
|
|
59
|
+
return match ? ' @' + match[1] : '';
|
|
60
|
+
} catch (_) { return ''; }
|
|
61
|
+
}
|
|
62
|
+
|
|
64
63
|
// One snapshot of what the web player reports, taken each time the menu opens.
|
|
65
64
|
function statsLine() {
|
|
66
65
|
const video = document.querySelector('video');
|
|
@@ -69,7 +68,7 @@ function statsLine() {
|
|
|
69
68
|
if (video) {
|
|
70
69
|
const width = video.videoWidth || 0;
|
|
71
70
|
const height = video.videoHeight || 0;
|
|
72
|
-
size = width > 0 && height > 0 ? width + ' × ' + height : t('statUnavailable');
|
|
71
|
+
size = width > 0 && height > 0 ? width + ' × ' + height + frameRate() : t('statUnavailable');
|
|
73
72
|
if (typeof video.getVideoPlaybackQuality === 'function') {
|
|
74
73
|
try {
|
|
75
74
|
const q = video.getVideoPlaybackQuality();
|
|
@@ -78,7 +77,7 @@ function statsLine() {
|
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
return t('statVideo') + ': ' + size + ' | ' + t('statFrames') + ': ' + dropped +
|
|
81
|
-
' | ' + t('statCodec') + ': ' + codecLabel(currentCodec());
|
|
80
|
+
' | ' + t('statCodec') + ': ' + codecLabel(t, currentCodec());
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
function buildOptions() {
|
|
@@ -89,12 +88,8 @@ function buildOptions() {
|
|
|
89
88
|
{ name: t('languageDE'), key: 's95bLanguage', value: 'de' },
|
|
90
89
|
{ name: t('languageEN'), key: 's95bLanguage', value: 'en' }
|
|
91
90
|
] },
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
options: CODECS.map(codec => ({ name: codecLabel(codec), key: 'preferredVideoCodec', value: codec })) },
|
|
95
|
-
{ name: t('quality'), icon: 'VIDEO_QUALITY', value: null, menuId: 's95b-quality',
|
|
96
|
-
menuHeader: { title: t('quality'), subtitle: t('qualityHint') },
|
|
97
|
-
options: QUALITIES.map(quality => ({ name: qualityLabel(quality), key: 'preferredVideoQuality', value: quality })) },
|
|
91
|
+
codecMenu(t),
|
|
92
|
+
qualityMenu(t),
|
|
98
93
|
{ name: t('light'), subtitle: t('lightHint'), value: 's95bLowOverhead' }
|
|
99
94
|
];
|
|
100
95
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* S95B Performance Edition – GPL-3.0-only
|
|
2
|
+
* Codec and default-quality choices, shared by the yellow menu and the playback settings popup.
|
|
3
|
+
* Both write TizenTube's own config keys (preferredVideoCodec, preferredVideoQuality).
|
|
4
|
+
*/
|
|
5
|
+
import { configRead } from '../config.js';
|
|
6
|
+
|
|
7
|
+
export const CODECS = ['any', 'vp9', 'av01', 'avc1'];
|
|
8
|
+
export const QUALITIES = ['auto', '2160p', '1440p', '1080p', '720p'];
|
|
9
|
+
|
|
10
|
+
export function codecLabel(t, value) {
|
|
11
|
+
return value === 'any' ? t('automatic') : value === 'vp9' ? 'VP9' : value === 'av01' ? 'AV1' : 'H.264';
|
|
12
|
+
}
|
|
13
|
+
export function qualityLabel(t, value) {
|
|
14
|
+
return value === 'auto' ? t('automatic') : value === '2160p' ? '2160p (4K)' : String(value);
|
|
15
|
+
}
|
|
16
|
+
export function currentCodec() {
|
|
17
|
+
const value = configRead('preferredVideoCodec');
|
|
18
|
+
return CODECS.indexOf(value) >= 0 ? value : 'any';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function codecMenu(t) {
|
|
22
|
+
return { name: t('codec'), icon: 'VIDEO_QUALITY', value: null, menuId: 's95b-codec',
|
|
23
|
+
menuHeader: { title: t('codec'), subtitle: t('codecHint') },
|
|
24
|
+
options: CODECS.map(codec => ({ name: codecLabel(t, codec), key: 'preferredVideoCodec', value: codec })) };
|
|
25
|
+
}
|
|
26
|
+
export function qualityMenu(t) {
|
|
27
|
+
return { name: t('quality'), icon: 'VIDEO_QUALITY', value: null, menuId: 's95b-quality',
|
|
28
|
+
menuHeader: { title: t('quality'), subtitle: t('qualityHint') },
|
|
29
|
+
options: QUALITIES.map(quality => ({ name: qualityLabel(t, quality), key: 'preferredVideoQuality', value: quality })) };
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* S95B Performance Edition – GPL-3.0-only
|
|
2
|
+
* Two entries for YouTube's playback settings popup (opened while a video plays): video codec and
|
|
3
|
+
* default quality for all videos. They open the same YouTube-drawn choice lists as the yellow menu.
|
|
4
|
+
* buttonItem is handed in by resolveCommand.js, which already imports it.
|
|
5
|
+
*/
|
|
6
|
+
import { configRead } from '../config.js';
|
|
7
|
+
import { translate } from './s95bI18n.js';
|
|
8
|
+
import { codecMenu, qualityMenu, codecLabel, qualityLabel, currentCodec } from './s95bOptions.js';
|
|
9
|
+
|
|
10
|
+
export function s95bPlaybackItems(buttonItem) {
|
|
11
|
+
const lang = configRead('s95bLanguage') === 'en' ? 'en' : 'de';
|
|
12
|
+
const t = key => translate(lang, key);
|
|
13
|
+
const entry = (menu, subtitle) => buttonItem({ title: menu.name, subtitle }, { icon: menu.icon }, [{
|
|
14
|
+
customAction: {
|
|
15
|
+
action: 'OPTIONS_SHOW',
|
|
16
|
+
parameters: {
|
|
17
|
+
options: menu.options,
|
|
18
|
+
selectedIndex: Math.max(0, menu.options.findIndex(o => o.value === configRead(o.key))),
|
|
19
|
+
update: false,
|
|
20
|
+
menuId: menu.menuId,
|
|
21
|
+
menuHeader: menu.menuHeader
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}]);
|
|
25
|
+
return [
|
|
26
|
+
entry(codecMenu(t), codecLabel(t, currentCodec())),
|
|
27
|
+
entry(qualityMenu(t), qualityLabel(t, configRead('preferredVideoQuality')))
|
|
28
|
+
];
|
|
29
|
+
}
|
package/mods/resolveCommand.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { configWrite, configRead } from './config.js';
|
|
2
2
|
import { enablePip } from './features/pictureInPicture.js';
|
|
3
|
+
import { s95bPlaybackItems } from './features/s95bPlayback.js';
|
|
3
4
|
import modernUI, { optionShow } from './ui/settings.js';
|
|
4
5
|
import { speedSettings } from './ui/speedUI.js';
|
|
5
6
|
import { showToast, buttonItem, showModal, QrCodeRenderer, overlayPanelItemListRenderer, overlayMessageRenderer } from './ui/ytUI.js';
|
|
@@ -151,6 +152,10 @@ export function patchResolveCommand() {
|
|
|
151
152
|
])
|
|
152
153
|
);
|
|
153
154
|
}
|
|
155
|
+
// S95B: video codec and default quality entries; a failure here never breaks the popup.
|
|
156
|
+
try {
|
|
157
|
+
cmd.openPopupAction.popup.overlaySectionRenderer.overlay.overlayTwoPanelRenderer.actionPanel.overlayPanelRenderer.content.overlayPanelItemListRenderer.items.splice(1, 0, ...s95bPlaybackItems(buttonItem));
|
|
158
|
+
} catch (e) { console.warn('[S95B] playback settings entries skipped:', e); }
|
|
154
159
|
} else if (cmd?.watchEndpoint?.videoId) {
|
|
155
160
|
window.isPipPlaying = false;
|
|
156
161
|
const ytlrPlayerContainer = document.querySelector('ytlr-player-container');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxymoron.13/tizentube-s95b",
|
|
3
3
|
"appName": "TizenTube S95B Performance (Fork)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Unofficial fork of TizenTube (@foxreis/tizentube) for TizenBrew: reversible performance profile, German/English settings, smaller bundle.",
|
|
6
6
|
"packageType": "mods",
|
|
7
7
|
"websiteURL": "https://youtube.com/tv?additionalDataUrl=http%3A%2F%2Flocalhost%3A8085%2Fdial%2Fapps%2FYouTube",
|
package/service/service.js
CHANGED
|
@@ -115,4 +115,7 @@ setInterval(() => {
|
|
|
115
115
|
|
|
116
116
|
app.listen(PORT, () => {
|
|
117
117
|
dialServer.start();
|
|
118
|
+
}).on('error', (e) => {
|
|
119
|
+
// S95B: without a handler, EADDRINUSE (e.g. original TizenTube running too) is an unhandled error event.
|
|
120
|
+
console.error('[S95B] service port ' + PORT + ' unavailable:', e && e.code);
|
|
118
121
|
});
|