stremiok 2.2.0 → 2.2.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  What changed in each version of StremioK, newest first. How everything works is in the
4
4
  [user guide](docs/USER-GUIDE.md).
5
5
 
6
+ ## 2.2.1 — 2026-10-24
7
+ - **Every language OpenSubtitles uses** (94, Indonesian among them, #1) for subtitles and audio:
8
+ to pick as your default, second choice or second subtitle, listed by name, and each track
9
+ shown by its language's name ("Pan" is Punjabi).
10
+ - **Only my languages** (Settings › Playback › Subtitles, *Off* by default): the subtitle menu
11
+ lists just your languages, and tracks that don't say theirs; *Other languages* at its end has
12
+ the rest.
13
+ - **Shorter settings pages:** the subtitles' size, color, background, edge, position and font are
14
+ on a page of their own (*Appearance*); Source ranking keeps *Played before first* above its
15
+ order, with *Filters* and *When you press Play* as pages of their own.
16
+ - **Hold OK on a program in the Guide tab:** *▶ Watch now*, *★ Add to / Remove from favorites*
17
+ for its channel, and *Find in your addons* for that program.
18
+ - **Fixed:** back on the Guide tab after CH+ / CH− lands on the channel you ended on, at the time
19
+ now, instead of the first channel played.
20
+
6
21
  ## 2.2.0 — 2026-10-23
7
22
  - **A TV guide for your IPTV channels:** what's on now and next on each channel's card, in
8
23
  Home's details strip and while a channel plays, and its schedule when you hold OK on it. From
package/README.md CHANGED
@@ -16,6 +16,7 @@ video through Samsung's AVPlay API.
16
16
  - **[Changelog](CHANGELOG.md)**: what changed in each version.
17
17
  - **[Developer guide](docs/DEVELOPER-GUIDE.md)**: how it's put together, and running it in a
18
18
  browser.
19
+ - **[Security policy](docs/SECURITY.md)**: reporting a vulnerability, privately.
19
20
 
20
21
  ## Highlights
21
22
 
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://stremiok.tizen/StremioKTizen" version="2.2.0" viewmodes="maximized">
3
+ id="http://stremiok.tizen/StremioKTizen" version="2.2.1" viewmodes="maximized">
4
4
  <tizen:application id="StremioK01.StremioK" package="StremioK01" 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>StremioK</title>
7
- <link rel="stylesheet" href="css/style.css?v=2.2.0">
7
+ <link rel="stylesheet" href="css/style.css?v=2.2.1">
8
8
  </head>
9
9
  <body>
10
10
  <!-- CSS-applied sentinel (kept off-screen); app.js waits for its width before rendering -->
@@ -53,37 +53,37 @@
53
53
 
54
54
  <!-- Load order matters: polyfills -> util -> iptv -> data -> input -> tvapps -> player parts -> player -> ytplay -> view parts -> views -> app -->
55
55
  <!-- ?v= busts the TV webview cache on every release (keep in sync with package.json) -->
56
- <script src="js/polyfills.js?v=2.2.0"></script>
57
- <script src="js/util.js?v=2.2.0"></script>
58
- <script src="js/iptv.js?v=2.2.0"></script>
59
- <script src="js/epg.js?v=2.2.0"></script>
60
- <script src="js/youtube.js?v=2.2.0"></script>
61
- <script src="js/stremio.js?v=2.2.0"></script>
62
- <script src="js/trakt.js?v=2.2.0"></script>
63
- <script src="js/keys.js?v=2.2.0"></script>
64
- <script src="js/focus.js?v=2.2.0"></script>
65
- <script src="js/tvapps.js?v=2.2.0"></script>
66
- <script src="js/player-core.js?v=2.2.0"></script>
67
- <script src="js/player-store.js?v=2.2.0"></script>
68
- <script src="js/player-lang.js?v=2.2.0"></script>
69
- <script src="js/player-quality.js?v=2.2.0"></script>
70
- <script src="js/player-subs.js?v=2.2.0"></script>
71
- <script src="js/player-audio.js?v=2.2.0"></script>
72
- <script src="js/player-controls.js?v=2.2.0"></script>
73
- <script src="js/player.js?v=2.2.0"></script>
74
- <script src="js/ytplay.js?v=2.2.0"></script>
75
- <script src="js/local.js?v=2.2.0"></script>
76
- <script src="js/downloads.js?v=2.2.0"></script>
77
- <script src="js/stremio-account.js?v=2.2.0"></script>
78
- <script src="js/view-core.js?v=2.2.0"></script>
79
- <script src="js/view-home.js?v=2.2.0"></script>
80
- <script src="js/view-guide.js?v=2.2.0"></script>
81
- <script src="js/view-detail.js?v=2.2.0"></script>
82
- <script src="js/view-search.js?v=2.2.0"></script>
83
- <script src="js/view-settings.js?v=2.2.0"></script>
84
- <script src="js/view-discover.js?v=2.2.0"></script>
85
- <script src="js/views.js?v=2.2.0"></script>
86
- <script src="js/app.js?v=2.2.0"></script>
87
- <script src="js/hubpreview.js?v=2.2.0"></script>
56
+ <script src="js/polyfills.js?v=2.2.1"></script>
57
+ <script src="js/util.js?v=2.2.1"></script>
58
+ <script src="js/iptv.js?v=2.2.1"></script>
59
+ <script src="js/epg.js?v=2.2.1"></script>
60
+ <script src="js/youtube.js?v=2.2.1"></script>
61
+ <script src="js/stremio.js?v=2.2.1"></script>
62
+ <script src="js/trakt.js?v=2.2.1"></script>
63
+ <script src="js/keys.js?v=2.2.1"></script>
64
+ <script src="js/focus.js?v=2.2.1"></script>
65
+ <script src="js/tvapps.js?v=2.2.1"></script>
66
+ <script src="js/player-core.js?v=2.2.1"></script>
67
+ <script src="js/player-store.js?v=2.2.1"></script>
68
+ <script src="js/player-lang.js?v=2.2.1"></script>
69
+ <script src="js/player-quality.js?v=2.2.1"></script>
70
+ <script src="js/player-subs.js?v=2.2.1"></script>
71
+ <script src="js/player-audio.js?v=2.2.1"></script>
72
+ <script src="js/player-controls.js?v=2.2.1"></script>
73
+ <script src="js/player.js?v=2.2.1"></script>
74
+ <script src="js/ytplay.js?v=2.2.1"></script>
75
+ <script src="js/local.js?v=2.2.1"></script>
76
+ <script src="js/downloads.js?v=2.2.1"></script>
77
+ <script src="js/stremio-account.js?v=2.2.1"></script>
78
+ <script src="js/view-core.js?v=2.2.1"></script>
79
+ <script src="js/view-home.js?v=2.2.1"></script>
80
+ <script src="js/view-guide.js?v=2.2.1"></script>
81
+ <script src="js/view-detail.js?v=2.2.1"></script>
82
+ <script src="js/view-search.js?v=2.2.1"></script>
83
+ <script src="js/view-settings.js?v=2.2.1"></script>
84
+ <script src="js/view-discover.js?v=2.2.1"></script>
85
+ <script src="js/views.js?v=2.2.1"></script>
86
+ <script src="js/app.js?v=2.2.1"></script>
87
+ <script src="js/hubpreview.js?v=2.2.1"></script>
88
88
  </body>
89
89
  </html>
package/js/app.js CHANGED
@@ -73,14 +73,17 @@ var App = (function () {
73
73
  // Close a live player directly back to the screen/card that launched it. The
74
74
  // originating screen stays mounted while the player is open, so restoring its
75
75
  // saved focus node also preserves the grid's horizontal and vertical scroll.
76
- // focusNode: the card to land on instead (the channel you switched to with CH+/CH−).
77
- function returnFromLive(focusNode) {
76
+ // focusNode: the card to land on instead (the channel you switched to with CH+/CH−); liveId:
77
+ // that channel, for the Guide tab, which lands on it at the time now (Views.guideBackOn: its
78
+ // cells from when it was played may be gone, or show hours long past).
79
+ function returnFromLive(focusNode, liveId) {
78
80
  var f = _stack.pop();
79
81
  if (!f) { goHome(); return; }
80
82
  if (f.screen !== _current) { abandon(_current); }
81
83
  showScreen(f.screen);
84
+ var guide = liveId && f.screen === 'home' && Views.guideBackOn ? Views.guideBackOn(liveId) : null;
82
85
  var alt = focusNode && document.documentElement.contains(focusNode) ? focusNode : null;
83
- Focus.setScope(screenEl(f.screen), alt || f.focus);
86
+ Focus.setScope(screenEl(f.screen), guide || alt || f.focus);
84
87
  }
85
88
 
86
89
  // After a screen is drawn, focus must be on it. A drawing path that doesn't put it there
package/js/player-lang.js CHANGED
@@ -34,7 +34,7 @@
34
34
  fre: ['fre', 'fra', 'fr', 'french', 'français', 'francais'],
35
35
  ger: ['ger', 'deu', 'de', 'german', 'deutsch'],
36
36
  ita: ['ita', 'it', 'italian', 'italiano'],
37
- por: ['por', 'pob', 'pt', 'portuguese', 'português', 'portugues'],
37
+ por: ['por', 'pob', 'pom', 'pt', 'portuguese', 'português', 'portugues'],
38
38
  dut: ['dut', 'nld', 'nl', 'dutch', 'nederlands'],
39
39
  rus: ['rus', 'ru', 'russian'],
40
40
  ara: ['ara', 'ar', 'arabic'],
@@ -42,7 +42,7 @@
42
42
  pol: ['pol', 'pl', 'polish', 'polski'],
43
43
  jpn: ['jpn', 'ja', 'japanese', '日本語'],
44
44
  kor: ['kor', 'ko', 'korean', '한국어'],
45
- chi: ['chi', 'zho', 'zh', 'cmn', 'chinese', 'mandarin', '中文'],
45
+ chi: ['chi', 'zho', 'zh', 'cmn', 'zht', 'zhe', 'chinese', 'mandarin', '中文'], // (zht / zhe: OpenSubtitles' traditional and bilingual)
46
46
  hin: ['hin', 'hi', 'hindi'],
47
47
  swe: ['swe', 'sv', 'swedish', 'svenska'],
48
48
  nor: ['nor', 'nob', 'nno', 'nb', 'nn', 'norwegian', 'norsk'],
@@ -58,7 +58,7 @@
58
58
  ukr: ['ukr', 'uk', 'ukrainian'],
59
59
  tha: ['tha', 'th', 'thai'],
60
60
  vie: ['vie', 'vi', 'vietnamese'],
61
- ind: ['ind', 'id', 'indonesian'],
61
+ ind: ['ind', 'id', 'indonesian', 'indonesia'], // ("Bahasa Indonesia": its last word)
62
62
  may: ['may', 'msa', 'ms', 'malay'],
63
63
  fil: ['fil', 'tgl', 'tl', 'filipino', 'tagalog'],
64
64
  cat: ['cat', 'ca', 'catalan', 'català'],
@@ -66,7 +66,7 @@
66
66
  glg: ['glg', 'gl', 'galician', 'galego'],
67
67
  bul: ['bul', 'bg', 'bulgarian'],
68
68
  hrv: ['hrv', 'hr', 'croatian', 'hrvatski'],
69
- srp: ['srp', 'sr', 'serbian'],
69
+ srp: ['srp', 'scc', 'sr', 'serbian'],
70
70
  per: ['per', 'fas', 'fa', 'persian', 'farsi'],
71
71
  est: ['est', 'et', 'estonian'],
72
72
  lav: ['lav', 'lv', 'latvian'],
@@ -74,7 +74,57 @@
74
74
  ice: ['ice', 'isl', 'is', 'icelandic'],
75
75
  tam: ['tam', 'ta', 'tamil'],
76
76
  tel: ['tel', 'te', 'telugu'],
77
- ben: ['ben', 'bn', 'bengali']
77
+ ben: ['ben', 'bn', 'bengali'],
78
+ // (2.2.0.2) the rest of OpenSubtitles' languages; two-letter codes that are also everyday
79
+ // words or labels (my, be, or, so, an, se, si, ne, mr, SD, BR…) left out
80
+ afr: ['afr', 'af', 'afrikaans'],
81
+ alb: ['alb', 'sqi', 'sq', 'albanian', 'shqip'],
82
+ arg: ['arg', 'aragonese'],
83
+ arm: ['arm', 'hye', 'hy', 'armenian'],
84
+ ast: ['ast', 'asturian'],
85
+ aze: ['aze', 'az', 'azerbaijani'],
86
+ bel: ['bel', 'belarusian'],
87
+ bos: ['bos', 'bs', 'bosnian'],
88
+ bre: ['bre', 'breton'],
89
+ bur: ['bur', 'mya', 'burmese'],
90
+ prs: ['prs', 'dari'],
91
+ epo: ['epo', 'eo', 'esperanto'],
92
+ ext: ['ext', 'extremaduran'],
93
+ gla: ['gla', 'gd', 'gaelic'],
94
+ geo: ['geo', 'kat', 'ka', 'georgian'],
95
+ ibo: ['ibo', 'ig', 'igbo'],
96
+ ina: ['ina', 'interlingua'],
97
+ gle: ['gle', 'ga', 'irish'],
98
+ kan: ['kan', 'kn', 'kannada'],
99
+ kaz: ['kaz', 'kk', 'kazakh'],
100
+ khm: ['khm', 'km', 'khmer'],
101
+ kur: ['kur', 'ku', 'kurdish'],
102
+ ltz: ['ltz', 'lb', 'luxembourgish'],
103
+ mac: ['mac', 'mkd', 'mk', 'macedonian'],
104
+ mal: ['mal', 'ml', 'malayalam'],
105
+ mni: ['mni', 'manipuri'],
106
+ mar: ['mar', 'marathi'],
107
+ mon: ['mon', 'mn', 'mongolian'],
108
+ mne: ['mne', 'montenegrin'],
109
+ nav: ['nav', 'nv', 'navajo'],
110
+ nep: ['nep', 'nepali'],
111
+ sme: ['sme', 'sami'],
112
+ oci: ['oci', 'oc', 'occitan'],
113
+ ori: ['ori', 'odia', 'oriya'],
114
+ pan: ['pan', 'pa', 'punjabi', 'panjabi'],
115
+ pus: ['pus', 'pashto'],
116
+ sat: ['sat', 'santali'],
117
+ snd: ['snd', 'sindhi'],
118
+ sin: ['sin', 'sinhala', 'sinhalese'],
119
+ som: ['som', 'somali'],
120
+ swa: ['swa', 'sw', 'swahili'],
121
+ syr: ['syr', 'syriac'],
122
+ tat: ['tat', 'tt', 'tatar'],
123
+ tok: ['tok', 'toki'],
124
+ tuk: ['tuk', 'tk', 'turkmen'],
125
+ urd: ['urd', 'ur', 'urdu'],
126
+ uzb: ['uzb', 'uz', 'uzbek'],
127
+ wel: ['wel', 'cym', 'cy', 'welsh']
78
128
  };
79
129
  function langMatches(pref, lang) {
80
130
  var names = LANG_ALIASES[pref] || [pref];
@@ -97,6 +147,14 @@
97
147
  per: 'Persian', fas: 'Persian', fa: 'Persian', slo: 'Slovak', slv: 'Slovenian', may: 'Malay', fil: 'Filipino',
98
148
  baq: 'Basque', glg: 'Galician', est: 'Estonian', lav: 'Latvian', lit: 'Lithuanian', ice: 'Icelandic',
99
149
  tam: 'Tamil', tel: 'Telugu', ben: 'Bengali', mul: 'Multiple languages', zxx: 'No language',
150
+ afr: 'Afrikaans', alb: 'Albanian', arg: 'Aragonese', arm: 'Armenian', ast: 'Asturian', aze: 'Azerbaijani',
151
+ bel: 'Belarusian', bos: 'Bosnian', bre: 'Breton', bur: 'Burmese', prs: 'Dari', epo: 'Esperanto', ext:
152
+ 'Extremaduran', gla: 'Scottish Gaelic', geo: 'Georgian', ibo: 'Igbo', ina: 'Interlingua', gle: 'Irish', kan:
153
+ 'Kannada', kaz: 'Kazakh', khm: 'Khmer', kur: 'Kurdish', ltz: 'Luxembourgish', mac: 'Macedonian', mal:
154
+ 'Malayalam', mni: 'Manipuri', mar: 'Marathi', mon: 'Mongolian', mne: 'Montenegrin', nav: 'Navajo', nep:
155
+ 'Nepali', sme: 'Northern Sami', oci: 'Occitan', ori: 'Odia', pan: 'Punjabi', pus: 'Pashto', sat: 'Santali',
156
+ snd: 'Sindhi', sin: 'Sinhala', som: 'Somali', swa: 'Swahili', syr: 'Syriac', tat: 'Tatar', tok: 'Toki Pona',
157
+ tuk: 'Turkmen', urd: 'Urdu', uzb: 'Uzbek', wel: 'Welsh',
100
158
  und: 'Unknown language', '': 'Unknown language' };
101
159
  function langName(lang) {
102
160
  var k = langKey(lang);
@@ -122,6 +180,9 @@
122
180
  // ----- for the other parts (and js/player.js) -----
123
181
  P.trackInfo = trackInfo; P.infoLang = infoLang; P.trackLang = trackLang; P.langMatches = langMatches;
124
182
  P.langName = langName; P.langRank = langRank; P.langKey = langKey;
183
+ // A track that doesn't say its language (by its key): none, "und", "unknown"…, or no letters.
184
+ // (A language StremioK has no name for is still a language.)
185
+ P.langUnstated = function (k) { k = ('' + (k || '')).trim(); return !/[a-zà-ÿ]/i.test(k) || /^(und|unk|unknown|unknown language|mul|zxx|n\/a|none)$/i.test(k); };
125
186
  // A language code's two-letter form (ISO 639-1: "spa" -> "es"), or '' (TMDB asks by it).
126
187
  P.lang2 = function (key) { var a = LANG_ALIASES[key] || []; for (var i = 0; i < a.length; i++) { if (/^[a-z]{2}$/.test(a[i])) { return a[i]; } } return ''; };
127
188
  })(PlayerParts);
@@ -85,7 +85,8 @@
85
85
  edge: pick3(SUB_EDGES, d.edge)[0], pos: pick3(SUB_POSITIONS, d.pos || 'bottom')[0], // (Bottom unless chosen: Lower comes first in the list)
86
86
  font: subFontFamily(d.font) ? d.font : 'default', // an unknown font -> TV default
87
87
  skip: skipWords(d.skip), // lines with one of these aren't shown (none by default: see player-subs' skipLines)
88
- second: typeof d.second === 'string' && /^[a-z]{2,3}$/.test(d.second) ? d.second : 'off' }; // a second subtitle at the top (player-subs; Off by default)
88
+ second: typeof d.second === 'string' && /^[a-z]{2,3}$/.test(d.second) ? d.second : 'off', // a second subtitle at the top (player-subs; Off by default)
89
+ only: d.only === 'on' ? 'on' : 'off' }; // the subtitle menu: only your languages (player-subs' langMenu; Off by default)
89
90
  }
