nuvio-tizen 1.9.5 → 1.9.6
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/config.xml +1 -1
- package/index.html +11 -11
- package/js/app.js +10 -1
- package/js/keys.js +9 -0
- package/js/views.js +37 -1
- package/js/ytplay.js +2 -17
- package/package.json +1 -1
package/config.xml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.org/ns/widgets"
|
|
3
|
-
id="http://nuvio.tizen/NuvioTizen" version="1.9.
|
|
3
|
+
id="http://nuvio.tizen/NuvioTizen" version="1.9.6" viewmodes="maximized">
|
|
4
4
|
<tizen:application id="Nuvio00001.Nuvio" package="Nuvio00001" required_version="3.0"/>
|
|
5
5
|
<content src="index.html"/>
|
|
6
6
|
<feature name="http://tizen.org/feature/screen.size.all"/>
|
package/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=1920, height=1080, user-scalable=no">
|
|
6
6
|
<title>Nuvio</title>
|
|
7
|
-
<link rel="stylesheet" href="css/style.css?v=1.9.
|
|
7
|
+
<link rel="stylesheet" href="css/style.css?v=1.9.6">
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<!-- CSS-applied sentinel (kept off-screen); app.js waits for its width before rendering -->
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
|
|
51
51
|
<!-- Load order matters: polyfills -> util -> iptv -> data -> input -> player -> ytplay -> views -> app -->
|
|
52
52
|
<!-- ?v= busts the TV webview cache on every release (keep in sync with package.json) -->
|
|
53
|
-
<script src="js/polyfills.js?v=1.9.
|
|
54
|
-
<script src="js/util.js?v=1.9.
|
|
55
|
-
<script src="js/iptv.js?v=1.9.
|
|
56
|
-
<script src="js/stremio.js?v=1.9.
|
|
57
|
-
<script src="js/keys.js?v=1.9.
|
|
58
|
-
<script src="js/focus.js?v=1.9.
|
|
59
|
-
<script src="js/player.js?v=1.9.
|
|
60
|
-
<script src="js/ytplay.js?v=1.9.
|
|
61
|
-
<script src="js/views.js?v=1.9.
|
|
62
|
-
<script src="js/app.js?v=1.9.
|
|
53
|
+
<script src="js/polyfills.js?v=1.9.6"></script>
|
|
54
|
+
<script src="js/util.js?v=1.9.6"></script>
|
|
55
|
+
<script src="js/iptv.js?v=1.9.6"></script>
|
|
56
|
+
<script src="js/stremio.js?v=1.9.6"></script>
|
|
57
|
+
<script src="js/keys.js?v=1.9.6"></script>
|
|
58
|
+
<script src="js/focus.js?v=1.9.6"></script>
|
|
59
|
+
<script src="js/player.js?v=1.9.6"></script>
|
|
60
|
+
<script src="js/ytplay.js?v=1.9.6"></script>
|
|
61
|
+
<script src="js/views.js?v=1.9.6"></script>
|
|
62
|
+
<script src="js/app.js?v=1.9.6"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
package/js/app.js
CHANGED
|
@@ -4,6 +4,7 @@ var App = (function () {
|
|
|
4
4
|
|
|
5
5
|
var _current = 'home';
|
|
6
6
|
var _stack = []; // frames: { screen, focus }
|
|
7
|
+
var _lastBackAt = 0; // debounce BACK across keydown + tizenhwkey
|
|
7
8
|
|
|
8
9
|
function screenEl(name) { return U.byId('screen-' + name); }
|
|
9
10
|
|
|
@@ -58,6 +59,7 @@ var App = (function () {
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
function back() {
|
|
62
|
+
if (typeof YTPlay !== 'undefined' && YTPlay.isOpen && YTPlay.isOpen()) { YTPlay.close(); return; }
|
|
61
63
|
if (Player.isActive()) { Player.exit(); return; }
|
|
62
64
|
if (Views.isModalOpen()) { Views.closeModal(); return; }
|
|
63
65
|
if (_current === 'detail' && Views.backInDetail()) { return; }
|
|
@@ -102,7 +104,14 @@ var App = (function () {
|
|
|
102
104
|
else { Focus.clickCurrent(); }
|
|
103
105
|
return true;
|
|
104
106
|
}
|
|
105
|
-
case 'BACK':
|
|
107
|
+
case 'BACK': {
|
|
108
|
+
// keydown(10009) and tizenhwkey can both fire for one press — debounce.
|
|
109
|
+
var now = Date.now();
|
|
110
|
+
if (now - _lastBackAt < 400) { return true; }
|
|
111
|
+
_lastBackAt = now;
|
|
112
|
+
back();
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
106
115
|
default: return true;
|
|
107
116
|
}
|
|
108
117
|
}
|
package/js/keys.js
CHANGED
|
@@ -67,10 +67,19 @@ var Keys = (function () {
|
|
|
67
67
|
dispatch(held >= LONG_MS ? 'OK_LONG' : 'OK', ev);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
// Tizen delivers the hardware RETURN/BACK as a 'tizenhwkey' event to the app
|
|
71
|
+
// document even when a cross-origin iframe (e.g. the YouTube leanback overlay)
|
|
72
|
+
// holds focus — where a normal keydown never reaches us. Route it through the same
|
|
73
|
+
// handler and preventDefault so the platform doesn't exit the app behind our back.
|
|
74
|
+
function onHwKey(ev) {
|
|
75
|
+
if (ev && ev.keyName === 'back') { dispatch('BACK', ev); }
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
function init() {
|
|
71
79
|
register();
|
|
72
80
|
document.addEventListener('keydown', onKeyDown, false);
|
|
73
81
|
document.addEventListener('keyup', onKeyUp, false);
|
|
82
|
+
document.addEventListener('tizenhwkey', onHwKey, false);
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
return { init: init, setHandler: setHandler, CODE: CODE };
|
package/js/views.js
CHANGED
|
@@ -526,6 +526,29 @@ var Views = (function () {
|
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
// ----- streams -----
|
|
529
|
+
// "Where to watch" / subscription links (Paramount+, Netflix, rent/buy, etc.) are
|
|
530
|
+
// pushed to the bottom, below directly-playable sources.
|
|
531
|
+
function isSubscriptionStream(stream) {
|
|
532
|
+
if (Stremio.isPlayable(stream)) {
|
|
533
|
+
var t = ('' + (stream.name || '') + ' ' + (stream.title || '') + ' ' + (stream.description || ''));
|
|
534
|
+
return /\b(subscription|subscribe|rent|buy|purchase)\b|paramount|netflix|disney\+|disney plus|hbo|max\b|hulu|peacock|prime video|apple tv|starz|showtime/i.test(t);
|
|
535
|
+
}
|
|
536
|
+
return true; // non-playable (external "where to watch") always sinks to the bottom
|
|
537
|
+
}
|
|
538
|
+
function streamRank(stream) {
|
|
539
|
+
if (Stremio.isPlayable(stream) && !isSubscriptionStream(stream)) { return 0; }
|
|
540
|
+
if (Stremio.isPlayable(stream)) { return 1; }
|
|
541
|
+
return 2;
|
|
542
|
+
}
|
|
543
|
+
function sortStreams(list) {
|
|
544
|
+
return list.map(function (s, i) { return { s: s, i: i }; })
|
|
545
|
+
.sort(function (a, b) {
|
|
546
|
+
var ra = streamRank(a.s), rb = streamRank(b.s);
|
|
547
|
+
return ra !== rb ? ra - rb : a.i - b.i; // stable within a rank
|
|
548
|
+
})
|
|
549
|
+
.map(function (x) { return x.s; });
|
|
550
|
+
}
|
|
551
|
+
|
|
529
552
|
function openStreams(videoId, label) {
|
|
530
553
|
_d.mode = 'streams';
|
|
531
554
|
_d.streamId = videoId;
|
|
@@ -534,7 +557,7 @@ var Views = (function () {
|
|
|
534
557
|
drawDetail();
|
|
535
558
|
U.spinner(true);
|
|
536
559
|
Stremio.getStreams(_d.type, videoId).then(function (list) {
|
|
537
|
-
_d.streams = list;
|
|
560
|
+
_d.streams = sortStreams(list);
|
|
538
561
|
U.spinner(false);
|
|
539
562
|
if (_d.mode === 'streams') { drawDetail(); }
|
|
540
563
|
}, function () {
|
|
@@ -544,6 +567,11 @@ var Views = (function () {
|
|
|
544
567
|
});
|
|
545
568
|
}
|
|
546
569
|
|
|
570
|
+
// Re-fetch the current title's sources and land focus on the first result.
|
|
571
|
+
function rescanStreams() {
|
|
572
|
+
if (_d && _d.streamId) { openStreams(_d.streamId, _d.streamLabel); }
|
|
573
|
+
}
|
|
574
|
+
|
|
547
575
|
function streamActionsBar() {
|
|
548
576
|
var row = U.el('div', 'btn-row');
|
|
549
577
|
row.appendChild(U.el('div', 'detail-meta', 'Sources for ' + (_d.streamLabel || '')));
|
|
@@ -588,6 +616,14 @@ var Views = (function () {
|
|
|
588
616
|
};
|
|
589
617
|
listScroll.appendChild(node);
|
|
590
618
|
});
|
|
619
|
+
|
|
620
|
+
// Re-scan: refresh the source list (and jump back to the first result).
|
|
621
|
+
var rescan = U.el('div', 'stream focusable rescan-row');
|
|
622
|
+
rescan.appendChild(document.createTextNode('↻ Re-scan for sources'));
|
|
623
|
+
rescan.appendChild(U.el('span', 's-sub', 'Refresh the list from all stream addons'));
|
|
624
|
+
rescan.__onselect = function () { rescanStreams(); };
|
|
625
|
+
listScroll.appendChild(rescan);
|
|
626
|
+
|
|
591
627
|
panel.appendChild(listScroll);
|
|
592
628
|
return panel;
|
|
593
629
|
}
|
package/js/ytplay.js
CHANGED
|
@@ -64,17 +64,6 @@ var YTPlay = (function () {
|
|
|
64
64
|
}, function () { return ''; });
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
function onHwKey(e) {
|
|
68
|
-
if (e && e.keyName === 'back') { if (e.preventDefault) { e.preventDefault(); } close(); }
|
|
69
|
-
}
|
|
70
|
-
function onKeyDown(e) {
|
|
71
|
-
if (e && (e.keyCode === 10009 || e.keyCode === 461)) {
|
|
72
|
-
if (e.preventDefault) { e.preventDefault(); }
|
|
73
|
-
if (e.stopPropagation) { e.stopPropagation(); }
|
|
74
|
-
close();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
67
|
function show(id) {
|
|
79
68
|
var o = overlay();
|
|
80
69
|
if (!o) { return; }
|
|
@@ -87,17 +76,13 @@ var YTPlay = (function () {
|
|
|
87
76
|
o.className = '';
|
|
88
77
|
U.byId('app').className = 'hidden';
|
|
89
78
|
_open = true;
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
document.addEventListener('tizenhwkey', onHwKey, true);
|
|
93
|
-
document.addEventListener('keydown', onKeyDown, true);
|
|
79
|
+
// Back is handled app-wide by keys.js (tizenhwkey) → App.back() → YTPlay.close(),
|
|
80
|
+
// which works even while this cross-origin iframe holds focus.
|
|
94
81
|
}
|
|
95
82
|
|
|
96
83
|
function close() {
|
|
97
84
|
if (!_open) { return; }
|
|
98
85
|
_open = false;
|
|
99
|
-
document.removeEventListener('tizenhwkey', onHwKey, true);
|
|
100
|
-
document.removeEventListener('keydown', onKeyDown, true);
|
|
101
86
|
var o = overlay();
|
|
102
87
|
if (o) { o.className = 'hidden'; U.clear(o); }
|
|
103
88
|
U.byId('app').className = '';
|