@oxymoron.13/tizentube-s95b 0.3.0 → 0.3.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.
@@ -1,6 +1,7 @@
1
1
  // Picture in Picture Mode for TizenTube
2
2
 
3
3
  import resolveCommand from "../resolveCommand.js";
4
+ import { s95bMiniLayout, s95bMiniEntered } from './s95bMiniPlayer.js';
4
5
 
5
6
  window.isPipPlaying = false;
6
7
  let PlayerService = null;
@@ -51,14 +52,10 @@ function enablePip() {
51
52
 
52
53
  function onPipEnter() {
53
54
  videoElement.style.removeProperty('inset');
54
- const pipWidth = window.innerWidth / 3.5;
55
- const pipHeight = window.innerHeight / 3.5;
56
- videoElement.style.width = `${pipWidth}px`;
57
- videoElement.style.height = `${pipHeight}px`;
58
- videoElement.style.top = '68vh';
59
- videoElement.style.left = '68vw';
55
+ s95bMiniLayout(videoElement);
60
56
 
61
57
  window.isPipPlaying = true;
58
+ s95bMiniEntered(videoElement);
62
59
  videoElement.removeEventListener('play', onPipEnter);
63
60
  }
64
61
 
@@ -10,6 +10,19 @@ export const STRINGS = Object.freeze({
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
+ exit: 'Beenden',
14
+ exitHintTizen: 'Beendet die App (TizenTube und TizenBrew, wie die Zurück-Taste in TizenBrew). TizenBrews Hintergrunddienst wird dabei nicht gestoppt.',
15
+ exitHintSignal: 'Die Tizen-Funktion zum Beenden ist hier nicht verfügbar; es wird YouTubes Beenden-Signal versucht (am TV ungeprüft). Sonst die Home-Taste der Fernbedienung benutzen.',
16
+ exitCancel: 'Abbrechen', exitNow: 'Ja, App beenden',
17
+ exitFailed: 'Beenden nicht möglich. Bitte die Home-Taste der Fernbedienung benutzen.',
18
+ mini: 'Mini-Player',
19
+ miniHint: 'TizenTubes Mini-Player (Eintrag „MiniPlayer“ im Video-Menü). Änderungen gelten beim nächsten Start des Mini-Players. Ein neu gestartetes Video öffnet im Vollbild und ersetzt den Mini-Player.',
20
+ miniOn: 'Mini-Player anbieten',
21
+ miniOnHint: 'Eintrag im Video-Menü und Schaltfläche in der Player-Leiste (die Leiste ändert sich eventuell erst nach einem Neustart von YouTube).',
22
+ miniPos: 'Position', posBR: 'Unten rechts', posBL: 'Unten links', posTR: 'Oben rechts', posTL: 'Oben links',
23
+ miniSize: 'Größe', sizeSmall: 'Klein', sizeMedium: 'Mittel (wie bisher)', sizeLarge: 'Groß',
24
+ miniBrowse: 'Beim Stöbern', browsePlay: 'Weiterlaufen', browsePause: 'Pausieren',
25
+ miniBrowseHint: 'Pausieren: Das Video hält im kleinen Fenster an (der Anfang ist kurz hörbar) und läuft im Vollbild an derselben Stelle weiter.',
13
26
  }),
14
27
  en: Object.freeze({
15
28
  title: 'S95B · Performance', language: 'Language', codec: 'Video codec', quality: 'Default quality (all videos)',
@@ -21,6 +34,19 @@ export const STRINGS = Object.freeze({
21
34
  statVideo: 'Video', statFrames: 'Dropped frames', statCodec: 'Codec preference',
22
35
  statUnavailable: 'not available', statWait: 'waiting for video',
23
36
  languageDE: 'Deutsch', languageEN: 'English',
37
+ exit: 'Exit',
38
+ exitHintTizen: 'Closes the app (TizenTube and TizenBrew, like the Back key in TizenBrew). TizenBrew’s background service is not stopped.',
39
+ exitHintSignal: 'The Tizen exit function is not available here; YouTube’s exit signal is tried instead (untested on the TV). Otherwise use the remote’s Home key.',
40
+ exitCancel: 'Cancel', exitNow: 'Yes, exit the app',
41
+ exitFailed: 'Could not exit. Please use the remote’s Home key.',
42
+ mini: 'Mini player',
43
+ miniHint: 'TizenTube’s mini player (“MiniPlayer” row in the video menu). Changes apply the next time the mini player starts. A newly started video opens full screen and replaces the mini player.',
44
+ miniOn: 'Offer mini player',
45
+ miniOnHint: 'Row in the video menu and button in the player bar (the bar may only change after restarting YouTube).',
46
+ miniPos: 'Position', posBR: 'Bottom right', posBL: 'Bottom left', posTR: 'Top right', posTL: 'Top left',
47
+ miniSize: 'Size', sizeSmall: 'Small', sizeMedium: 'Medium (as before)', sizeLarge: 'Large',
48
+ miniBrowse: 'While browsing', browsePlay: 'Keep playing', browsePause: 'Pause',
49
+ miniBrowseHint: 'Pause: the video stops in the small window (its start is briefly audible) and continues full screen from the same position.',
24
50
  })
25
51
  });
26
52
  export function translate(lang, key) {
@@ -5,11 +5,16 @@
5
5
  */
6
6
  import { configRead, configWrite, configChangeEmitter } from '../config.js';
7
7
  import { optionShow } from '../ui/settings.js';
8
+ import { showToast } from '../ui/ytUI.js';
9
+ import resolveCommand from '../resolveCommand.js';
10
+ import { S95B_VERSION_TAG } from './s95bVersion.js';
8
11
  import { translate } from './s95bI18n.js';
9
12
  import { codecMenu, qualityMenu, codecLabel, currentCodec } from './s95bOptions.js';
13
+ import { MINI_POSITIONS, MINI_SIZES, MINI_BROWSE } from './s95bMiniPlayer.js';
10
14
 
11
15
  const STATE_KEY = 's95b-perf-edition-v1';
12
16
  const EDITED = ['enablePreviews', 'enableDeArrow', 'enableDeArrowThumbnails'];
17
+ const VERSION = String(S95B_VERSION_TAG).split(':')[1] || '?';
13
18
  const defaultState = { lang: 'de', light: false, previous: null };
14
19
  let state = { ...defaultState };
15
20
 
@@ -80,6 +85,49 @@ function statsLine() {
80
85
  ' | ' + t('statCodec') + ': ' + codecLabel(t, currentCodec());
81
86
  }
82
87
 
88
+ const MINI_DEFAULTS = { s95bMiniPos: 'br', s95bMiniSize: 'medium', s95bMiniBrowse: 'play' };
89
+ const MINI_VALUES = { s95bMiniPos: MINI_POSITIONS, s95bMiniSize: MINI_SIZES, s95bMiniBrowse: MINI_BROWSE };
90
+ const MINI_LABELS = { br: 'posBR', bl: 'posBL', tr: 'posTR', tl: 'posTL', small: 'sizeSmall', medium: 'sizeMedium',
91
+ large: 'sizeLarge', play: 'browsePlay', pause: 'browsePause' };
92
+
93
+ function miniChoice(title, key, hint) {
94
+ return { name: t(title), value: null, menuId: 's95b-' + key,
95
+ menuHeader: { title: t(title), subtitle: hint ? t(hint) : t('miniHint') },
96
+ options: MINI_VALUES[key].map(value => ({ name: t(MINI_LABELS[value]), key, value })) };
97
+ }
98
+ function miniMenu() {
99
+ return { name: t('mini'), icon: 'CLEAR_COOKIES', value: null, menuId: 's95b-mini',
100
+ menuHeader: { title: t('mini'), subtitle: t('miniHint') },
101
+ options: [
102
+ // TizenTube's own switch; the fork extends it to the "MiniPlayer" row in the video menu.
103
+ { name: t('miniOn'), subtitle: t('miniOnHint'), value: 'enableMPButton' },
104
+ miniChoice('miniPos', 's95bMiniPos'),
105
+ miniChoice('miniSize', 's95bMiniSize'),
106
+ miniChoice('miniBrowse', 's95bMiniBrowse', 'miniBrowseHint')
107
+ ] };
108
+ }
109
+
110
+ // Exit: the same call TizenBrew's own Back key uses; YouTube's exit signal is the fallback.
111
+ function canExitViaTizen() {
112
+ try { return !!(window.tizen && window.tizen.application && typeof window.tizen.application.getCurrentApplication === 'function'); }
113
+ catch (_) { return false; }
114
+ }
115
+ function exitMenu() {
116
+ return { name: t('exit'), value: null, menuId: 's95b-exit',
117
+ menuHeader: { title: t('exit'), subtitle: canExitViaTizen() ? t('exitHintTizen') : t('exitHintSignal') },
118
+ options: [
119
+ { name: t('exitCancel'), key: 's95bExit', value: 'no' },
120
+ { name: t('exitNow'), key: 's95bExit', value: 'now' }
121
+ ] };
122
+ }
123
+ function exitApp() {
124
+ if (canExitViaTizen()) {
125
+ try { window.tizen.application.getCurrentApplication().exit(); return; } catch (_) { /* try the signal */ }
126
+ }
127
+ try { resolveCommand({ signalAction: { signal: 'EXIT_APP' } }); }
128
+ catch (e) { showToast('TizenTube', t('exitFailed')); }
129
+ }
130
+
83
131
  function buildOptions() {
84
132
  return [
85
133
  { name: t('language'), value: null, menuId: 's95b-language',
@@ -90,7 +138,9 @@ function buildOptions() {
90
138
  ] },
91
139
  codecMenu(t),
92
140
  qualityMenu(t),
93
- { name: t('light'), subtitle: t('lightHint'), value: 's95bLowOverhead' }
141
+ miniMenu(),
142
+ { name: t('light'), subtitle: t('lightHint'), value: 's95bLowOverhead' },
143
+ exitMenu()
94
144
  ];
95
145
  }
96
146
 
@@ -98,7 +148,7 @@ function openMenu() {
98
148
  try {
99
149
  optionShow({
100
150
  menuId: 's95b-menu',
101
- menuHeader: { title: t('title'), subtitle: statsLine() },
151
+ menuHeader: { title: t('title') + ' v' + VERSION, subtitle: statsLine() },
102
152
  selectedIndex: 0,
103
153
  options: buildOptions()
104
154
  }, false);
@@ -119,13 +169,21 @@ function onConfigChange(event) {
119
169
  const detail = event && event.detail;
120
170
  if (!detail) return;
121
171
  if (detail.key === 's95bLowOverhead') turnLight(detail.value === true);
122
- else if (detail.key === 's95bLanguage') { state.lang = detail.value === 'en' ? 'en' : 'de'; save(); }
172
+ else if (detail.key === 's95bExit') {
173
+ if (detail.value !== 'now') return;
174
+ configWrite('s95bExit', 'no'); // never leave "now" stored
175
+ exitApp();
176
+ } else if (detail.key === 's95bLanguage') { state.lang = detail.value === 'en' ? 'en' : 'de'; save(); }
123
177
  }
124
178
 
125
179
  function init() {
126
180
  // Menu entries read their current value from the TizenTube config, so mirror our stored state into it first.
127
181
  if (configRead('s95bLowOverhead') !== state.light) configWrite('s95bLowOverhead', state.light);
128
182
  if (configRead('s95bLanguage') !== state.lang) configWrite('s95bLanguage', state.lang);
183
+ if (configRead('s95bExit') !== 'no') configWrite('s95bExit', 'no');
184
+ Object.keys(MINI_DEFAULTS).forEach(key => {
185
+ if (MINI_VALUES[key].indexOf(configRead(key)) < 0) configWrite(key, MINI_DEFAULTS[key]);
186
+ });
129
187
  configChangeEmitter.addEventListener('configChange', onConfigChange);
130
188
  // Capture phase on window: only the yellow key is consumed.
131
189
  window.addEventListener('keydown', onKeys, true);
@@ -0,0 +1,36 @@
1
+ /* S95B Performance Edition – GPL-3.0-only
2
+ * Settings for TizenTube's own mini player (features/pictureInPicture.js): position, size and
3
+ * whether the video keeps playing while browsing. Called from the two patched lines in pictureInPicture.js.
4
+ * Defaults reproduce upstream exactly: 1/3.5 of the screen at 68vh / 68vw (bottom right), keeps playing.
5
+ */
6
+ import { configRead } from '../config.js';
7
+
8
+ export const MINI_POSITIONS = ['br', 'bl', 'tr', 'tl'];
9
+ export const MINI_SIZES = ['small', 'medium', 'large'];
10
+ export const MINI_BROWSE = ['play', 'pause'];
11
+ const DIVISOR = { small: 4.5, medium: 3.5, large: 2.5 };
12
+ // Upstream's gap between the mini player and the right/bottom screen edge, in vw/vh (100 - 68 - 100/3.5).
13
+ const GAP = 100 - 68 - 100 / 3.5;
14
+ const round = value => Math.round(value * 100) / 100;
15
+
16
+ export function s95bMiniLayout(videoElement) {
17
+ const position = MINI_POSITIONS.indexOf(configRead('s95bMiniPos')) >= 0 ? configRead('s95bMiniPos') : 'br';
18
+ const divisor = DIVISOR[configRead('s95bMiniSize')] || DIVISOR.medium;
19
+ const far = round(100 - 100 / divisor - GAP);
20
+ const near = round(GAP);
21
+ videoElement.style.width = `${window.innerWidth / divisor}px`;
22
+ videoElement.style.height = `${window.innerHeight / divisor}px`;
23
+ videoElement.style.top = (position[0] === 'b' ? far : near) + 'vh';
24
+ videoElement.style.left = (position[1] === 'r' ? far : near) + 'vw';
25
+ }
26
+
27
+ // "Pause while browsing": pause once the reloaded video has really started, so YouTube's own play() call
28
+ // is not interrupted. Going back to full screen starts the video again from this position (upstream behavior).
29
+ export function s95bMiniEntered(videoElement) {
30
+ if (configRead('s95bMiniBrowse') !== 'pause') return;
31
+ const pauseOnce = () => {
32
+ videoElement.removeEventListener('playing', pauseOnce);
33
+ if (window.isPipPlaying) videoElement.pause();
34
+ };
35
+ videoElement.addEventListener('playing', pauseOnce);
36
+ }
@@ -0,0 +1,2 @@
1
+ // Generated by create-fork.mjs from the kit's VERSION file. Do not edit.
2
+ export const S95B_VERSION_TAG = 's95b-version:0.3.2';
@@ -97,7 +97,7 @@ export function patchResolveCommand() {
97
97
  }
98
98
  }
99
99
 
100
- cmd.openPopupAction.popup.overlaySectionRenderer.overlay.overlayTwoPanelRenderer.actionPanel.overlayPanelRenderer.content.overlayPanelItemListRenderer.items.splice(2, 0,
100
+ if (configRead('enableMPButton')) cmd.openPopupAction.popup.overlaySectionRenderer.overlay.overlayTwoPanelRenderer.actionPanel.overlayPanelRenderer.content.overlayPanelItemListRenderer.items.splice(2, 0,
101
101
  buttonItem(
102
102
  { title: t('player.miniPlayer') },
103
103
  { icon: 'CLEAR_COOKIES' }, [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oxymoron.13/tizentube-s95b",
3
- "appName": "TizenTube S95B Performance (Fork)",
4
- "version": "0.3.0",
3
+ "appName": "TizenTube S95B 0.3.2 (Fork)",
4
+ "version": "0.3.2",
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",