90
91
  // Settings › Subtitles › Skip lines with: words (or bits, ".srt"), capitals or not; at most 20.
91
92
  function skipWords(x) {
package/js/player-subs.js CHANGED
@@ -583,19 +583,32 @@
583
583
  // Level 1: one entry per language, spellings merged ("en", "eng" and "English" are one
584
584
  // group), named in full, with the file's own tracks in the same groups as the addons'.
585
585
  // Order: the language showing now, the preferred language, then by name.
586
- function langMenu() {
586
+ // Only my languages (Settings, Off by default): just your default, second-choice and
587
+ // second-subtitle languages, the one showing, and tracks that don't say their language;
588
+ // Other languages, after them, lists the rest (all: that list).
589
+ function langMenu(all) {
587
590
  var groups = Object.create(null), order = []; // (no inherited names: a language an addon calls "__proto__" is just a key)
591
+ function key(l) { var k = langKey(l); return P.langUnstated(k) ? '' : k; } // (no language, "und"…: one Unknown language)
588
592
  function add(k, item) {
589
593
  if (!groups[k]) { groups[k] = []; order.push(k); }
590
594
  groups[k].push(item);
591
595
  }
592
- getTextTracks().forEach(function (tr, i) { add(langKey(textLang(tr)), { emb: tr, n: i }); });
596
+ getTextTracks().forEach(function (tr, i) { add(key(textLang(tr)), { emb: tr, n: i }); });
593
597
  var offer = S.ctx && !S.ctx._relayCc ? S.ctx._ccOffer : null; // (a live channel's closed captions, not on yet)
594
- (offer || []).forEach(function (c, i) { add(langKey(c.lang || ''), { cc: c, n: i, emb: true }); });
595
- _subTracks.forEach(function (tr) { add(langKey(tr.lang), { track: tr }); });
596
- var curKey = _subOn ? langKey(_subLabel) : (_embSub != null ? langKey(_embLang) : null);
598
+ (offer || []).forEach(function (c, i) { add(key(c.lang || ''), { cc: c, n: i, emb: true }); });
599
+ _subTracks.forEach(function (tr) { add(key(tr.lang), { track: tr }); });
600
+ var curKey = _subOn ? key(_subLabel) : (_embSub != null ? key(_embLang) : null);
597
601
  var pref = getSubCfg().lang;
598
602
  var prefs = [curKey, pref].filter(function (x) { return x && x !== 'off'; });
603
+ var cfg = getSubCfg(), hidden = 0;
604
+ var mine = [cfg.lang, cfg.lang2, cfg.second].filter(function (l) { return l && l !== 'off' && l !== 'none'; });
605
+ if (cfg.only === 'on' && !all && mine.length) {
606
+ order = order.filter(function (k) {
607
+ var keep = k === curKey || k === '' || mine.some(function (l) { return langKey(l) === k || langMatches(l, k); });
608
+ if (!keep) { hidden++; }
609
+ return keep;
610
+ });
611
+ }
599
612
  order.sort(function (a, b) {
600
613
  var ra = langRank(a, prefs), rb = langRank(b, prefs);
601
614
  if (ra !== rb) { return ra - rb; }
@@ -610,11 +623,12 @@
610
623
  sel: (k === curKey),
611
624
  run: function () { pushMenu(langTracksMenu(k, list)); } });
612
625
  });
626
+ if (hidden) { items.push({ label: 'Other languages', detail: hidden + ' more', run: function () { pushMenu(langMenu(true)); } }); }
613
627
  var sec = getSubCfg().second;
614
628
  if (_subTracks.length) { items.push({ label: 'Second subtitle: ' + (sec === 'off' ? 'Off' : langName(sec)), detail: 'At the top, at the same time', run: function () { pushMenu(secondMenu()); } }); }
615
629
  var idx = 0;
616
630
  for (var i = 0; i < items.length; i++) { if (items[i].sel) { idx = i; break; } }
617
- return { title: 'Subtitle language', items: items, idx: idx, rebuild: langMenu };
631
+ return { title: all ? 'All subtitle languages' : 'Subtitle language', items: items, idx: idx, rebuild: function () { return langMenu(all); } };
618
632
  }
