stremiok 2.1.0 → 2.2.0
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 +82 -0
- package/README.md +9 -9
- package/config.xml +1 -1
- package/css/style.css +27 -0
- package/index.html +34 -31
- package/js/app.js +19 -7
- package/js/epg.js +320 -0
- package/js/iptv.js +198 -25
- package/js/keys.js +2 -0
- package/js/player-controls.js +50 -8
- package/js/player-core.js +112 -12
- package/js/player-store.js +21 -1
- package/js/player-subs.js +38 -8
- package/js/stremio.js +70 -6
- package/js/trakt.js +1 -0
- package/js/util.js +24 -13
- package/js/view-core.js +128 -55
- package/js/view-guide.js +332 -0
- package/js/view-home.js +148 -33
- package/js/view-settings.js +103 -35
- package/js/views.js +3 -2
- package/js/ytplay.js +53 -5
- package/package.json +2 -1
- package/service/index.js +863 -46
package/js/ytplay.js
CHANGED
|
@@ -62,12 +62,16 @@ var YTPlay = (function () {
|
|
|
62
62
|
}
|
|
63
63
|
// A live HLS channel whose sound the TV's player stutters on (its server cuts the AAC sound
|
|
64
64
|
// mid-frame): the service looks at it and, if so, gives a relay that repacks the sound:
|
|
65
|
-
// { fix, sure, why, url
|
|
66
|
-
// h: the channel's own User-Agent /
|
|
67
|
-
|
|
65
|
+
// { fix, sure, why, url, cc: [{ ch, lang }] (its closed captions), ccSure }. force: the relay
|
|
66
|
+
// at once (the channel's already known to need it). h: the channel's own User-Agent /
|
|
67
|
+
// Referer ({ ua, ref }). opts (with force): { fix: false (the sound as it is), cc: the caption
|
|
68
|
+
// channels to add as subtitle tracks }.
|
|
69
|
+
function liveOpen(url, h, force, opts) {
|
|
68
70
|
if (MODE === 'browser') { return Promise.reject(new Error('no service')); }
|
|
71
|
+
var cc = opts && opts.cc ? opts.cc.map(function (c) { return c.ch + (c.lang ? '-' + c.lang : ''); }).join(',') : '';
|
|
69
72
|
return ensureService().then(entryAuth).then(function (a) {
|
|
70
|
-
var q = '/live/open?u=' + encodeURIComponent(url) + (h && h.ua ? '&ua=' + encodeURIComponent(h.ua) : '') + (h && h.ref ? '&ref=' + encodeURIComponent(h.ref) : '') + (force ? '&force=1' : '')
|
|
73
|
+
var q = '/live/open?u=' + encodeURIComponent(url) + (h && h.ua ? '&ua=' + encodeURIComponent(h.ua) : '') + (h && h.ref ? '&ref=' + encodeURIComponent(h.ref) : '') + (force ? '&force=1' : '') +
|
|
74
|
+
(force && opts && opts.fix === false ? '&fix=0' : '') + (force && cc ? '&cc=' + encodeURIComponent(cc) : '');
|
|
71
75
|
return U.getJSON(PROXY + q + a.q, force ? 5000 : 30000, 65536, null, a.h);
|
|
72
76
|
});
|
|
73
77
|
}
|
|
@@ -91,6 +95,50 @@ var YTPlay = (function () {
|
|
|
91
95
|
return U.getJSON(PROXY + '/aiom/addons?base=' + encodeURIComponent(base) + a.q, 40000, 1048576, null, h);
|
|
92
96
|
});
|
|
93
97
|
}
|
|
98
|
+
// A playlist's TV guide, read and kept by the service (see service/index.js, epgControl):
|
|
99
|
+
// epgLoad asks it to read it for these channels (k: their keys as a bitset, EPG.bitsFor) ->
|
|
100
|
+
// { state, at, channels, programmes, partial, error }; epgGet: their programs -> { <key>:
|
|
101
|
+
// [[start, stop, title, desc]] }. Both POSTs, what's asked in the body: a newer TV's service
|
|
102
|
+
// reads 8 KB of a request's address and headers, less than thousands of channels' keys.
|
|
103
|
+
// (a POST refused, a service from before it took them or one without the token: asked
|
|
104
|
+
// again as a GET, when it fits in the address)
|
|
105
|
+
// An app that can't read the service's token (its header instead): the guide ticket, asked
|
|
106
|
+
// for once, given with the POSTs (the TV adds an Origin to them, which the header alone doesn't pass).
|
|
107
|
+
var _ticket = null;
|
|
108
|
+
function epgTicket(a) {
|
|
109
|
+
if (a.q || MODE === 'tizenbrew') { return Promise.resolve(a.q); } // (the token; TizenBrew's origin passes as it is)
|
|
110
|
+
if (!_ticket) {
|
|
111
|
+
_ticket = U.getJSON(PROXY + '/epg/ticket', 8000, 4096, null, a.h).then(function (r) {
|
|
112
|
+
return r && /^[0-9a-f]{48}$/.test(r.ticket || '') ? '&ticket=' + r.ticket : '';
|
|
113
|
+
}, function () { _ticket = null; return ''; });
|
|
114
|
+
}
|
|
115
|
+
return _ticket;
|
|
116
|
+
}
|
|
117
|
+
function epgAsk(path, body, maxBytes) {
|
|
118
|
+
if (MODE === 'browser') { return Promise.reject(new Error('no service')); }
|
|
119
|
+
return ensureService().then(entryAuth).then(function (a) {
|
|
120
|
+
return epgTicket(a).then(function (cred) { return { a: a, cred: cred }; });
|
|
121
|
+
}).then(function (x) {
|
|
122
|
+
var a = x.a, q = x.cred ? '?' + x.cred.replace(/^&/, '') : '';
|
|
123
|
+
return U.postJSON(PROXY + path + q, body, 15000, maxBytes, a.h).then(null, function (err) {
|
|
124
|
+
if (err && /^HTTP 403\b/.test(err.message || '') && /ticket=/.test(q)) { _ticket = null; } // (the service started again since: a new ticket next time)
|
|
125
|
+
if (!(err && /^HTTP (403|405)\b/.test(err.message || '') && body.length < 6000)) { throw err; }
|
|
126
|
+
return U.getJSON(PROXY + path + (q ? q + '&' : '?') + body, 15000, maxBytes, null, a.h);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// Settings › Storage: the saved guides ({ count, bytes }), and deleting them all.
|
|
131
|
+
function epgFiles() {
|
|
132
|
+
if (MODE === 'browser') { return Promise.resolve(null); }
|
|
133
|
+
return ensureService().then(entryAuth).then(function (a) { return U.getJSON(PROXY + '/epg/files' + (a.q ? '?' + a.q.replace(/^&/, '') : ''), 8000, 4096, null, a.h); });
|
|
134
|
+
}
|
|
135
|
+
function epgClear() { return epgAsk('/epg/clear', '', 4096); }
|
|
136
|
+
function epgLoad(u, k, force) {
|
|
137
|
+
return epgAsk('/epg/load', 'u=' + encodeURIComponent(u) + '&k=' + k + (force ? '&force=1' : ''), 65536);
|
|
138
|
+
}
|
|
139
|
+
function epgGet(u, keys, from, to, max) {
|
|
140
|
+
return epgAsk('/epg/get', 'u=' + encodeURIComponent(u) + '&ids=' + encodeURIComponent(keys.join('|')) + '&from=' + from + '&to=' + to + (max ? '&max=' + max : ''), 4194304);
|
|
141
|
+
}
|
|
94
142
|
// Whether YouTube gives the video out to the service (it refuses some uploads to the player
|
|
95
143
|
// the service asks as): false when it says no; true when it does, or no answer came (the
|
|
96
144
|
// player tries). The service keeps what it found, so playing it next asks YouTube nothing new.
|
|
@@ -181,6 +229,6 @@ var YTPlay = (function () {
|
|
|
181
229
|
return MODE === 'tizenbrew' ? 'TizenBrew module' : MODE === 'wgt' ? 'Sideloaded app (.wgt)' : 'Browser (development)';
|
|
182
230
|
}
|
|
183
231
|
|
|
184
|
-
return { ensureService: ensureService, resolverUrl: resolverUrl, infoUrl: infoUrl, canPlay: canPlay, liveOpen: liveOpen, hlsMaster: hlsMaster, aiomAddons: aiomAddons, entryOpen: entryOpen, entryPoll: entryPoll, entryClose: entryClose, subsUrl: subsUrl, audioLang: audioLang, setAudioLang: setAudioLang, installMode: function () { return MODE; },
|
|
232
|
+
return { ensureService: ensureService, resolverUrl: resolverUrl, infoUrl: infoUrl, canPlay: canPlay, liveOpen: liveOpen, hlsMaster: hlsMaster, aiomAddons: aiomAddons, epgLoad: epgLoad, epgGet: epgGet, epgFiles: epgFiles, epgClear: epgClear, entryOpen: entryOpen, entryPoll: entryPoll, entryClose: entryClose, subsUrl: subsUrl, audioLang: audioLang, setAudioLang: setAudioLang, installMode: function () { return MODE; },
|
|
185
233
|
installLabel: installLabel, unavailableMessage: unavailableMessage, isRunning: isRunning, port: PORT };
|
|
186
234
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stremiok",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "StremioK streaming client for Tizen 3.0+ Samsung TVs (TizenBrew app module).",
|
|
5
5
|
"packageType": "app",
|
|
6
6
|
"appName": "StremioK",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"icon.png",
|
|
15
15
|
"config.xml",
|
|
16
16
|
"README.md",
|
|
17
|
+
"CHANGELOG.md",
|
|
17
18
|
"LICENSE"
|
|
18
19
|
],
|
|
19
20
|
"keys": [
|