@hatfiller/tizenpstream 1.0.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/.npmrc.example ADDED
@@ -0,0 +1 @@
1
+ //registry.npmjs.org/:_authToken=YOUR_TOKEN
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Tizen-PStream
2
+
3
+ TizenBrew **site modification** module for [P-Stream](https://pstream.mov) on Samsung Tizen TVs. Adds TV remote support and D-pad focus navigation, aimed at **older Tizen TVs (pre-2022)** and current models.
4
+
5
+ ## Install
6
+
7
+ 1. Install [TizenBrew](https://github.com/reisxd/TizenBrew) on your Samsung Tizen TV.
8
+ 2. In TizenBrew’s module manager, add `@hatfiller/tizenpstream` as an NPM module.
9
+
10
+ ## What it does
11
+
12
+ - **D-pad navigation**: Arrow keys move focus between links, buttons, and the video (spatial-style).
13
+ - **Enter on video**: Play/pause when the video has focus.
14
+ - **Back key**: Exits fullscreen when in fullscreen.
15
+ - **Media keys**: Registers Play/Pause, Stop, FastForward, Rewind, Next/Previous so the TV sends key events to the page.
16
+
17
+ The script is written in ES5-style JS for compatibility with older Tizen WebKit.
18
+
19
+ ## Module type
20
+
21
+ This is a **site modification** module (`packageType: "mods"`). It injects `mods/userScript.js` (or `dist/userScript.js`) into `https://pstream.mov` when you open it via TizenBrew.
22
+
23
+ ## Build
24
+
25
+ ```bash
26
+ npm run build
27
+ ```
28
+
29
+ This copies `mods/userScript.js` to `dist/userScript.js` (used when publishing).
30
+
31
+ ## License
32
+
33
+ MIT
@@ -0,0 +1,150 @@
1
+ /**
2
+ * TizenBrew mod for P-Stream (pstream.mov) on Samsung Tizen TVs.
3
+ * Adds TV remote key handling and focus navigation for pre-2022 and older Tizen.
4
+ * ES5-friendly for broad WebKit compatibility.
5
+ */
6
+ (function () {
7
+ 'use strict';
8
+
9
+ var KEY_LEFT = 37;
10
+ var KEY_UP = 38;
11
+ var KEY_RIGHT = 39;
12
+ var KEY_DOWN = 40;
13
+ var KEY_ENTER = 13;
14
+ var KEY_BACK = 10009;
15
+
16
+ /** @type {HTMLVideoElement | null} */
17
+ var lastKnownVideo = null;
18
+
19
+ function getFocusableSelector() {
20
+ return 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"]), input:not([disabled]), select, textarea, [onclick], video';
21
+ }
22
+
23
+ function getFocusables(container) {
24
+ var el = container || document.body;
25
+ var nodes = el.querySelectorAll(getFocusableSelector());
26
+ var list = [];
27
+ for (var i = 0; i < nodes.length; i++) {
28
+ var n = nodes[i];
29
+ if (n.offsetParent !== null && !n.disabled) {
30
+ list.push(n);
31
+ }
32
+ }
33
+ return list;
34
+ }
35
+
36
+ function getVisibleCenter(el) {
37
+ var r = el.getBoundingClientRect();
38
+ return { x: r.left + r.width / 2, y: r.top + r.height / 2 };
39
+ }
40
+
41
+ function findNeighbor(current, focusables, dir) {
42
+ var curCenter = getVisibleCenter(current);
43
+ var best = null;
44
+ var bestDist = Infinity;
45
+ for (var i = 0; i < focusables.length; i++) {
46
+ var cand = focusables[i];
47
+ if (cand === current) continue;
48
+ var cRect = cand.getBoundingClientRect();
49
+ if (cRect.width === 0 || cRect.height === 0) continue;
50
+ var cCenter = getVisibleCenter(cand);
51
+ var dx = cCenter.x - curCenter.x;
52
+ var dy = cCenter.y - curCenter.y;
53
+ var use = false;
54
+ if (dir === 'left' && dx < 0) use = true;
55
+ if (dir === 'right' && dx > 0) use = true;
56
+ if (dir === 'up' && dy < 0) use = true;
57
+ if (dir === 'down' && dy > 0) use = true;
58
+ if (!use) continue;
59
+ var dist = dx * dx + dy * dy;
60
+ if (dist < bestDist) {
61
+ bestDist = dist;
62
+ best = cand;
63
+ }
64
+ }
65
+ return best;
66
+ }
67
+
68
+ function handleKeydown(e) {
69
+ var key = e.keyCode;
70
+ var dir = null;
71
+ if (key === KEY_LEFT) dir = 'left';
72
+ if (key === KEY_UP) dir = 'up';
73
+ if (key === KEY_RIGHT) dir = 'right';
74
+ if (key === KEY_DOWN) dir = 'down';
75
+
76
+ if (dir) {
77
+ var focusables = getFocusables();
78
+ var current = document.activeElement;
79
+ if (!current || current === document.body) {
80
+ current = focusables[0] || null;
81
+ }
82
+ var next = findNeighbor(current, focusables, dir);
83
+ if (next) {
84
+ next.focus();
85
+ e.preventDefault();
86
+ e.stopPropagation();
87
+ return false;
88
+ }
89
+ }
90
+
91
+ if (key === KEY_ENTER) {
92
+ var target = document.activeElement;
93
+ if (target && target.tagName === 'VIDEO') {
94
+ if (target.paused) target.play();
95
+ else target.pause();
96
+ e.preventDefault();
97
+ return false;
98
+ }
99
+ }
100
+
101
+ if (key === KEY_BACK) {
102
+ if (lastKnownVideo && document.fullscreenElement) {
103
+ document.exitFullscreen();
104
+ e.preventDefault();
105
+ return false;
106
+ }
107
+ }
108
+
109
+ return true;
110
+ }
111
+
112
+ function registerTVKeys() {
113
+ try {
114
+ if (typeof tizen !== 'undefined' && tizen.tvinputdevice) {
115
+ var keys = [
116
+ 'MediaPlayPause', 'MediaPlay', 'MediaPause', 'MediaStop',
117
+ 'MediaFastForward', 'MediaRewind', 'MediaTrackNext', 'MediaTrackPrevious'
118
+ ];
119
+ for (var i = 0; i < keys.length; i++) {
120
+ try {
121
+ tizen.tvinputdevice.registerKey(keys[i]);
122
+ } catch (err) {}
123
+ }
124
+ }
125
+ } catch (err) {}
126
+ }
127
+
128
+ function trackVideo() {
129
+ var v = document.querySelector('video');
130
+ if (v && v !== lastKnownVideo) {
131
+ lastKnownVideo = v;
132
+ v.addEventListener('play', function () {
133
+ lastKnownVideo = v;
134
+ });
135
+ }
136
+ }
137
+
138
+ function init() {
139
+ registerTVKeys();
140
+ document.addEventListener('keydown', handleKeydown, true);
141
+ trackVideo();
142
+ setInterval(trackVideo, 2000);
143
+ }
144
+
145
+ if (document.readyState === 'loading') {
146
+ document.addEventListener('DOMContentLoaded', init);
147
+ } else {
148
+ init();
149
+ }
150
+ })();
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@hatfiller/tizenpstream",
3
+ "appName": "PStream",
4
+ "version": "1.0.0",
5
+ "description": "TizenBrew module for P-Stream (pstream.mov) on Samsung Tizen TVs — TV remote support and focus handling for pre-2022 and older Tizen.",
6
+ "packageType": "mods",
7
+ "websiteURL": "https://pstream.mov",
8
+ "main": "dist/userScript.js",
9
+ "scripts": {
10
+ "build": "node -e \"require('fs').mkdirSync('dist',{recursive:true}); require('fs').copyFileSync('mods/userScript.js','dist/userScript.js')\"",
11
+ "prepublishOnly": "npm run build"
12
+ },
13
+ "author": "perlytiara",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/perlytiara/TizenPStream.git"
17
+ },
18
+ "keys": [
19
+ "MediaPlayPause",
20
+ "MediaPlay",
21
+ "MediaPause",
22
+ "MediaStop",
23
+ "MediaFastForward",
24
+ "MediaRewind",
25
+ "MediaTrackNext",
26
+ "MediaTrackPrevious"
27
+ ],
28
+ "license": "MIT"
29
+ }