stremiok 2.2.0 → 2.2.2

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/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) {
@@ -129,14 +130,14 @@
129
130
  var SIZE_PX = { small: 28, medium: 36, large: 46, xlarge: 58 };
130
131
  function styleSpan(span) {
131
132
  var cfg = getSubCfg();
132
- span.style.fontSize = (SIZE_PX[cfg.size] || 36) + 'px';
133
+ span.style.fontSize = (SIZE_PX[cfg.size] || 36) / U.zoom() + 'px'; // (their own size, whatever the zoom)
133
134
  span.style.fontFamily = subFontFamily(cfg.font);
134
135
  span.style.color = pick3(SUB_COLORS, cfg.color)[2];
135
136
  span.style.background = pick3(SUB_BGS, cfg.bg)[2];
136
137
  span.style.textShadow = pick3(SUB_EDGES, cfg.edge)[2];
137
138
  if (span.parentNode) { span.parentNode.style.bottom = Math.max(subsPosPx(), (P.S && P.S.subsLift) || 0) + 'px'; } // (raised over the progress bar: core's liftSubs)
138
139
  }
139
- function subsPosPx() { return pick3(SUB_POSITIONS, getSubCfg().pos)[2]; }
140
+ function subsPosPx() { return pick3(SUB_POSITIONS, getSubCfg().pos)[2] / U.zoom(); }
140
141
 
141
142
  // ---------- progress (Continue Watching) ----------
142
143
  function progressAll() { return U.store.get(PROGRESS_KEY, {}); }
package/js/player-subs.js CHANGED
@@ -48,7 +48,7 @@
48
48
 
49
49
  // A new playback: no subtitles yet.
50
50
  function resetSubs() {
51
- _subGen++;
51
+ _subGen++; stopSubLoad();
52
52
  _subTracks = []; _cues = null; _subOn = false; _subLabel = ''; _subTrackUrl = '';
53
53
  _subDelay = 0; _subTrackId = null; _embSub = null; _embLang = ''; _embWant = null;
54
54
  _embDelay = 0; _embQueue = []; _embNudge = null;
@@ -285,12 +285,52 @@
285
285
  // Each choice (a track, Off, a built-in one) is a new one: a download still on its way
286
286
  // for an earlier choice is dropped when it comes, without a word (_subGen).
287
287
  var _subGen = 0;
288
+ // A subtitle file's time to arrive: some servers build it when asked (12 s, seen from an
289
+ // OpenSubtitles proxy); the video plays on meanwhile.
290
+ var SUB_FILE_MS = 20000;
291
+ // The download under way (_subLoad: { url, ctx, cancel, timer, done }): the same track picked
292
+ // again while it comes waits for it (it used to be asked for again, what was on its way
293
+ // dropped: slow servers took several picks); another choice stops it, its connection freed.
294
+ // The files downloaded (the last SUB_FILES_MAX, while StremioK runs) come back at once.
295
+ var _subLoad = null, SUB_FILES_MAX = 8, _subFiles = []; // ([{ url, text }], newest last)
296
+ function subFile(url) { for (var i = 0; i < _subFiles.length; i++) { if (_subFiles[i].url === url) { return _subFiles[i].text; } } return null; }
297
+ function keepSubFile(url, text) {
298
+ _subFiles = _subFiles.filter(function (f) { return f.url !== url; });
299
+ _subFiles.push({ url: url, text: text });
300
+ if (_subFiles.length > SUB_FILES_MAX) { _subFiles.shift(); }
301
+ }
302
+ function stopSubLoad() {
303
+ var l = _subLoad;
304
+ _subLoad = null;
305
+ if (l) { clearInterval(l.timer); l.cancel.cancel(); }
306
+ }
307
+ // A subtitle file: from those kept, else downloaded (the same one shared by whoever asks).
308
+ function subText(track, cancel) {
309
+ var kept = subFile(track.url);
310
+ if (kept !== null) { return Promise.resolve(kept); }
311
+ return U.getText(track.url, SUB_FILE_MS, undefined, cancel || null, false, track.headers || null).then(function (text) { keepSubFile(track.url, text); return text; }); // (a USB file's: the service's header where there's no token)
312
+ }
288
313
  function loadTrack(track, onDone) {
289
314
  if (!U.parseUrl(track.url)) { U.toast('That subtitle track has no usable link'); return; } // (http(s) only)
290
- U.toast('Loading subtitles…', 1500);
315
+ var l = _subLoad;
316
+ if (l && l.url === track.url && l.ctx === S.ctx) { // (still coming: waited for, not asked for again)
317
+ if (onDone) { l.done.push(onDone); }
318
+ U.toast('Still loading subtitles… ' + l.secs + ' s', 1500);
319
+ return;
320
+ }
321
+ stopSubLoad();
291
322
  var forCtx = S.ctx, gen = ++_subGen;
292
323
  function stale() { return !S.active || S.ctx !== forCtx || gen !== _subGen; } // (another playback, or another choice, meanwhile)
293
- U.getText(track.url, 15000, undefined, null, false, track.headers || null).then(function (text) { // (a USB file's: the service's header where there's no token)
324
+ var cancel = U.cancelToken(), mine = { url: track.url, ctx: forCtx, cancel: cancel, secs: 0, done: onDone ? [onDone] : [], timer: null };
325
+ if (subFile(track.url) === null) { // (a download: how long it's been, every second, till it's in)
326
+ _subLoad = mine;
327
+ U.toast('Loading subtitles…', 1500);
328
+ mine.timer = setInterval(function () { mine.secs++; if (_subLoad === mine) { U.toast('Loading subtitles… ' + mine.secs + ' s', 1500); } }, 1000);
329
+ }
330
+ function finished() { clearInterval(mine.timer); if (_subLoad === mine) { _subLoad = null; } }
331
+ onDone = function () { mine.done.forEach(function (f) { try { f(); } catch (e) {} }); };
332
+ subText(track, cancel).then(function (text) {
333
+ finished();
294
334
  if (stale()) { return; }
295
335
  var cues = parseCues(text);
296
336
  if (!cues.length) { U.toast('Could not parse subtitles'); return; }
@@ -299,8 +339,8 @@
299
339
  _subTrackId = (track.id != null) ? track.id : null; _subDelay = 0;
300
340
  renderSubs();
301
341
  U.toast('Subtitles: ' + (track.lang || 'on'));
302
- if (onDone) { onDone(); }
303
- }, function () { if (!stale()) { U.toast('Failed to download subtitles'); } });
342
+ onDone();
343
+ }, function () { finished(); if (!stale()) { U.toast('Failed to download subtitles'); } });
304
344
  }
305
345
 
306
346
  // ---------- the second subtitle ----------
@@ -323,7 +363,7 @@
323
363
  var track = _subTracks.filter(function (t) { return t.url && langMatches(want, t.lang); })[0];
324
364
  if (!track || (_cues2 && _sub2Label === track.url)) { if (!track) { secondOff(); } return; }
325
365
  var forCtx = S.ctx, gen = ++_sub2Gen;
326
- U.getText(track.url, 15000, undefined, null, false, track.headers || null).then(function (text) {
366
+ subText(track).then(function (text) {
327
367
  if (!S.active || S.ctx !== forCtx || gen !== _sub2Gen) { return; }
328
368
  var cues = parseCues(text);
329
369
  if (!cues.length) { return; }
@@ -374,7 +414,7 @@
374
414
  }
375
415
 
376
416
  function subsOff() {
377
- _subGen++;
417
+ _subGen++; stopSubLoad(); // (a download under way for a track: stopped)
378
418
  _subOn = false; _cues = null; _subLabel = ''; _subTrackUrl = ''; _subTrackId = null; _subDelay = 0;
379
419
  embOff();
380
420
  S.subsEl.className = 'hidden'; U.clear(S.subsEl);
@@ -583,19 +623,32 @@
583
623
  // Level 1: one entry per language, spellings merged ("en", "eng" and "English" are one
584
624
  // group), named in full, with the file's own tracks in the same groups as the addons'.
585
625
  // Order: the language showing now, the preferred language, then by name.
586
- function langMenu() {
626
+ // Only my languages (Settings, Off by default): just your default, second-choice and
627
+ // second-subtitle languages, the one showing, and tracks that don't say their language;
628
+ // Other languages, after them, lists the rest (all: that list).
629
+ function langMenu(all) {
587
630
  var groups = Object.create(null), order = []; // (no inherited names: a language an addon calls "__proto__" is just a key)
631
+ function key(l) { var k = langKey(l); return P.langUnstated(k) ? '' : k; } // (no language, "und"…: one Unknown language)
588
632
  function add(k, item) {
589
633
  if (!groups[k]) { groups[k] = []; order.push(k); }
590
634
  groups[k].push(item);
591
635
  }
592
- getTextTracks().forEach(function (tr, i) { add(langKey(textLang(tr)), { emb: tr, n: i }); });
636
+ getTextTracks().forEach(function (tr, i) { add(key(textLang(tr)), { emb: tr, n: i }); });
593
637
  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);
638
+ (offer || []).forEach(function (c, i) { add(key(c.lang || ''), { cc: c, n: i, emb: true }); });
639
+ _subTracks.forEach(function (tr) { add(key(tr.lang), { track: tr }); });
640
+ var curKey = _subOn ? key(_subLabel) : (_embSub != null ? key(_embLang) : null);
597
641
  var pref = getSubCfg().lang;
598
642
  var prefs = [curKey, pref].filter(function (x) { return x && x !== 'off'; });
643
+ var cfg = getSubCfg(), hidden = 0;
644
+ var mine = [cfg.lang, cfg.lang2, cfg.second].filter(function (l) { return l && l !== 'off' && l !== 'none'; });
645
+ if (cfg.only === 'on' && !all && mine.length) {
646
+ order = order.filter(function (k) {
647
+ var keep = k === curKey || k === '' || mine.some(function (l) { return langKey(l) === k || langMatches(l, k); });
648
+ if (!keep) { hidden++; }
649
+ return keep;
650
+ });
651
+ }
599
652
  order.sort(function (a, b) {
600
653
  var ra = langRank(a, prefs), rb = langRank(b, prefs);
601
654
  if (ra !== rb) { return ra - rb; }
@@ -610,11 +663,12 @@
610
663
  sel: (k === curKey),
611
664
  run: function () { pushMenu(langTracksMenu(k, list)); } });
612
665
  });
666
+ if (hidden) { items.push({ label: 'Other languages', detail: hidden + ' more', run: function () { pushMenu(langMenu(true)); } }); }
613
667
  var sec = getSubCfg().second;
614
668
  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
669
  var idx = 0;
616
670
  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 };
671
+ return { title: all ? 'All subtitle languages' : 'Subtitle language', items: items, idx: idx, rebuild: function () { return langMenu(all); } };
618
672
  }
619
673
 
620
674
  function inFile() { return S.ctx && (S.ctx._relayCc || S.ctx._ccOffer) ? 'closed captions' : 'in the file'; }