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 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.5" viewmodes="maximized">
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.5">
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.5"></script>
54
- <script src="js/util.js?v=1.9.5"></script>
55
- <script src="js/iptv.js?v=1.9.5"></script>
56
- <script src="js/stremio.js?v=1.9.5"></script>
57
- <script src="js/keys.js?v=1.9.5"></script>
58
- <script src="js/focus.js?v=1.9.5"></script>
59
- <script src="js/player.js?v=1.9.5"></script>
60
- <script src="js/ytplay.js?v=1.9.5"></script>
61
- <script src="js/views.js?v=1.9.5"></script>
62
- <script src="js/app.js?v=1.9.5"></script>
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': back(); return true;
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
- // Hardware Back is delivered app-wide (tizenhwkey) even when the iframe has
91
- // focus; the keydown capture covers the moment before the iframe grabs focus.
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 = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuvio-tizen",
3
- "version": "1.9.5",
3
+ "version": "1.9.6",
4
4
  "description": "Nuvio-style Stremio-addon streaming client for Tizen 3.0+ Samsung TVs (TizenBrew app module).",
5
5
  "packageType": "app",
6
6
  "appName": "Nuvio",