619
633
 
620
634
  function inFile() { return S.ctx && (S.ctx._relayCc || S.ctx._ccOffer) ? 'closed captions' : 'in the file'; }
package/js/view-detail.js CHANGED
@@ -2039,7 +2039,7 @@
2039
2039
  if (_d) { _d.direct = false; _d.directList = false; _d.directFailed = false; } // (only this once: played again from the list, back to the list)
2040
2040
  var onExit = function (how) {
2041
2041
  // (after CH+/CH−, back on the card of the channel you ended on)
2042
- if (exitToBack) { App.returnFromLive(_d.channels ? _d.channels.list[_d.channels.idx].card : null); return; }
2042
+ if (exitToBack) { var ch = _d.channels ? _d.channels.list[_d.channels.idx] : null; App.returnFromLive(ch ? ch.card : null, ch ? ch.meta.id : null); return; }
2043
2043
  // played straight away (a single YouTube video): back where it was opened from
2044
2044
  if (direct && !page.directFailed && _d === page) { App.back(); return; } // (Home redrawn if Continue Watching changed)
2045
2045
  // a channel's video played straight away: back on the channel's videos, on it
package/js/view-guide.js CHANGED
@@ -7,10 +7,13 @@
7
7
  an edge, never back before the current half hour for one still on), ↑ / ↓ the channel above /
