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 +15 -0
- package/README.md +1 -0
- package/config.xml +1 -1
- package/index.html +33 -33
- package/js/app.js +6 -3
- package/js/player-lang.js +66 -5
- package/js/player-store.js +2 -1
- package/js/player-subs.js +20 -6
- package/js/view-detail.js +1 -1
- package/js/view-guide.js +41 -2
- package/js/view-settings.js +139 -56
- package/js/views.js +1 -1
- package/package.json +1 -1
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.
|
|
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.
|
|
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.
|
|
57
|
-
<script src="js/util.js?v=2.2.
|
|
58
|
-
<script src="js/iptv.js?v=2.2.
|
|
59
|
-
<script src="js/epg.js?v=2.2.
|
|
60
|
-
<script src="js/youtube.js?v=2.2.
|
|
61
|
-
<script src="js/stremio.js?v=2.2.
|
|
62
|
-
<script src="js/trakt.js?v=2.2.
|
|
63
|
-
<script src="js/keys.js?v=2.2.
|
|
64
|
-
<script src="js/focus.js?v=2.2.
|
|
65
|
-
<script src="js/tvapps.js?v=2.2.
|
|
66
|
-
<script src="js/player-core.js?v=2.2.
|
|
67
|
-
<script src="js/player-store.js?v=2.2.
|
|
68
|
-
<script src="js/player-lang.js?v=2.2.
|
|
69
|
-
<script src="js/player-quality.js?v=2.2.
|
|
70
|
-
<script src="js/player-subs.js?v=2.2.
|
|
71
|
-
<script src="js/player-audio.js?v=2.2.
|
|
72
|
-
<script src="js/player-controls.js?v=2.2.
|
|
73
|
-
<script src="js/player.js?v=2.2.
|
|
74
|
-
<script src="js/ytplay.js?v=2.2.
|
|
75
|
-
<script src="js/local.js?v=2.2.
|
|
76
|
-
<script src="js/downloads.js?v=2.2.
|
|
77
|
-
<script src="js/stremio-account.js?v=2.2.
|
|
78
|
-
<script src="js/view-core.js?v=2.2.
|
|
79
|
-
<script src="js/view-home.js?v=2.2.
|
|
80
|
-
<script src="js/view-guide.js?v=2.2.
|
|
81
|
-
<script src="js/view-detail.js?v=2.2.
|
|
82
|
-
<script src="js/view-search.js?v=2.2.
|
|
83
|
-
<script src="js/view-settings.js?v=2.2.
|
|
84
|
-
<script src="js/view-discover.js?v=2.2.
|
|
85
|
-
<script src="js/views.js?v=2.2.
|
|
86
|
-
<script src="js/app.js?v=2.2.
|
|
87
|
-
<script src="js/hubpreview.js?v=2.2.
|
|
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
|
-
|
|
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);
|
package/js/player-store.js
CHANGED
|
@@ -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'
|
|
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
|
-
|
|
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(
|
|
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(
|
|
595
|
-
_subTracks.forEach(function (tr) { add(
|
|
596
|
-
var curKey = _subOn ?
|
|
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) {
|
|
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
|
-
|
|
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);
|
package/js/view-settings.js
CHANGED
|
@@ -98,11 +98,35 @@
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// ================= SUBTITLE CONFIG =================
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
['
|
|
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('
|
|
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
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
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
|
-
//
|
|
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,
|