8
8
  below, at the same time. Home's details strip describes the focused program; OK on what's on
9
9
  now plays the channel, on another says what it is. A guide-only channel's (tvg-type="guide":
10
- watched elsewhere) any program: the film looked up in your addons (findInAddons). */
10
+ watched elsewhere) any program: the film looked up in your addons (findInAddons). Hold OK on
11
+ a program: Watch now, the channel's favorites, Find in your addons (hold). */
11
12
  (function (V) {
12
13
  'use strict';
13
14
  function scr() { return V.scr.apply(null, arguments); } // core
15
+ function pickList() { return V.pickList.apply(null, arguments); } // core
16
+ function refreshAddonRows() { return V.refreshAddonRows.apply(null, arguments); } // home
14
17
 
15
18
  var SPAN = 150 * 60, STEP = 30 * 60, ROWS = 10, AHEAD = 10;
16
19
  var _g = null; // the grid on screen: { el, start, chans, lists: { id: programs }, asked: { id: 1 }, top, nodes: [row: cells], box }
@@ -165,6 +168,7 @@
165
168
  wait.__t = want && want.id === c.id && typeof want.t === 'number' ? want.t : Math.max(now, g.start);
166
169
  wait.__meta = { name: c.name, releaseInfo: 'Reading its TV guide…' };
167
170
  hooks(wait, -1);
171
+ wait.__onlongpress = function () { hold(g, r, null); };
168
172
  track.appendChild(wait);
169
173
  nodes.push(wait);
170
174
  if (want && want.id === c.id) { found = wait; }
@@ -177,6 +181,7 @@
177
181
  n.__meta = { name: p.title, releaseInfo: c.name + ' · ' + EPG.dayTime(p.start) + '–' + EPG.hhmm(p.stop) + (c.guideOnly ? ' · OK: find it in your addons' : ''), description: p.desc, picture: p.picture };
178
182
  n.__p = p; n.__i = i;
179
183
  n.__onselect = function () { pick(g, r, p); };
184
+ n.__onlongpress = function () { hold(g, r, p); };
180
185
  hooks(n, i);
181
186
  track.appendChild(n);
182
187
  nodes.push(n);
@@ -301,6 +306,23 @@
301
306
  (p.stop <= now ? ' (already shown)' : ''), 8000);
302
307
  }
303
308
 
309
+ // Hold OK on a program: ▶ Watch now (what's on now, a channel that plays), ★ Add to / Remove
310
+ // from favorites (its channel), Find in your addons (that program, as a guide-only channel's
311
+ // OK). p: null for a channel whose guide hasn't come (its favorites only).
312
+ function hold(g, r, p) {
313
+ var c = g.chans[r], now = Date.now() / 1000, fav = IPTV.isFavorite(c.id), items = [];
314
+ if (p && !c.guideOnly && p.start <= now && now < p.stop) { items.push(['play', '▶ Watch now']); }
315
+ items.push(['fav', fav ? '★ Remove from favorites' : '★ Add to favorites']);
316
+ if (p && p.title) { items.push(['find', '🔍 Find in your addons']); }
317
+ pickList(c.name + (p && p.title ? ' · ' + p.title : ''), items, items[0][0], function (v) {
318
+ if (v === 'play') { pick(g, r, p); return; }
319
+ if (v === 'find') { findInAddons(p); return; }
320
+ IPTV.setFavorite(c.id, !fav);
321
+ U.toast(fav ? 'Removed from favorites' : 'Added to favorites: ★ Favorites, first on the IPTV and Guide tabs', 3000);
322
+ refreshAddonRows('local:iptv');
323
+ });
324
+ }
325
+
304
326
  // A guide-only channel's program: a film found by the guide's original title and year (else
305
327
  // its title) opens its page with your addons' sources, as Discover does; a show, or a film
306
328
  // without one sure match, opens Search with its name.
@@ -328,5 +350,22 @@
328
350
  Stremio.findFilm({ title: p.title, orig: p.orig, year: p.year }, cancel).then(done, function () { done(null); });
329
351
  }
330
352
 
331
- V.guideGrid = guideGrid;
353
+ // Back on the Guide from a channel played from it (BACK, or its stream gave up), after CH+ /
354
+ // CH−: the hours at the present again, the row of the channel ended on in view (read if it
355
+ // wasn't), and what's on now on it. Returns that cell; null when the Guide isn't on screen or
356
+ // the channel isn't on it.
357
+ function guideBackOn(id) {
358
+ var g = _g, r = -1;
359
+ if (!id || !g || !g.box || !document.documentElement.contains(g.box)) { return null; }
360
+ for (var i = 0; i < g.chans.length; i++) { if (g.chans[i].id === id) { r = i; break; } }
361
+ if (r < 0) { return null; }
362
+ var now = Date.now() / 1000;
363
+ g.start = floorStep(now); g.live = true;
364
+ if (r < g.top) { g.top = r; } else if (r >= g.top + ROWS) { g.top = r - ROWS + 1; }
365
+ var n = draw(g, { id: id, t: now });
366
+ ensure(g).then(function (got) { if (got) { refresh(g); } }); // (its programs, when they weren't read: then onto what's on)
367
+ return n;
368
+ }
369
+
370
+ V.guideGrid = guideGrid; V.guideBackOn = guideBackOn;
332
371
  })(ViewParts);
@@ -98,11 +98,35 @@
98
98
  }
99
99
 
100
100
  // ================= SUBTITLE CONFIG =================
101
- var SUB_LANGS = [['off', 'Off'], ['eng', 'English'], ['spa', 'Spanish'], ['fre', 'French'],
102
- ['ger', 'German'], ['ita', 'Italian'], ['por', 'Portuguese'], ['dut', 'Dutch'],
103
- ['rus', 'Russian'], ['ara', 'Arabic'], ['tur', 'Turkish'], ['pol', 'Polish']];
101
+ // Every language player-lang recognizes on a track (its LANG_ALIASES), by name.
102
+ var SUB_LANGS = [['off', 'Off'], ['afr', 'Afrikaans'], ['alb', 'Albanian'], ['ara', 'Arabic'],
103
+ ['arg', 'Aragonese'], ['arm', 'Armenian'], ['ast', 'Asturian'], ['aze', 'Azerbaijani'],
104
+ ['baq', 'Basque'], ['bel', 'Belarusian'], ['ben', 'Bengali'], ['bos', 'Bosnian'],
105
+ ['bre', 'Breton'], ['bul', 'Bulgarian'], ['bur', 'Burmese'], ['cat', 'Catalan'],
106
+ ['chi', 'Chinese'], ['hrv', 'Croatian'], ['cze', 'Czech'], ['dan', 'Danish'], ['prs', 'Dari'],
107
+ ['dut', 'Dutch'], ['eng', 'English'], ['epo', 'Esperanto'], ['est', 'Estonian'],
108
+ ['ext', 'Extremaduran'], ['fil', 'Filipino'], ['fin', 'Finnish'], ['fre', 'French'],
109
+ ['glg', 'Galician'], ['geo', 'Georgian'], ['ger', 'German'], ['gre', 'Greek'],
110
+ ['heb', 'Hebrew'], ['hin', 'Hindi'], ['hun', 'Hungarian'], ['ice', 'Icelandic'],
111
+ ['ibo', 'Igbo'], ['ind', 'Indonesian'], ['ina', 'Interlingua'], ['gle', 'Irish'],
112
+ ['ita', 'Italian'], ['jpn', 'Japanese'], ['kan', 'Kannada'], ['kaz', 'Kazakh'],
113
+ ['khm', 'Khmer'], ['kor', 'Korean'], ['kur', 'Kurdish'], ['lav', 'Latvian'],
114
+ ['lit', 'Lithuanian'], ['ltz', 'Luxembourgish'], ['mac', 'Macedonian'], ['may', 'Malay'],
115
+ ['mal', 'Malayalam'], ['mni', 'Manipuri'], ['mar', 'Marathi'], ['mon', 'Mongolian'],
116
+ ['mne', 'Montenegrin'], ['nav', 'Navajo'], ['nep', 'Nepali'], ['sme', 'Northern Sami'],
117
+ ['nor', 'Norwegian'], ['oci', 'Occitan'], ['ori', 'Odia'], ['pus', 'Pashto'],
118
+ ['per', 'Persian'], ['pol', 'Polish'], ['por', 'Portuguese'], ['pan', 'Punjabi'],
119
+ ['rum', 'Romanian'], ['rus', 'Russian'], ['sat', 'Santali'], ['gla', 'Scottish Gaelic'],
120
+ ['srp', 'Serbian'], ['snd', 'Sindhi'], ['sin', 'Sinhala'], ['slo', 'Slovak'],
121
+ ['slv', 'Slovenian'], ['som', 'Somali'], ['spa', 'Spanish'], ['swa', 'Swahili'],
122
+ ['swe', 'Swedish'], ['syr', 'Syriac'], ['tam', 'Tamil'], ['tat', 'Tatar'], ['tel', 'Telugu'],
123
+ ['tha', 'Thai'], ['tok', 'Toki Pona'], ['tur', 'Turkish'], ['tuk', 'Turkmen'],
124
+ ['ukr', 'Ukrainian'], ['urd', 'Urdu'], ['uzb', 'Uzbek'], ['vie', 'Vietnamese'],
125
+ ['wel', 'Welsh']];
104
126
  // A second choice, turned on when nothing is in the default language (from any source).
105
127
  var SUB_LANGS2 = [['none', 'None']].concat(SUB_LANGS.slice(1));
128
+ // The subtitle menu with only the three languages above (and tracks of no known language).
129
+ var SUB_ONLY = [['off', 'Off'], ['on', 'On']];
106
130
  var SUB_SIZES = [['small', 'Small'], ['medium', 'Medium'], ['large', 'Large'], ['xlarge', 'Extra Large']];
107
131
 
108
132
  function labelFor(list, v) {
@@ -338,7 +362,7 @@
338
362
  // BACK goes up one level, onto the row it came from: from a section to its group (on
339
363
  // that section, not the first one), from Home feeds / Search results to Appearance,
340
364
  // from a group to the Settings list (on that group).
341
- var SECTION_PARENT = { menuorder: 'appearance', feeds: 'appearance', searchfeeds: 'appearance', aiomanager: 'addons' };
365
+ var SECTION_PARENT = { menuorder: 'appearance', feeds: 'appearance', searchfeeds: 'appearance', aiomanager: 'addons', subtitlelook: 'subtitles', rankfilters: 'ranking', rankplay: 'ranking' };
342
366
  Object.keys(SETTINGS_GROUPS).forEach(function (g) {
343
367
  SETTINGS_GROUPS[g].rows.forEach(function (row) { SECTION_PARENT[row[2]] = g; });
344
368
  });
@@ -365,12 +389,15 @@
365
389
  else if (_settingsSection === 'integrations') { settingsIntegrations(wrap); }
366
390
  else if (_settingsSection === 'audio') { settingsAudio(wrap); }
367
391
  else if (_settingsSection === 'subtitles') { settingsSubtitles(wrap); }
392
+ else if (_settingsSection === 'subtitlelook') { settingsSubtitleLook(wrap); }
368
393
  else if (_settingsSection === 'skipping') { settingsSkipping(wrap); }
369
394
  else if (_settingsSection === 'seeking') { settingsSeeking(wrap); }
370
395
  else if (_settingsSection === 'menuorder') { settingsFeeds(wrap, 'menu'); }
371
396
  else if (_settingsSection === 'feeds') { settingsFeeds(wrap, 'home'); }
372
397
  else if (_settingsSection === 'searchfeeds') { settingsFeeds(wrap, 'search'); }
373
398
  else if (_settingsSection === 'ranking') { settingsFeeds(wrap, 'ranking'); }
399
+ else if (_settingsSection === 'rankfilters') { settingsRankFilters(wrap); }
400
+ else if (_settingsSection === 'rankplay') { settingsRankPlay(wrap); }
374
401
  else if (SETTINGS_GROUPS[_settingsSection]) { settingsGroup(wrap, _settingsSection); }
375
402
  else if (_settingsSection === 'logs') { settingsLogs(wrap); }
376
403
  else if (_settingsSection === 'storage') { settingsStorage(wrap); }
@@ -1399,14 +1426,7 @@
1399
1426
  wrap.appendChild(subPickRow('Default language', SUB_LANGS, 'lang'));
1400
1427
  wrap.appendChild(subPickRow('Second choice', SUB_LANGS2, 'lang2'));
1401
1428
  wrap.appendChild(subPickRow('Second subtitle (at the top)', SUB_LANGS, 'second'));
1402
- wrap.appendChild(subPickRow('Size', SUB_SIZES, 'size'));
1403
- // [id, label] for the picker (colors with a swatch)
1404
- function plain(list) { return list.map(function (x) { return [x[0], x[1]]; }); }
1405
- wrap.appendChild(subPickRow('Color', Player.SUB_COLORS.map(function (x) { return [x[0], x[1], null, x[2]]; }), 'color'));
1406
- wrap.appendChild(subPickRow('Background', plain(Player.SUB_BGS), 'bg'));
1407
- wrap.appendChild(subPickRow('Edge', plain(Player.SUB_EDGES), 'edge'));
1408
- wrap.appendChild(subPickRow('Position', plain(Player.SUB_POSITIONS), 'pos'));
1409
- wrap.appendChild(subPickRow('Font', Player.SUB_FONTS, 'font'));
1429
+ wrap.appendChild(subPickRow('Only my languages', SUB_ONLY, 'only'));
1410
1430
  // lines to leave out: those with one of your words (a subtitle site's ads, a file's name…)
1411
1431
  var skip = Player.getSubCfg().skip || [];
1412
1432
  var sk = U.el('div', 'addon-item focusable cfg-row');
@@ -1419,6 +1439,21 @@
1419
1439
  onConfirm: function (v) { var c = Player.getSubCfg(); c.skip = ('' + v).split(','); Player.setSubCfg(c); App.back(); renderSettings(); } });
1420
1440
  };
1421
1441
  wrap.appendChild(sk);
1442
+ var look = sectionRow('Appearance ›', 'Size, color, background, edge, position, font', 'subtitlelook'); // (their own page: settingsSubtitleLook)
1443
+ wrap.appendChild(look);
1444
+ if (_focusChild === 'subtitlelook') { _focusChild = null; Focus.setScope(scr('settings'), look); } else { focusSettings(); }
1445
+ }
1446
+ // Subtitles › Appearance: how subtitles look.
1447
+ function settingsSubtitleLook(wrap) {
1448
+ wrap.appendChild(U.el('h2', null, 'Subtitle appearance'));
1449
+ wrap.appendChild(subPickRow('Size', SUB_SIZES, 'size'));
1450
+ // [id, label] for the picker (colors with a swatch)
1451
+ function plain(list) { return list.map(function (x) { return [x[0], x[1]]; }); }
1452
+ wrap.appendChild(subPickRow('Color', Player.SUB_COLORS.map(function (x) { return [x[0], x[1], null, x[2]]; }), 'color'));
1453
+ wrap.appendChild(subPickRow('Background', plain(Player.SUB_BGS), 'bg'));
1454
+ wrap.appendChild(subPickRow('Edge', plain(Player.SUB_EDGES), 'edge'));
1455
+ wrap.appendChild(subPickRow('Position', plain(Player.SUB_POSITIONS), 'pos'));
1456
+ wrap.appendChild(subPickRow('Font', Player.SUB_FONTS, 'font'));
1422
1457
  focusSettings();
1423
1458
  }
1424
1459
 
@@ -1928,7 +1963,7 @@
1928
1963
  list: function () { return Promise.resolve(Stremio.getRanking().criteria); }, keyOf: function (c) { return c.key; },
1929
1964
  move: function (k, d) { Stremio.moveRanking(k, d); return Promise.resolve(); }, toggle: function (k) { Stremio.toggleRanking(k); },
1930
1965
  hidden: function (k) { return !Stremio.getRanking().criteria.some(function (c) { return c.key === k && c.on; }); },
1931
- before: function (wrap) { // played before, above the resolution groups
1966
+ before: function (wrap) { // played before, above the resolution groups; then the two pages
1932
1967
  var played = Stremio.getRanking().played;
1933
1968
  var r = U.el('div', 'addon-item focusable cfg-row feed-extra');
1934
1969
  r.setAttribute('data-fk', 'played'); r.setAttribute('data-ctl', 'extra');
@@ -1937,48 +1972,9 @@
1937
1972
  : 'Sources you played rank like any other') + ' · OK to turn ' + (played ? 'off' : 'on')));
1938
1973
  r.__onselect = function () { Stremio.setPlayedFirst(!played); refeed('ranking', 'played', 'extra'); };
1939
1974
  wrap.appendChild(r);
1940
- // direct links only: the sources the TV can't play left out of the list
1941
- var hideUn = Stremio.getPrefs().hideUnplayable;
1942
- var un = U.el('div', 'addon-item focusable cfg-row feed-extra');
1943
- un.setAttribute('data-fk', 'unplayable'); un.setAttribute('data-ctl', 'extra');
1944
- un.appendChild(U.el('div', 'a-name', 'Sources the TV can’t play: ' + (hideUn ? 'Hidden' : 'Shown')));
1945
- un.appendChild(U.el('div', 'a-url', (hideUn ? 'Left out (torrents, links it can’t open): direct links only; where-to-watch apps stay'
1946
- : 'Listed, dimmed, at the bottom (torrents, links it can’t open)') + ' · OK to ' + (hideUn ? 'show them' : 'hide them')));
1947
- un.__onselect = function () { Stremio.setPref('hideUnplayable', !hideUn); refeed('ranking', 'unplayable', 'extra'); };
1948
- wrap.appendChild(un);
1949
- // skip the source list: Play plays the top source at once
1950
- var skipList = Stremio.getPrefs().skipSourceList;
1951
- var sl = U.el('div', 'addon-item focusable cfg-row feed-extra');
1952
- sl.setAttribute('data-fk', 'skiplist'); sl.setAttribute('data-ctl', 'extra');
1953
- sl.appendChild(U.el('div', 'a-name', 'Skip the source list: ' + (skipList ? 'On' : 'Off')));
1954
- sl.appendChild(U.el('div', 'a-url', (skipList ? '▶ Play, OK on an episode and ▶ Play SxEx play the top source at once (hold OK on an episode › Details for the list)'
1955
- : '▶ Play and OK on an episode show the sources to pick from') + ' · OK to turn ' + (skipList ? 'off' : 'on')));
1956
- sl.__onselect = function () { Stremio.setPref('skipSourceList', !skipList); refeed('ranking', 'skiplist', 'extra'); };
1957
- wrap.appendChild(sl);
1958
- // a source that doesn't start: the next one
1959
- var nextOn = Stremio.getPrefs().nextOnFail;
1960
- var nx = U.el('div', 'addon-item focusable cfg-row feed-extra');
1961
- nx.setAttribute('data-fk', 'nextonfail'); nx.setAttribute('data-ctl', 'extra');
1962
- nx.appendChild(U.el('div', 'a-name', 'The next source if one doesn’t start: ' + (nextOn ? 'On' : 'Off')));
1963
- nx.appendChild(U.el('div', 'a-url', (nextOn ? 'A source the TV refuses, or that hasn’t started after 45 s, gives way to the next best one (up to 3 more)'
1964
- : 'A source that doesn’t start takes you back to the list') + ' · OK to turn ' + (nextOn ? 'off' : 'on')));
1965
- nx.__onselect = function () { Stremio.setPref('nextOnFail', !nextOn); refeed('ranking', 'nextonfail', 'extra'); };
1966
- wrap.appendChild(nx);
1967
- // size limits: movies and series apart
1968
- [['movie', 'Movies’ sizes'], ['series', 'Series’ sizes (an episode)']].forEach(function (k) { wrap.appendChild(sizeRow(k[0], k[1])); });
1969
- // your words: sources with one of them at the bottom, for every title
1970
- var words = Stremio.getPrefs().hideWords;
1971
- var w = U.el('div', 'addon-item focusable cfg-row feed-extra');
1972
- w.setAttribute('data-fk', 'words'); w.setAttribute('data-ctl', 'extra');
1973
- w.appendChild(U.el('div', 'a-name', 'Hide sources with these words: ' + (words.length ? words.join(', ') : 'None')));
1974
- w.appendChild(U.el('div', 'a-url', 'A source with one of them in its name, description or file name (a whole word: “CAM” isn’t in “CAMERA”) goes to the bottom, for every title · OK to change'));
1975
- w.__onselect = function () {
1976
- App.openEntry({ title: 'Hide sources with these words', value: words.join(', '), placeholder: 'CAM, HDTS, TELESYNC',
1977
- hint: 'Words separated by commas, capitals or not. Sources with one go to the bottom and are never picked on their own. Leave it empty for none.',
1978
- validate: function () { return null; },
1979
- onConfirm: function (v) { App.back(); Stremio.setHideWords(v); refeed('ranking', 'words', 'extra'); } });
1980
- };
1981
- wrap.appendChild(w);
1975
+ wrap.appendChild(rankPageRow('Filters ›', filtersSummary(), 'rankfilters'));
1976
+ wrap.appendChild(rankPageRow('When you press Play ›', onPlaySummary(), 'rankplay'));
1977
+ if (_focusChild === 'rankfilters' || _focusChild === 'rankplay') { _feedFocus = { key: _focusChild, ctl: 'extra' }; _focusChild = null; } // (BACK from one: on its row)
1982
1978
  wrap.appendChild(U.el('div', 'formats-group', 'Then, in this order'));
1983
1979
  },
1984
1980
  after: function (wrap) {
@@ -1997,7 +1993,94 @@
1997
1993
  }, function () { wrap.appendChild(U.el('div', 'hint', 'Your addons couldn\'t be read just now.')); });
1998
1994
  } }
1999
1995
  };
2000
- // Size limits (Settings › Source ranking): smallest, largest and most per hour, for movies or
1996
+ // Source ranking's two pages (rankfilters, rankplay): a row on it that opens one, saying
1997
+ // what's set there.
1998
+ function rankPageRow(title, summary, section) {
1999
+ var r = U.el('div', 'addon-item focusable cfg-row feed-extra');
2000
+ r.setAttribute('data-fk', section); r.setAttribute('data-ctl', 'extra');
2001
+ r.appendChild(U.el('div', 'a-name', title));
2002
+ r.appendChild(U.el('div', 'a-url', summary));
2003
+ r.__onselect = function () { _settingsSection = section; renderSettings(); };
2004
+ return r;
2005
+ }
2006
+ function filtersSummary() {
2007
+ var P = Stremio.getPrefs(), bits = ['Sources the TV can’t play: ' + (P.hideUnplayable ? 'Hidden' : 'Shown')], sizes = [];
2008
+ [['movie', 'Movies'], ['series', 'Series']].forEach(function (k) { var L = P.sizeLimits[k[0]]; if (L.min || L.max || L.perHour) { sizes.push(k[1] + ' ' + sizeSummary(L)); } });
2009
+ bits.push(sizes.length ? sizes.join(', ') : 'No size limits');
2010
+ bits.push(P.hideWords.length ? 'Words: ' + P.hideWords.join(', ') : 'No words');
2011
+ return bits.join(' · ');
2012
+ }
2013
+ function onPlaySummary() {
2014
+ var P = Stremio.getPrefs();
2015
+ return 'Source list: ' + (P.skipSourceList ? 'skipped' : 'shown') + ' · The next source if one doesn’t start: ' + (P.nextOnFail ? 'On' : 'Off');
2016
+ }
2017
+ // A page of its own: its title, a hint, its rows; focus back on the one just changed.
2018
+ function rankPage(wrap, title, hint, rows) {
2019
+ wrap.appendChild(U.el('h2', null, 'Playback › Source ranking › ' + title));
2020
+ wrap.appendChild(U.el('div', 'hint feeds-hint', hint));
2021
+ rows.forEach(function (r) { wrap.appendChild(r); });
2022
+ var pref = null;
2023
+ if (_feedFocus) { pref = rows.filter(function (r) { return r.getAttribute('data-fk') === _feedFocus.key; })[0] || null; _feedFocus = null; }
2024
+ Focus.setScope(scr('settings'), pref || rows[0]);
2025
+ }
2026
+ // Filters: what's left out, or sent to the bottom.
2027
+ function settingsRankFilters(wrap) {
2028
+ rankPage(wrap, 'Filters', 'Sources these leave out, or send to the bottom of the list: never picked on their own.',
2029
+ [unplayableRow(), sizeRow('movie', 'Movies’ sizes'), sizeRow('series', 'Series’ sizes (an episode)'), wordsRow()]);
2030
+ }
2031
+ // When you press Play: whether the list shows, and a source that doesn't start.
2032
+ function settingsRankPlay(wrap) {
2033
+ rankPage(wrap, 'When you press Play', 'What ▶ Play does with the ranked list.', [skipListRow(), nextOnFailRow()]);
2034
+ }
2035
+ // direct links only: the sources the TV can't play left out of the list
2036
+ function unplayableRow() {
2037
+ var hideUn = Stremio.getPrefs().hideUnplayable;
2038
+ var un = U.el('div', 'addon-item focusable cfg-row feed-extra');
2039
+ un.setAttribute('data-fk', 'unplayable'); un.setAttribute('data-ctl', 'extra');
2040
+ un.appendChild(U.el('div', 'a-name', 'Sources the TV can’t play: ' + (hideUn ? 'Hidden' : 'Shown')));
2041
+ un.appendChild(U.el('div', 'a-url', (hideUn ? 'Left out (torrents, links it can’t open): direct links only; where-to-watch apps stay'
2042
+ : 'Listed, dimmed, at the bottom (torrents, links it can’t open)') + ' · OK to ' + (hideUn ? 'show them' : 'hide them')));
2043
+ un.__onselect = function () { Stremio.setPref('hideUnplayable', !hideUn); refeed('ranking', 'unplayable', 'extra'); };
2044
+ return un;
2045
+ }
2046
+ // skip the source list: Play plays the top source at once
2047
+ function skipListRow() {
2048
+ var skipList = Stremio.getPrefs().skipSourceList;
2049
+ var sl = U.el('div', 'addon-item focusable cfg-row feed-extra');
2050
+ sl.setAttribute('data-fk', 'skiplist'); sl.setAttribute('data-ctl', 'extra');
2051
+ sl.appendChild(U.el('div', 'a-name', 'Skip the source list: ' + (skipList ? 'On' : 'Off')));
2052
+ sl.appendChild(U.el('div', 'a-url', (skipList ? '▶ Play, OK on an episode and ▶ Play SxEx play the top source at once (hold OK on an episode › Details for the list)'
2053
+ : '▶ Play and OK on an episode show the sources to pick from') + ' · OK to turn ' + (skipList ? 'off' : 'on')));
2054
+ sl.__onselect = function () { Stremio.setPref('skipSourceList', !skipList); refeed('ranking', 'skiplist', 'extra'); };
2055
+ return sl;
2056
+ }
2057
+ // a source that doesn't start: the next one
2058
+ function nextOnFailRow() {
2059
+ var nextOn = Stremio.getPrefs().nextOnFail;
2060
+ var nx = U.el('div', 'addon-item focusable cfg-row feed-extra');
2061
+ nx.setAttribute('data-fk', 'nextonfail'); nx.setAttribute('data-ctl', 'extra');
2062
+ nx.appendChild(U.el('div', 'a-name', 'The next source if one doesn’t start: ' + (nextOn ? 'On' : 'Off')));
2063
+ nx.appendChild(U.el('div', 'a-url', (nextOn ? 'A source the TV refuses, or that hasn’t started after 45 s, gives way to the next best one (up to 3 more)'
2064
+ : 'A source that doesn’t start takes you back to the list') + ' · OK to turn ' + (nextOn ? 'off' : 'on')));
2065
+ nx.__onselect = function () { Stremio.setPref('nextOnFail', !nextOn); refeed('ranking', 'nextonfail', 'extra'); };
2066
+ return nx;
2067
+ }
2068
+ // your words: sources with one of them at the bottom, for every title
2069
+ function wordsRow() {
2070
+ var words = Stremio.getPrefs().hideWords;
2071
+ var w = U.el('div', 'addon-item focusable cfg-row feed-extra');
2072
+ w.setAttribute('data-fk', 'words'); w.setAttribute('data-ctl', 'extra');
2073
+ w.appendChild(U.el('div', 'a-name', 'Hide sources with these words: ' + (words.length ? words.join(', ') : 'None')));
2074
+ w.appendChild(U.el('div', 'a-url', 'A source with one of them in its name, description or file name (a whole word: “CAM” isn’t in “CAMERA”) goes to the bottom, for every title · OK to change'));
2075
+ w.__onselect = function () {
2076
+ App.openEntry({ title: 'Hide sources with these words', value: words.join(', '), placeholder: 'CAM, HDTS, TELESYNC',
2077
+ hint: 'Words separated by commas, capitals or not. Sources with one go to the bottom and are never picked on their own. Leave it empty for none.',
2078
+ validate: function () { return null; },
2079
+ onConfirm: function (v) { App.back(); Stremio.setHideWords(v); refeed('ranking', 'words', 'extra'); } });
2080
+ };
2081
+ return w;
2082
+ }
2083
+ // Size limits (Settings › Source ranking › Filters): smallest, largest and most per hour, for movies or
2001
2084
  // series; OK on the row: which one, then its value.
2002
2085
  var SIZE_CHOICES = {
2003
2086
  min: { label: 'Smallest', none: 'None', values: [0.1, 0.3, 0.5, 1, 2] },
package/js/views.js CHANGED
@@ -7,7 +7,7 @@ var Views = (function (V) {
7
7
  return {
8
8
  renderHome: V.renderHome, renderDetail: V.renderDetail, applyAppearance: V.applyAppearance,
9
9
  renderPerson: V.renderPerson, detailSnapshot: V.detailSnapshot, restoreDetail: V.restoreDetail, lightSnapshot: V.lightSnapshot, renderSettings: V.renderSettings,
10
- settingsRoot: V.settingsRoot, backInSettings: V.backInSettings,
10
+ settingsRoot: V.settingsRoot, backInSettings: V.backInSettings, guideBackOn: V.guideBackOn,
11
11
  // a screen left: its downloads still running (a title page's details, Discover's
12
12
  // pages, a search's rows) stopped
13
13
  leaveScreen: function (name) { var f = { detail: V.leaveDetail, discover: V.leaveDiscover, search: V.leaveSearch }[name]; if (f) { f(); } }, backInLocal: V.backInLocal, setHomeTab: V.setHomeTab,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stremiok",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "StremioK streaming client for Tizen 3.0+ Samsung TVs (TizenBrew app module).",
5
5
  "packageType": "app",
6
6
  "appName": "StremioK",