Browse Source

Darba versija

Ivars 7 years ago
parent
commit
22e552ab48

+ 10
- 4
ContentSources.py View File

30
             fname = os.path.split(fname)[1]
30
             fname = os.path.split(fname)[1]
31
             if fname == "__init__": continue
31
             if fname == "__init__": continue
32
             if ext == '.py':
32
             if ext == '.py':
33
+                print "Importing %s"%fname
33
                 mod = __import__(fname)
34
                 mod = __import__(fname)
34
                 reload(mod)
35
                 reload(mod)
35
                 if "Source" in dir(mod):
36
                 if "Source" in dir(mod):
180
             print s #.encode(sys.stdout.encoding,"replace")
181
             print s #.encode(sys.stdout.encoding,"replace")
181
 
182
 
182
         while True:
183
         while True:
183
-            a = raw_input("Enter numeber, q for exit: ")
184
+            a = raw_input("Enter number, (-) for download, q for exit: ")
184
             if a in ("q","Q","x","X"):
185
             if a in ("q","Q","x","X"):
185
                 exit_loop = True
186
                 exit_loop = True
186
                 print "Exiting"
187
                 print "Exiting"
191
             except:
192
             except:
192
                 print "Not number!"
193
                 print "Not number!"
193
         if exit_loop: break
194
         if exit_loop: break
195
+        download = False
196
+        if n<0:
197
+            n = abs(n)
198
+            download = True
194
         cur2 = content[n]
199
         cur2 = content[n]
195
-
196
         data0 = cur2[1].split("::")[1] if "::" in cur2[1] else cur2[1]
200
         data0 = cur2[1].split("::")[1] if "::" in cur2[1] else cur2[1]
197
         if not data0:
201
         if not data0:
198
             pass
202
             pass
199
-
200
         elif cur2[1] == "back":
203
         elif cur2[1] == "back":
201
             cur = history.pop()
204
             cur = history.pop()
202
         elif sources.is_video(cur2[1]):
205
         elif sources.is_video(cur2[1]):
213
                     traceback.print_exc()
216
                     traceback.print_exc()
214
                     streams = []
217
                     streams = []
215
             if streams:
218
             if streams:
216
-                util.play_video(streams)
219
+                if not download:
220
+                    util.play_video(streams)
221
+                else:
222
+                    Downloader.download_video(streams)
217
             else:
223
             else:
218
                 print "**No stream to play - %s "%(
224
                 print "**No stream to play - %s "%(
219
                     cur2[1])
225
                     cur2[1])

+ 288
- 63
Downloader.py View File

1
-from boxbranding import getMachineBrand, getMachineName
2
-
1
+#from boxbranding import getMachineBrand, getMachineName
2
+import sys,os, os.path, re
3
+import urlparse, requests
3
 from twisted.web import client
4
 from twisted.web import client
4
 from twisted.internet import reactor, defer, ssl
5
 from twisted.internet import reactor, defer, ssl
5
 
6
 
7
+USER_AGENT = "Enigma2 HbbTV/1.1.1 (+PVR+RTSP+DL;OpenATV;;;)"
6
 
8
 
9
+#####################################################################################################
7
 class HTTPProgressDownloader(client.HTTPDownloader):
10
 class HTTPProgressDownloader(client.HTTPDownloader):
8
-	def __init__(self, url, outfile, headers=None):
9
-		client.HTTPDownloader.__init__(self, url, outfile, headers=headers, agent="Enigma2 HbbTV/1.1.1 (+PVR+RTSP+DL;OpenATV;;;)")
10
-		self.status = None
11
-		self.progress_callback = None
12
-		self.deferred = defer.Deferred()
13
-
14
-	def noPage(self, reason):
15
-		if self.status == "304":
16
-			print reason.getErrorMessage()
17
-			client.HTTPDownloader.page(self, "")
18
-		else:
19
-			client.HTTPDownloader.noPage(self, reason)
20
-
21
-	def gotHeaders(self, headers):
22
-		if self.status == "200":
23
-			if headers.has_key("content-length"):
24
-				self.totalbytes = int(headers["content-length"][0])
25
-			else:
26
-				self.totalbytes = 0
27
-			self.currentbytes = 0.0
28
-		return client.HTTPDownloader.gotHeaders(self, headers)
29
-
30
-	def pagePart(self, packet):
31
-		if self.status == "200":
32
-			self.currentbytes += len(packet)
33
-		if self.totalbytes and self.progress_callback:
34
-			self.progress_callback(self.currentbytes, self.totalbytes)
35
-		return client.HTTPDownloader.pagePart(self, packet)
36
-
37
-	def pageEnd(self):
38
-		return client.HTTPDownloader.pageEnd(self)
39
-
40
-class downloadWithProgress:
41
-	def __init__(self, url, outputfile, contextFactory=None, *args, **kwargs):
42
-		if hasattr(client, '_parse'):
43
-			scheme, host, port, path = client._parse(url)
44
-		else:
45
-			from twisted.web.client import _URI
46
-			uri = _URI.fromBytes(url)
47
-			scheme = uri.scheme
48
-			host = uri.host
49
-			port = uri.port
50
-			path = uri.path
51
-
52
-		self.factory = HTTPProgressDownloader(url, outputfile, *args, **kwargs)
53
-		if scheme == "https":
54
-			self.connection = reactor.connectSSL(host, port, self.factory, ssl.ClientContextFactory())
55
-		else:
56
-			self.connection = reactor.connectTCP(host, port, self.factory)
57
-
58
-	def start(self):
59
-		return self.factory.deferred
60
-
61
-	def stop(self):
62
-		if self.connection:
63
-			print "[stop]"
64
-			self.connection.disconnect()
65
-
66
-	def addProgress(self, progress_callback):
67
-		print "[addProgress]"
68
-		self.factory.progress_callback = progress_callback
11
+    def __init__(self, url, outfile, headers=None):
12
+        agent = USER_AGENT
13
+        if headers and "user-agent" in headers:
14
+            agent = headers["user-agent"]
15
+        if headers and "User-Agent" in headers:
16
+            agent = headers["User-Agent"]
17
+        client.HTTPDownloader.__init__(self, url, outfile, headers=headers, agent=agent)
18
+        self.status = None
19
+        self.progress_callback = None
20
+        self.deferred = defer.Deferred()
21
+
22
+    def noPage(self, reason):
23
+        if self.status == "304":
24
+            print reason.getErrorMessage()
25
+            client.HTTPDownloader.page(self, "")
26
+        else:
27
+            client.HTTPDownloader.noPage(self, reason)
28
+
29
+    def gotHeaders(self, headers):
30
+        if self.status == "200":
31
+            if headers.has_key("content-length"):
32
+                self.totalbytes = int(headers["content-length"][0])
33
+            else:
34
+                self.totalbytes = 0
35
+            self.currentbytes = 0.0
36
+        return client.HTTPDownloader.gotHeaders(self, headers)
37
+
38
+    def pagePart(self, packet):
39
+        if self.status == "200":
40
+            self.currentbytes += len(packet)
41
+        if self.totalbytes and self.progress_callback:
42
+            self.progress_callback(self.currentbytes, self.totalbytes)
43
+        return client.HTTPDownloader.pagePart(self, packet)
44
+
45
+    def pageEnd(self):
46
+        return client.HTTPDownloader.pageEnd(self)
47
+
48
+class DownloadWithProgress:
49
+    def __init__(self, url, outputfile, headers=None, limit=0, contextFactory=None, *args, **kwargs):
50
+        self.limit = limit
51
+        uri = urlparse.urlparse(url)
52
+        scheme = uri.scheme
53
+        host = uri.hostname
54
+        port = uri.port if uri.port else 80
55
+        path = uri.path
56
+        if not headers:
57
+            headers = {"user-agent":USER_AGENT}
58
+        self.factory = HTTPProgressDownloader(url, outputfile, headers, *args, **kwargs)
59
+        if scheme == "https":
60
+            self.connection = reactor.connectSSL(host, port, self.factory, ssl.ClientContextFactory())
61
+        else:
62
+            self.connection = reactor.connectTCP(host, port, self.factory)
63
+
64
+    def start(self):
65
+        return self.factory.deferred
66
+
67
+    def stop(self):
68
+        if self.connection:
69
+            print "[stop]"
70
+            self.connection.disconnect()
71
+
72
+    def addProgress(self, progress_callback):
73
+        print "[addProgress]"
74
+        self.factory.progress_callback = progress_callback
75
+
76
+#####################################################################################################
77
+class DownloadWithProgressFragmented:
78
+    def __init__(self, url, outputfile, headers = None, limit = 0, contextFactory=None, *args, **kwargs):
79
+        self.url = url
80
+        self.outputfile = outputfile
81
+        self.base_url = "/".join(url.split("/")[:-1])+"/"
82
+        self.headers = headers if headers else  {"user-agent":"Enigma2"}
83
+        self.limit = limit
84
+        self.agent = kwargs["agent"] if "agent" in kwargs else None
85
+        self.cookie = kwargs["cookie"] if "cookie" in kwargs else None
86
+        self.deferred = defer.Deferred()
87
+        #self.deferred.addCallback(self.start_download)
88
+
89
+    def start_download(self):
90
+        print "Start download"
91
+        try:
92
+            r = requests.get(self.url,headers=self.headers)
93
+        except Exception as e:
94
+            #self.deferred.errback("Cannot open manifsest file - %s"%url)
95
+            self.deferred.errback(e)
96
+        if not r.content.startswith("#EXTM3U"):
97
+            self.deferred.errback(Exception("Not valid manifest file - %s"%self.url))
98
+        streams = re.findall(r"#EXT-X-STREAM-INF:.*?BANDWIDTH=(\d+).*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
99
+        if streams:
100
+            sorted(streams, key=lambda item: int(item[0]), reverse=True)
101
+            url = streams[0][1]
102
+            if not url.startswith("http"):
103
+                url = self.base_url + url
104
+            try:
105
+                r = requests.get(url, headers=self.headers)
106
+            except Exception as e:
107
+                self.deferred.errback(Exception("Cannot open manifsest file - %s"%url))
108
+        self.ts_list = re.findall(r"#EXTINF:([\d\.]+),.*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
109
+        if not len(self.ts_list):
110
+            self.deferred.errback(Exception("Cannot read fragment list in  manifsest file - %s"%url))
111
+        self.ts_num = 0
112
+        self.type = "vod" if "#EXT-X-ENDLIST" in r.content else "live"
113
+        self.currentbytes = 0.0
114
+        self.totalbytes = -1
115
+        self.currenttime = 0.0
116
+        self.totaltime = sum(map(float,zip(*self.ts_list)[0]))
117
+        self.ts_file = open(self.outputfile, "wb")
118
+        self.download_fragment()
119
+
120
+    def download_fragment(self):
121
+        if self.ts_num>=len(self.ts_list):
122
+            pass
123
+            print "Call later"
124
+            reactor.callLater(10,self.update_manifest)
125
+            reactor.callLater(10, self.download_fragment)
126
+        else:
127
+            print "Start fragment download"
128
+            url = self.ts_list[self.ts_num][1]
129
+            if not "://" in url:
130
+                url = self.base_url+url
131
+            self.d = client.getPage(url,headers = self.headers)
132
+            self.d.addCallbacks(self.download_ok,self.download_err)
133
+
134
+
135
+    def download_ok(self,content):
136
+        content_length = len(content)
137
+        self.currentbytes += content_length
138
+        self.currenttime += float(self.ts_list[self.ts_num][0])
139
+        self.totalbytes = self.currentbytes * self.totaltime / self.currenttime
140
+        self.ts_num += 1
141
+        #print "Fragment %s downloaded (%s)"%(self.ts_num,len(content))
142
+        self.ts_file.write(content)
143
+        self.progress_callback(self.currentbytes, self.totalbytes)
144
+        if self.type == "vod":
145
+            if self.ts_num >= len(self.ts_list) or (self.limit and self.currenttime>self.limit):
146
+                self.ts_file.close()
147
+                self.download_finished()
148
+            else:
149
+                self.download_fragment()
150
+        else:
151
+            if self.limit and self.currenttime>self.limit: # TODO
152
+                self.ts_file.close()
153
+                self.download_finished()
154
+            else:
155
+                self.download_fragment()
156
+
157
+    def update_manifest(self):
158
+        self.d2 = client.getPage(self.url, headers=self.headers)
159
+        self.d2.addCallbacks(self.update_manifest_ok, self.update_manifest_err)
160
+
161
+    def update_manifest_ok(self,content):
162
+        print "Update manifest"
163
+        ts_list = re.findall(r"#EXTINF:([\d\.]+),\n(.+?)$", content, re.IGNORECASE | re.MULTILINE)
164
+        last_ts = self.ts_list[-1]
165
+        found = False
166
+        for ts in ts_list:
167
+            if ts == last_ts:
168
+                found = True
169
+            elif found:
170
+                print "Append %s"%ts[1]
171
+                self.ts_list.append(ts)
172
+        #reactor.callLater(5,self.download_fragment)
173
+
174
+    def update_manifest_err(self,content):
175
+        return
176
+
177
+    def download_err(self,content):
178
+        self.deferred.errback("Error while downloading %s"%self.ts_list[self.ts_num][1])
179
+
180
+    def download_finished(self):
181
+        self.totalbytes = self.currentbytes
182
+        self.deferred.callback("Done")
183
+
184
+    def start(self):
185
+        reactor.callLater(1,self.start_download)
186
+        return self.deferred
187
+
188
+    def stop(self):
189
+        self.deferred.errback() # TODO
190
+
191
+    def addProgress(self, progress_callback):
192
+        print "[addProgress]"
193
+        self.progress_callback = progress_callback
194
+
195
+#####################################################################################################
196
+def get_header(url,headers=None):
197
+    headers = {"user-agent":USER_AGENT}
198
+    r = requests.head(url,headers=headers)
199
+    return r.headers
200
+
201
+def get_ext(mtype):
202
+    stype = "http"
203
+    if mtype in ("vnd.apple.mpegURL","application/x-mpegURL",'application/x-mpegurl',"application/vnd.apple.mpegurl"):
204
+        return ".ts","hls"
205
+    elif mtype in ("application/dash+xml"):
206
+        return ".ts","dash" # TODO dash stream type  could be different !
207
+    elif mtype in ("video/mp4"):
208
+        return ".mp4","http"
209
+    elif mtype in ("video/MP2T","video/mp2t"):
210
+        return ".ts","http"
211
+    elif mtype in ("video/x-flv"):
212
+        return ".flv","http"
213
+    elif mtype in ("video/quicktime"):
214
+        return ".mov","http"
215
+    elif mtype in ("video/x-msvideo"):
216
+        return ".avi","http"
217
+    elif mtype in ("video/x-ms-wmv"):
218
+        return ".wmv","http"
219
+    elif mtype in ("video/x-matroska"):
220
+        return ".mkv","http"
221
+    else:
222
+        return ".mp4","http"
223
+
224
+
225
+
226
+##############################################
227
+def print_progress(currentbytes, totalbytes):
228
+    progress = float(currentbytes)/float(totalbytes)*100
229
+    print "%s (%i/%i)"%(progress,currentbytes,totalbytes)
230
+
231
+def download_ok(*args):
232
+    print "Download OK"
233
+    reactor.stop()
234
+
235
+def download_err(e):
236
+    print "Download Error %s"%e.getBriefTraceback()
237
+    pass
238
+def stop():
239
+    reactor.stop()
240
+###############################################
241
+
242
+def download_vide(stream):
243
+    stream = stream[0]
244
+    url = stream["url"]
245
+    headers = stream["headers"]
246
+    output = stream["name"].replace("\\"," ").replace(":"," ").replace("|"," ")
247
+    try:
248
+        h = get_header(url,headers={"user-agent":"Enigma2"})
249
+        mtype = h.get("content-type")
250
+        ext,stream_type = get_ext(mtype)
251
+    except:
252
+        ext,stream_type = (".ts","hls")
253
+    #output = urlparse.urlparse(url)[2].split('/')[-1] + ext
254
+    output = output+ext
255
+    output = os.path.join("downloads", output)
256
+    if stream_type == "hls":
257
+        d = DownloadWithProgressFragmented(url,output,headers={"user-agent":"Enigma2"})
258
+    else:
259
+        d = DownloadWithProgress(url,output,headers={"user-agent":"Enigma2"})
260
+    d.addProgress(print_progress)
261
+    d.start().addCallback(download_ok).addErrback(download_err)
262
+    reactor.run()
263
+
264
+
265
+if __name__ == "__main__":
266
+    if len(sys.argv)>2:
267
+        url= sys.argv[1]
268
+        output = sys.argv[1]
269
+    else:
270
+        url = "http://walterebert.com/playground/video/hls/ts/480x270.m3u8"
271
+        url = "https://r3---sn-bavc5ajvh-gpme.googlevideo.com/videoplayback?key=yt6&mime=video%2Fmp4&sparams=clen%2Cdur%2Cei%2Cgir%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Cratebypass%2Crequiressl%2Csource%2Cupn%2Cexpire&expire=1490986184&lmt=1490940183963773&dur=1302.639&itag=18&ratebypass=yes&mm=31&requiressl=yes&ipbits=0&upn=azFGj8gY02w&ip=85.254.87.15&pl=23&ei=aFDeWLzqDcn-dLC_gdAM&signature=083F353AC09CD98A70AD7D9438DD3C91C781166B.715456B9C35F040BDC4728CA76A0D1779B684A90&source=youtube&mv=m&mt=1490964451&ms=au&mn=sn-bavc5ajvh-gpme&gir=yes&clen=73596250&id=o-AGH9y-hWn1MtW1VzCyI_8XYYEWODsTDBZbfagQH3BrfQ&initcwndbps=4493750"
272
+        #url = "http://techslides.com/demos/sample-videos/small.mp4"
273
+        #url = "http://wx17.poiuytrew.pw/s/c507282042b1bf25e0b72c34a68426f3/hd_30/Jackie.2016.D.iTunes.BDRip.1080p_720.mp4"
274
+        #url = "http://player.tvnet.lv/live/amlst:11/chunklist_w361981294_b528000.m3u8"
275
+        #url = "http://vod-hls-uk-live.akamaized.net/usp/auth/vod/piff_abr_full_hd/a3e90e-b08ktytr/vf_b08ktytr_f9d55583-afc7-49bb-9bf4-d8f1ac99f56f.ism.hlsv2.ism/vf_b08ktytr_f9d55583-afc7-49bb-9bf4-d8f1ac99f56f.ism.hlsv2-audio=128000-video=5070000.m3u8"
276
+        #url = "https://58174450afee9.streamlock.net/vod/mp4:_definst_/f/e/8e49fc32.mp4/playlist.m3u8?safwerwfasendtime=1490877870&safwerwfasstarttime=1490859339&safwerwfashash=hS2FfVZysQVazBQ6RJn1IhUevBkKxIF09Ly3BjfT43U="
277
+    try:
278
+        h = get_header(url,headers={"user-agent":"Enigma2"})
279
+        mtype = h.get("content-type")
280
+        ext,stream_type = get_ext(mtype)
281
+    except:
282
+        ext,stream_type = (".ts","hls")
283
+    output = urlparse.urlparse(url)[2].split('/')[-1] + ext
284
+    output = os.path.join("downloads", output)
285
+    if stream_type == "hls":
286
+        d = DownloadWithProgressFragmented(url,output,headers={"user-agent":"Enigma2"})
287
+    else:
288
+        d = DownloadWithProgress(url,output,headers={"user-agent":"Enigma2"})
289
+    d.addProgress(print_progress)
290
+    d.start().addCallback(download_ok).addErrback(download_err)
291
+    reactor.run()
292
+
293
+

+ 47
- 20
PlayStream.py View File

6
 # Used fragments of code from enigma2-plugin-tv3play by Taapat (https://github.com/Taapat/enigma2-plugin-tv3play)
6
 # Used fragments of code from enigma2-plugin-tv3play by Taapat (https://github.com/Taapat/enigma2-plugin-tv3play)
7
 #
7
 #
8
 
8
 
9
-__version__ = "0.6l"
9
+__version__ = "0.6r"
10
 __id__ = "playstream"
10
 __id__ = "playstream"
11
 __title__ = "PlayStream"
11
 __title__ = "PlayStream"
12
 __author__ = "ivars777@gmail.com"
12
 __author__ = "ivars777@gmail.com"
13
 __desc__ = "Play online video streams from various sources"
13
 __desc__ = "Play online video streams from various sources"
14
 
14
 
15
 import os,time,sys,os,os.path
15
 import os,time,sys,os,os.path
16
-import datetime,re
16
+import datetime,re, traceback
17
 import urllib2
17
 import urllib2
18
 
18
 
19
 from Plugins.Plugin import PluginDescriptor
19
 from Plugins.Plugin import PluginDescriptor
53
 from plugin_locale import _
53
 from plugin_locale import _
54
 import ContentSources
54
 import ContentSources
55
 import util
55
 import util
56
-from VideoDownload import downloadJob, HLSDownloadJob,VideoDownloadList
56
+from VideoDownload import DownloadJob, HLSDownloadJob,VideoDownloadList
57
+from Downloader import get_header, get_ext
57
 #import enigma2_api
58
 #import enigma2_api
58
 
59
 
59
 e2 = None
60
 e2 = None
135
         #print self.content
136
         #print self.content
136
         self["list"].setList(self.content)
137
         self["list"].setList(self.content)
137
         self["cur"].setText(self.content[0][3])
138
         self["cur"].setText(self.content[0][3])
138
-        self.setTitle2(self.cur_menu[0])
139
+        self.setTitle2(self.cur_menu[0] + " (" + self.cur_menu[1].split("::")[0]+")")
139
         self.show_picture(self.content[0][2])
140
         self.show_picture(self.content[0][2])
140
         reactor.callLater(1,self.check_update)
141
         reactor.callLater(1,self.check_update)
141
 
142
 
253
             return
254
             return
254
 
255
 
255
         elif self.sources.is_video(current[1]):
256
         elif self.sources.is_video(current[1]):
256
-            if self.sources.stream_type(current[1]):
257
+            if ContentSources.stream_type(current[1]):
257
                 stream = util.item()
258
                 stream = util.item()
258
                 stream["url"] = current[1]
259
                 stream["url"] = current[1]
259
                 stream["name"] = current[0]
260
                 stream["name"] = current[0]
263
                     streams = self.sources.get_streams(current[1])
264
                     streams = self.sources.get_streams(current[1])
264
                 except Exception,e:
265
                 except Exception,e:
265
                     print str(e)
266
                     print str(e)
267
+                    traceback.print_exc()
266
                     self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
268
                     self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
267
                     return
269
                     return
268
             if streams:
270
             if streams:
274
                 try:
276
                 try:
275
                     self.session.open(PSPlayer, streams)
277
                     self.session.open(PSPlayer, streams)
276
                 except Exception as e:
278
                 except Exception as e:
279
+                    traceback.print_exc()
277
                     self.msg("Error launching player - " + str(e))
280
                     self.msg("Error launching player - " + str(e))
278
                     return
281
                     return
279
             else:
282
             else:
288
                 index = zip(*new_content)[1].index(cur_menu_old[1])
291
                 index = zip(*new_content)[1].index(cur_menu_old[1])
289
             except:
292
             except:
290
                 index = 0
293
                 index = 0
291
-            self.setTitle2(self.cur_menu[0])
294
+            self.setTitle2(self.cur_menu[0]+ " (" + self.cur_menu[1].split("::")[0]+")")
292
             self.show_content(new_content,index)
295
             self.show_content(new_content,index)
293
 
296
 
294
         else:
297
         else:
313
                 nfo = self.sources.get_info(self.current[1])
316
                 nfo = self.sources.get_info(self.current[1])
314
             except Exception, e:
317
             except Exception, e:
315
                 print str(e)
318
                 print str(e)
319
+                traceback.print_exc()
316
                 self.session.open(MessageBox, "Error - %s" % str(e), MessageBox.TYPE_INFO)
320
                 self.session.open(MessageBox, "Error - %s" % str(e), MessageBox.TYPE_INFO)
317
                 return
321
                 return
318
         else:
322
         else:
333
                 new_content = self.sources.get_content(self.cur_menu[1])
337
                 new_content = self.sources.get_content(self.cur_menu[1])
334
             except Exception,e:
338
             except Exception,e:
335
                 self.cur_menu = self.history.pop()
339
                 self.cur_menu = self.history.pop()
340
+                traceback.print_exc()
336
                 self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
341
                 self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
337
                 return
342
                 return
338
-            self.setTitle2(self.cur_menu[0])
343
+            self.setTitle2(self.cur_menu[0] + " (" + self.cur_menu[1].split("::")[0]+")")
339
             self.show_content(new_content)
344
             self.show_content(new_content)
340
 
345
 
341
     def back(self):
346
     def back(self):
483
         self.session.open(VideoDownloadList)
488
         self.session.open(VideoDownloadList)
484
 
489
 
485
     def download_video(self,current, download_dir=""):
490
     def download_video(self,current, download_dir=""):
486
-        if self.sources.stream_type(current[1]):
491
+        if ContentSources.stream_type(current[1]):
487
             stream = util.item()
492
             stream = util.item()
488
             stream["url"] = current[1]
493
             stream["url"] = current[1]
489
             stream["name"] = current[0]
494
             stream["name"] = current[0]
493
                 streams = self.sources.get_streams(current[1])
498
                 streams = self.sources.get_streams(current[1])
494
             except Exception,e:
499
             except Exception,e:
495
                 print "Error - %s"%str(e)
500
                 print "Error - %s"%str(e)
501
+                traceback.print_exc()
496
                 self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
502
                 self.session. open(MessageBox, "Error - %s"%str(e) , MessageBox.TYPE_INFO)
497
                 return
503
                 return
498
         if not streams:
504
         if not streams:
514
         print "download stream",stream
520
         print "download stream",stream
515
         #self.msg("Start downloading..")
521
         #self.msg("Start downloading..")
516
         self.stream = stream
522
         self.stream = stream
517
-        stream_type = self.stream["type"] #self.sources.stream_type(stream["url"])
523
+        url = stream["url"]
524
+        headers = stream["headers"] if "headers" in stream else {}
525
+        stream_type = ContentSources.stream_type(url) #self.sources.stream_type(stream["url"])
518
         if not stream_type: #
526
         if not stream_type: #
519
             print "Not supported stream type found to download - %s"%(self.current[1])
527
             print "Not supported stream type found to download - %s"%(self.current[1])
520
             self.msg("Not supported stream type found to download - %s"%(self.current[1]))
528
             self.msg("Not supported stream type found to download - %s"%(self.current[1]))
521
             return
529
             return
522
 
530
 
523
         title = self.stream["name"].strip()
531
         title = self.stream["name"].strip()
524
-        url = self.stream["url"]
525
-        stream_type = self.stream["type"] #self.sources.stream_type(stream["url"])
526
         downloadDir = config.plugins.playstream.download_dir.value if not download_dir else download_dir
532
         downloadDir = config.plugins.playstream.download_dir.value if not download_dir else download_dir
527
         if not os.path.exists(downloadDir):
533
         if not os.path.exists(downloadDir):
528
             try:
534
             try:
529
                 os.mkdir(downloadDir)
535
                 os.mkdir(downloadDir)
530
             except Exception as e:
536
             except Exception as e:
537
+                traceback.print_exc()
531
                 print 'Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e))
538
                 print 'Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e))
532
                 self.msg('Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e)))
539
                 self.msg('Error creating download directory "%s"!\nPlease specify in the settings existing directory\n%s'%(downloadDir,str(e)))
533
                 return
540
                 return
534
         fname0 = util.make_fname(title)
541
         fname0 = util.make_fname(title)
535
-        fname = fname0 +".mp4"
536
-        outputfile = os.path.join(downloadDir, fname)
537
         #print "Trying to download - ", current
542
         #print "Trying to download - ", current
538
         if self.stream["img"]:
543
         if self.stream["img"]:
539
             try:
544
             try:
566
             nfofile = subfile = os.path.join(downloadDir,fname0+".nfo")
571
             nfofile = subfile = os.path.join(downloadDir,fname0+".nfo")
567
             with open(nfofile,"w") as f:
572
             with open(nfofile,"w") as f:
568
                 nfo_txt = util.nfo2xml(self.stream["nfo"])
573
                 nfo_txt = util.nfo2xml(self.stream["nfo"])
569
-                f.write()
574
+                f.write(nfo_txt)
575
+        try:
576
+            h = get_header(url,headers)
577
+        except:
578
+            h = None
579
+        if not h:
580
+            self.session.open(MessageBox, "Can non get headers - %s" % url)
581
+            return False
582
+
583
+        mtype = h.get("content-type")
584
+        if not mtype:
585
+            mtype = "videp/mp4" # TODO default mtype if not content-type in header?
586
+
587
+        fext,stream_type = get_ext(mtype)
588
+        fname = fname0 + fext
589
+        outputfile = os.path.join(downloadDir, fname)
570
         overwrite = config.plugins.playstream.overwrite_download.value
590
         overwrite = config.plugins.playstream.overwrite_download.value
571
         if not overwrite and os.path.exists(outputfile):
591
         if not overwrite and os.path.exists(outputfile):
572
             self.msg( _('Sorry, this file already exists:\n%s') % outputfile)
592
             self.msg( _('Sorry, this file already exists:\n%s') % outputfile)
573
             return False
593
             return False
574
             #os.remove(outputfile)
594
             #os.remove(outputfile)
575
 
595
 
576
-        if stream_type in ("http","https"):
596
+        if stream_type in ("http","https", "hls"):
577
             print "\n**Download %s - %s"%(title,url)
597
             print "\n**Download %s - %s"%(title,url)
578
-            #reload(downloadJob)
579
-            job_manager.AddJob(downloadJob(url, outputfile, title[:20], self.video_download_stop))
598
+            #reload(DownloadJob)
599
+            try:
600
+                job_manager.AddJob(DownloadJob(url, outputfile, title[:20], self.video_download_stop, headers, stream_type))
601
+            except Exception as e:
602
+                print str(e)
603
+                traceback.print_exc()
604
+                self.session.open(MessageBox, "Error - %s" % str(e), MessageBox.TYPE_INFO)
605
+                return  False
606
+
580
             self.active_downloads += 1
607
             self.active_downloads += 1
581
             #self.msg(_('Video download started!'))
608
             #self.msg(_('Video download started!'))
582
             return True
609
             return True
595
             self.msg("RSTP stream download not yet implemented!")
622
             self.msg("RSTP stream download not yet implemented!")
596
             return False
623
             return False
597
         else:
624
         else:
598
-            self.msg("Unkown stream type!")
625
+            self.msg("Unkown stream type - %s!"%stream_type)
599
             return False
626
             return False
600
 
627
 
601
 
628
 
637
             self.session.open(OptionsScreen,self)
664
             self.session.open(OptionsScreen,self)
638
 
665
 
639
     def video_download_stop(self,title):
666
     def video_download_stop(self,title):
640
-        #self.activeDownloads -= 1
641
-        #self.msg("Download '%s'finished!"%title)
667
+        self.active_downloads -= 1
668
+        self.msg2(title)
642
         print "video_download_stop ", title
669
         print "video_download_stop ", title
643
 
670
 
644
     def msg2(self,msg,timeout=10,mtype = None):
671
     def msg2(self,msg,timeout=10,mtype = None):

+ 231
- 216
VideoDownload.py View File

1
 import os
1
 import os
2
+from os import path
2
 
3
 
3
 from enigma import eTimer, getDesktop
4
 from enigma import eTimer, getDesktop
4
 from Components.ActionMap import ActionMap
5
 from Components.ActionMap import ActionMap
7
 from Components.Sources.StaticText import StaticText
8
 from Components.Sources.StaticText import StaticText
8
 from Components.Task import Task, Job, job_manager
9
 from Components.Task import Task, Job, job_manager
9
 from Screens.Screen import Screen
10
 from Screens.Screen import Screen
10
-from Tools.Downloader import downloadWithProgress
11
 from Tools.Directories import fileExists
11
 from Tools.Directories import fileExists
12
-from os import path
12
+
13
+from Downloader import DownloadWithProgress, DownloadWithProgressFragmented,get_header
14
+
15
+
16
+def timer_callback(timer,callback):
17
+    if "callback" in dir(timer):
18
+        timer.callback.append(callback)
19
+    else:
20
+        timer_conn = timer.timeout.connect(callback)
21
+
22
+###########################################################################################
23
+class DownloadJob(Job):
24
+    def __init__(self, url, outputfile, title, downloadStop,headers=None,stream_type="http"):
25
+        Job.__init__(self, title)
26
+        DownloadTask(self, url, outputfile, title, downloadStop,headers,stream_type)
27
+
28
+
29
+class DownloadTask(Task):
30
+    def __init__(self, job, url, outputfile, title, downloadStop,headers={},stream_type="http"):
31
+        Task.__init__(self, job, _('Downloading'))
32
+        self.job = job
33
+        self.title = title
34
+        self.url = url
35
+        self.outputfile = outputfile
36
+        self.headers = headers
37
+        self.stream_type = stream_type
38
+        self.downloadStop = downloadStop
39
+        self.job.currentbytes = 0
40
+        self.job.totalbytes = -1
41
+
42
+    def run(self, callback):
43
+        self.callback = callback
44
+        if self.stream_type == "hls":
45
+            self.download = DownloadWithProgressFragmented(self.url, self.outputfile,self.headers)
46
+        else:
47
+            self.download = DownloadWithProgress(self.url, self.outputfile, self.headers)
48
+        self.download.addProgress(self.downloadProgress)
49
+        self.download.start().addCallback(self.downloadFinished).addErrback(self.downloadFailed)
50
+
51
+    def downloadProgress(self, currentbytes, totalbytes):
52
+        self.job.currentbytes = currentbytes
53
+        self.job.totalbytes = totalbytes
54
+        progress = self.job.currentbytes/float(self.job.totalbytes) * 100
55
+        self.setProgress(progress)
56
+
57
+    def downloadFinished(self, result):
58
+        Task.processFinished(self, 0)
59
+        self.setProgress(self.end)
60
+        self.downloadStop("Success - " + self.title)
61
+
62
+    def downloadFailed(self, failure_instance=None, error_message=''):
63
+        print '[PlayStream] Video download failed'
64
+        if error_message == '' and failure_instance is not None:
65
+            error_message = failure_instance.getErrorMessage()
66
+            print '[PlayStream]', str(error_message)
67
+        Task.processFinished(self, 1)
68
+        self.downloadStop("Failed - "+ self.title)
13
 
69
 
14
 class HLSDownloadJob(Job):
70
 class HLSDownloadJob(Job):
15
-	def __init__(self, url, destfile, title,downloadStop):
16
-		Job.__init__(self, title)
17
-		AddHLSProcessTask(self, url, destfile, title, downloadStop)
71
+    def __init__(self, url, destfile, title,downloadStop,headers=None):
72
+        Job.__init__(self, title)
73
+        AddHLSProcessTask(self, url, destfile, title, downloadStop,headers)
18
 
74
 
75
+
76
+######################################################################################################
19
 class AddHLSProcessTask(Task):
77
 class AddHLSProcessTask(Task):
20
-	def __init__(self, job, url, destfile, title,downloadStop):
21
-		Task.__init__(self, job, title)
22
-		self.job = job
23
-		self.title = title
24
-		cmdline = '/usr/bin/gst-launch-1.0 "%s" !  hlsdemux ! filesink location="%s"'%(url,destfile)	
25
-		self.setCmdline(cmdline)
26
-		self.url = url
27
-		self.destfile = destfile
28
-		self.downloadStop = downloadStop
29
-		self.job.currentbytes = 0
30
-		self.job.totalbytes = 0
31
-		#self.setProgress(100)
32
-		self.ProgressTimer = eTimer()
33
-		self.ProgressTimer.callback.append(self.ProgressUpdate)
34
-
35
-	def ProgressUpdate(self):
36
-		if not fileExists(self.destfile, 'r'):
37
-			return
38
-		self.job.currentbytes = path.getsize(self.destfile)
39
-		progress = self.job.currentbytes/float(self.job.totalbytes) * 100 if self.job.totalbytes else 0
40
-		self.setProgress(progress)
41
-		#self.setProgress(int((path.getsize(self.destfile)/float(self.totalbytes))*100))
42
-		self.ProgressTimer.start(5000, True)
43
-
44
-	def prepare(self):
45
-		self.job.totalbytes = -1 # TODO getsize(self.url)
46
-		self.ProgressTimer.start(5000, True)
47
-
48
-	def afterRun(self):
49
-		#self.setProgress(100)
50
-		self.ProgressTimer.stop()
51
-		self.downloadStop(self.title)
52
-
53
-class downloadJob(Job):
54
-	def __init__(self, url, outputfile, title, downloadStop):
55
-		Job.__init__(self, title)
56
-		downloadTask(self, url, outputfile, title, downloadStop)
57
-
58
-
59
-class downloadTask(Task):
60
-	def __init__(self, job, url, outputfile, title, downloadStop):
61
-		Task.__init__(self, job, _('Downloading'))
62
-		self.job = job
63
-		self.title = title
64
-		self.url = url
65
-		self.outputfile = outputfile
66
-		self.downloadStop = downloadStop
67
-		self.job.currentbytes = 0
68
-		self.job.totalbytes = -1		
69
-
70
-	def run(self, callback):
71
-		self.callback = callback
72
-		self.download = downloadWithProgress(self.url, self.outputfile)
73
-		self.download.addProgress(self.downloadProgress)
74
-		self.download.start().addCallback(self.downloadFinished)\
75
-			.addErrback(self.downloadFailed)
76
-
77
-	def downloadProgress(self, currentbytes, totalbytes):
78
-		self.job.currentbytes = currentbytes
79
-		self.job.totalbytes = totalbytes
80
-		progress = self.job.currentbytes/float(self.job.totalbytes) * 100
81
-		self.setProgress(progress)
82
-
83
-	def downloadFinished(self, result):
84
-		Task.processFinished(self, 0)
85
-		self.setProgress(self.end)
86
-		self.downloadStop(self.title)
87
-
88
-	def downloadFailed(self, failure_instance=None, error_message=''):
89
-		print '[PlayStream] Video download failed'
90
-		if error_message == '' and failure_instance is not None:
91
-			error_message = failure_instance.getErrorMessage()
92
-			print '[PlayStream]', str(error_message)
93
-		Task.processFinished(self, 1)
94
-		self.downloadStop("")
78
+    def __init__(self, job, url, destfile, title,downloadStop,headers=None):
79
+        Task.__init__(self, job, title)
80
+        self.job = job
81
+        self.title = title
82
+        cmdline = '/usr/bin/gst-launch-1.0 "%s" !  hlsdemux ! filesink location="%s"'%(url,destfile)
83
+        self.setCmdline(cmdline)
84
+        self.url = url
85
+        self.destfile = destfile
86
+        self.downloadStop = downloadStop
87
+        self.job.currentbytes = 0
88
+        self.job.totalbytes = 0
89
+        #self.setProgress(100)
90
+        self.ProgressTimer = eTimer()
91
+        timer_callback(self.ProgressTimer, self.ProgressUpdate)
92
+
93
+    def ProgressUpdate(self):
94
+        if not fileExists(self.destfile, 'r'):
95
+            return
96
+        self.job.currentbytes = path.getsize(self.destfile)
97
+        progress = self.job.currentbytes/float(self.job.totalbytes) * 100 if self.job.totalbytes else 0
98
+        self.setProgress(progress)
99
+        #self.setProgress(int((path.getsize(self.destfile)/float(self.totalbytes))*100))
100
+        self.ProgressTimer.start(5000, True)
101
+
102
+    def prepare(self):
103
+        self.job.totalbytes = -1 # TODO getsize(self.url)
104
+        self.ProgressTimer.start(5000, True)
105
+
106
+    def afterRun(self):
107
+        #self.setProgress(100)
108
+        self.ProgressTimer.stop()
109
+        self.downloadStop(self.title)
95
 
110
 
96
 
111
 
97
 class VideoDownloadList(Screen):
112
 class VideoDownloadList(Screen):
98
-	screenWidth = getDesktop(0).size().width()
99
-	if screenWidth and screenWidth == 1920:
100
-		skin = """<screen position="center,center" size="945,555">
101
-				<widget source="list" render="Listbox" position="center,45" size="900,405" \
102
-					scrollbarMode="showOnDemand" >
103
-					<convert type="TemplatedMultiContent" >
104
-						{"template": [
105
-							MultiContentEntryText(pos=(15,1), size=(465,33), \
106
-								font=0, flags=RT_HALIGN_LEFT, text=1), # Title
107
-							MultiContentEntryText(pos=(345,1), size=(225,33), \
108
-								font=0, flags=RT_HALIGN_RIGHT, text=2), # State
109
-							MultiContentEntryProgress(pos=(585,6), size=(150,33), \
110
-								percent=-3), # Progress
111
-							MultiContentEntryText(pos=(750,1), size=(120,33), \
112
-								font=0, flags=RT_HALIGN_LEFT, text=4), # Percentage
113
-							],
114
-						"fonts": [gFont("Regular",30)],
115
-						"itemHeight": 45}
116
-					</convert>
117
-				</widget>
118
-				<ePixmap position="center,484" size="210,60" pixmap="skin_default/buttons/red.png" \
119
-					transparent="1" alphatest="on" />
120
-				<widget source="key_red" render="Label" position="center,485" zPosition="2" \
121
-					size="210,60" valign="center" halign="center" font="Regular;33" transparent="1" />
122
-				</screen>"""
123
-	else:
124
-		skin = """<screen position="center,center" size="630,370">
125
-				<widget source="list" render="Listbox" position="center,30" size="600,270" \
126
-					scrollbarMode="showOnDemand" >
127
-					<convert type="TemplatedMultiContent" >
128
-						{"template": [
129
-							MultiContentEntryText(pos=(10,1), size=(210,22), \
130
-								font=0, flags=RT_HALIGN_LEFT, text=1), # Title
131
-							MultiContentEntryText(pos=(230,1), size=(150,22), \
132
-								font=0, flags=RT_HALIGN_RIGHT, text=2), # State
133
-							MultiContentEntryProgress(pos=(390,4), size=(100,22), \
134
-								percent=-3), # Progress
135
-							MultiContentEntryText(pos=(500,1), size=(80,22), \
136
-								font=0, flags=RT_HALIGN_LEFT, text=4), # Percentage
137
-							],
138
-						"fonts": [gFont("Regular",20)],
139
-						"itemHeight": 30}
140
-					</convert>
141
-				</widget>
142
-				<ePixmap position="center,323" size="140,40" pixmap="skin_default/buttons/red.png" \
143
-					transparent="1" alphatest="on" />
144
-				<widget source="key_red" render="Label" position="center,328" zPosition="2" \
145
-					size="140,30" valign="center" halign="center" font="Regular;22" transparent="1" />
146
-				</screen>"""
147
-
148
-	def __init__(self, session):
149
-		Screen.__init__(self, session)
150
-		self['key_red'] = StaticText(_('Exit'))
151
-		self['list'] = List([])
152
-		self['actions'] = ActionMap(['SetupActions', 'ColorActions'],
153
-			{
154
-				'cancel': self.close,
155
-				'ok': self.ok,
156
-				'red': self.close,
157
-		                'blue': self.abort
158
-			}, -2)
159
-		self.onLayoutFinish.append(self.layoutFinished)
160
-		self.onClose.append(self.cleanVariables)
161
-		self.progressTimer = eTimer()
162
-		self.progressTimer.callback.append(self.updateDownloadList)
163
-
164
-	def layoutFinished(self):
165
-		self.setTitle(_('Active video downloads'))
166
-		self.updateDownloadList()
167
-
168
-	def cleanVariables(self):
169
-		del self.progressTimer
170
-
171
-	def updateDownloadList(self):
172
-		self.progressTimer.stop()
173
-		downloadList = []
174
-		for job in job_manager.getPendingJobs():
175
-			progress = job.progress / float(job.end) * 100
176
-			currentbytes = job.currentbytes if "currentbytes" in dir(job) else 0
177
-			downloadList.append((job, job.name, job.getStatustext(),
178
-				int(progress), "%.1fM"%(currentbytes/1024.0/1024.0) ))
179
-		self['list'].updateList(downloadList)
180
-		if downloadList:
181
-			self.progressTimer.startLongTimer(2)
182
-
183
-	def ok(self):
184
-		current = self['list'].getCurrent()
185
-		if current:
186
-			from Screens.TaskView import JobView
187
-			self.session.open(JobView, current[0])
188
-			
189
-	def abort(self):
190
-		current = self['list'].getCurrent()
191
-		job = current[0]
192
-		if job.status == job.NOT_STARTED:
193
-			job_manager.active_jobs.remove(job)
194
-			self.close(False)
195
-		elif job.status == job.IN_PROGRESS:
196
-			job.cancel()
197
-		else:
198
-			self.close(False)
199
-	
113
+    screenWidth = getDesktop(0).size().width()
114
+    if screenWidth and screenWidth == 1920:
115
+        skin = """<screen position="center,center" size="945,555">
116
+                <widget source="list" render="Listbox" position="center,45" size="900,405" \
117
+                    scrollbarMode="showOnDemand" >
118
+                    <convert type="TemplatedMultiContent" >
119
+                        {"template": [
120
+                            MultiContentEntryText(pos=(15,1), size=(465,33), \
121
+                                font=0, flags=RT_HALIGN_LEFT, text=1), # Title
122
+                            MultiContentEntryText(pos=(345,1), size=(225,33), \
123
+                                font=0, flags=RT_HALIGN_RIGHT, text=2), # State
124
+                            MultiContentEntryProgress(pos=(585,6), size=(150,33), \
125
+                                percent=-3), # Progress
126
+                            MultiContentEntryText(pos=(750,1), size=(120,33), \
127
+                                font=0, flags=RT_HALIGN_LEFT, text=4), # Percentage
128
+                            ],
129
+                        "fonts": [gFont("Regular",30)],
130
+                        "itemHeight": 45}
131
+                    </convert>
132
+                </widget>
133
+                <ePixmap position="center,484" size="210,60" pixmap="skin_default/buttons/red.png" \
134
+                    transparent="1" alphatest="on" />
135
+                <widget source="key_red" render="Label" position="center,485" zPosition="2" \
136
+                    size="210,60" valign="center" halign="center" font="Regular;33" transparent="1" />
137
+                </screen>"""
138
+    else:
139
+        skin = """<screen position="center,center" size="630,370">
140
+                <widget source="list" render="Listbox" position="center,30" size="600,270" \
141
+                    scrollbarMode="showOnDemand" >
142
+                    <convert type="TemplatedMultiContent" >
143
+                        {"template": [
144
+                            MultiContentEntryText(pos=(10,1), size=(210,22), \
145
+                                font=0, flags=RT_HALIGN_LEFT, text=1), # Title
146
+                            MultiContentEntryText(pos=(230,1), size=(150,22), \
147
+                                font=0, flags=RT_HALIGN_RIGHT, text=2), # State
148
+                            MultiContentEntryProgress(pos=(390,4), size=(100,22), \
149
+                                percent=-3), # Progress
150
+                            MultiContentEntryText(pos=(500,1), size=(80,22), \
151
+                                font=0, flags=RT_HALIGN_LEFT, text=4), # Percentage
152
+                            ],
153
+                        "fonts": [gFont("Regular",20)],
154
+                        "itemHeight": 30}
155
+                    </convert>
156
+                </widget>
157
+                <ePixmap position="center,323" size="140,40" pixmap="skin_default/buttons/red.png" \
158
+                    transparent="1" alphatest="on" />
159
+                <widget source="key_red" render="Label" position="center,328" zPosition="2" \
160
+                    size="140,30" valign="center" halign="center" font="Regular;22" transparent="1" />
161
+                </screen>"""
162
+
163
+    def __init__(self, session):
164
+        Screen.__init__(self, session)
165
+        self['key_red'] = StaticText(_('Exit'))
166
+        self['list'] = List([])
167
+        self['actions'] = ActionMap(['SetupActions', 'ColorActions'],
168
+            {
169
+                'cancel': self.close,
170
+                'ok': self.ok,
171
+                'red': self.close,
172
+                        'blue': self.abort
173
+            }, -2)
174
+        self.onLayoutFinish.append(self.layoutFinished)
175
+        self.onClose.append(self.cleanVariables)
176
+        self.progressTimer = eTimer()
177
+        timer_callback(self.progressTimer,self.updateDownloadList)
178
+
179
+    def layoutFinished(self):
180
+        self.setTitle(_('Active video downloads'))
181
+        self.updateDownloadList()
182
+
183
+    def cleanVariables(self):
184
+        del self.progressTimer
185
+
186
+    def updateDownloadList(self):
187
+        self.progressTimer.stop()
188
+        downloadList = []
189
+        for job in job_manager.getPendingJobs():
190
+            progress = job.progress / float(job.end) * 100
191
+            currentbytes = job.currentbytes if "currentbytes" in dir(job) else 0
192
+            downloadList.append((job, job.name, job.getStatustext(),
193
+                int(progress), "%.1fM"%(currentbytes/1024.0/1024.0) ))
194
+        self['list'].updateList(downloadList)
195
+        if downloadList:
196
+            self.progressTimer.startLongTimer(2)
197
+
198
+    def ok(self):
199
+        current = self['list'].getCurrent()
200
+        if current:
201
+            from Screens.TaskView import JobView
202
+            self.session.open(JobView, current[0])
203
+
204
+    def abort(self):
205
+        current = self['list'].getCurrent()
206
+        job = current[0]
207
+        if job.status == job.NOT_STARTED:
208
+            job_manager.active_jobs.remove(job)
209
+            self.close(False)
210
+        elif job.status == job.IN_PROGRESS:
211
+            job.cancel()
212
+        else:
213
+            self.close(False)
214
+
200
 
215
 
201
 class VideoDirBrowser(Screen):
216
 class VideoDirBrowser(Screen):
202
-	def __init__(self, session, downloadDir):
203
-		Screen.__init__(self, session)
204
-		self.skinName = ['VideoDirBrowser', 'FileBrowser']
205
-		self['key_red'] = StaticText(_('Cancel'))
206
-		self['key_green'] = StaticText(_('Use'))
207
-		if not os.path.exists(downloadDir):
208
-			downloadDir = '/'
209
-		self.filelist = FileList(downloadDir, showFiles = False)
210
-		self['filelist'] = self.filelist
211
-		self['FilelistActions'] = ActionMap(['SetupActions', 'ColorActions'],
212
-			{
213
-				'cancel': self.cancel,
214
-				'red': self.cancel,
215
-				'ok': self.ok,
216
-				'green': self.use
217
-			}, -2)
218
-		self.onLayoutFinish.append(self.layoutFinished)
219
-
220
-	def layoutFinished(self):
221
-		self.setTitle(_('Please select the download directory'))
222
-
223
-	def ok(self):
224
-		if self.filelist.canDescent():
225
-			self.filelist.descent()
226
-
227
-	def use(self):
228
-		currentDir = self['filelist'].getCurrentDirectory()
229
-		dirName = self['filelist'].getFilename()
230
-		if currentDir is None or \
231
-			(self.filelist.canDescent() and dirName and len(dirName) > len(currentDir)):
232
-			self.close(dirName)
233
-
234
-	def cancel(self):
235
-		self.close(False)
217
+    def __init__(self, session, downloadDir):
218
+        Screen.__init__(self, session)
219
+        self.skinName = ['VideoDirBrowser', 'FileBrowser']
220
+        self['key_red'] = StaticText(_('Cancel'))
221
+        self['key_green'] = StaticText(_('Use'))
222
+        if not os.path.exists(downloadDir):
223
+            downloadDir = '/'
224
+        self.filelist = FileList(downloadDir, showFiles = False)
225
+        self['filelist'] = self.filelist
226
+        self['FilelistActions'] = ActionMap(['SetupActions', 'ColorActions'],
227
+            {
228
+                'cancel': self.cancel,
229
+                'red': self.cancel,
230
+                'ok': self.ok,
231
+                'green': self.use
232
+            }, -2)
233
+        self.onLayoutFinish.append(self.layoutFinished)
234
+
235
+    def layoutFinished(self):
236
+        self.setTitle(_('Please select the download directory'))
237
+
238
+    def ok(self):
239
+        if self.filelist.canDescent():
240
+            self.filelist.descent()
241
+
242
+    def use(self):
243
+        currentDir = self['filelist'].getCurrentDirectory()
244
+        dirName = self['filelist'].getFilename()
245
+        if currentDir is None or \
246
+            (self.filelist.canDescent() and dirName and len(dirName) > len(currentDir)):
247
+            self.close(dirName)
248
+
249
+    def cancel(self):
250
+        self.close(False)

+ 14
- 0
changelog.md View File

1
+**0.6r** (01.04.2017)
2
+- [feature] LMT Straume video (bez TV, kas strādā tikai LMT tīklā)
3
+
4
+**0.6p** (31.03.2017)
5
+- [bugfix] filmix sērijas
6
+- [bugfix] daļa failu download kļūdu novērstas (bet līdz galam vēl nestrādā)
7
+
8
+**0.6n** (29.03.2017)
9
+- [bugfix][feature] failu download pārtaisīts nezimantojot gstreamer
10
+
11
+**0.6m** (28.03.2017)
12
+- [bugfix] viaplay login un playback
13
+- [feature] tekošais source nosaukums loga titlē
14
+
1
 **0.6j** (22.03.2017)
15
 **0.6j** (22.03.2017)
2
 - salabota InsecureRequestWaening importa kļūda
16
 - salabota InsecureRequestWaening importa kļūda
3
 
17
 

+ 1
- 0
deploy.bat View File

21
 PlayStream.png
21
 PlayStream.png
22
 ContentSources.py
22
 ContentSources.py
23
 VideoDownload.py
23
 VideoDownload.py
24
+Downloader.py
24
 enigma2_api.py
25
 enigma2_api.py
25
 resolver.py
26
 resolver.py
26
 util.py
27
 util.py

+ 15
- 10
imake.bat View File

1
 @echo off
1
 @echo off
2
 :=== Parameters ===
2
 :=== Parameters ===
3
 
3
 
4
-if ()==(%1%) (
5
-    python get_version.py PlayStream.py >version.txt
6
-    cat version.txt
7
-    pause
8
-    set /p ver=<version.txt
9
-    echo Version: %ver%
10
-) else (
11
-    set ver=%1
12
-)
4
+python get_version.py PlayStream.py >version.txt
5
+cat version.txt
6
+pause
7
+set /p ver=<version.txt
8
+echo Version: %ver%
9
+
13
 pause
10
 pause
14
 set prog=PlayStream
11
 set prog=PlayStream
15
 set pack_name=enigma2-plugin-extensions-playstream
12
 set pack_name=enigma2-plugin-extensions-playstream
20
 set script_dir=usr\script\
17
 set script_dir=usr\script\
21
 set ipk_dir=ipkg\
18
 set ipk_dir=ipkg\
22
 set release_dir=release\
19
 set release_dir=release\
23
-set feed_dir=w:\feed\
20
+set feed_dir=q:\web\feed\
24
 
21
 
25
 set AR=\MinGW\bin\ar.exe
22
 set AR=\MinGW\bin\ar.exe
26
 set TAR=\MinGW\msys\1.0\bin\tar.exe
23
 set TAR=\MinGW\msys\1.0\bin\tar.exe
40
 %prog%.png
37
 %prog%.png
41
 ContentSources.py
38
 ContentSources.py
42
 VideoDownload.py
39
 VideoDownload.py
40
+Downloader.py
43
 enigma2_api.py
41
 enigma2_api.py
44
 resolver.py
42
 resolver.py
45
 util.py
43
 util.py
61
 sources\mtgplay.py
59
 sources\mtgplay.py
62
 sources\play24.py
60
 sources\play24.py
63
 sources\replay.py
61
 sources\replay.py
62
+sources\lmt.py
64
 sources\serialguru.py
63
 sources\serialguru.py
65
 sources\tvdom.py
64
 sources\tvdom.py
66
 sources\ustvnow.py
65
 sources\ustvnow.py
160
 %AR% -r %release_dir%%pack_name%_%ver% ipkg\debian-binary ipkg\data.tar.gz ipkg\control.tar.gz
159
 %AR% -r %release_dir%%pack_name%_%ver% ipkg\debian-binary ipkg\data.tar.gz ipkg\control.tar.gz
161
 @echo on
160
 @echo on
162
 mv %release_dir%%pack_name%_%ver% %release_dir%%pack_name%_%ver%.ipk
161
 mv %release_dir%%pack_name%_%ver% %release_dir%%pack_name%_%ver%.ipk
162
+
163
 git add %release_dir%%pack_name%_%ver%.ipk
163
 git add %release_dir%%pack_name%_%ver%.ipk
164
+if not ()==(%1%) (
165
+git commit -m %ver%
166
+git tag %ver%
167
+git push
168
+)
164
 
169
 
165
 copy %release_dir%%pack_name%_%ver%.ipk %feed_dir%%pack_name%_%ver%.ipk
170
 copy %release_dir%%pack_name%_%ver%.ipk %feed_dir%%pack_name%_%ver%.ipk
166
 pushd  %feed_dir%
171
 pushd  %feed_dir%

+ 274
- 83
playstreamproxy.py View File

26
 
26
 
27
 HOST_NAME = ""
27
 HOST_NAME = ""
28
 PORT_NUMBER = 88
28
 PORT_NUMBER = 88
29
-DEBUG = False
29
+DEBUG = True
30
+DEBUG2 = False
30
 
31
 
32
+SPLIT_CHAR = "~"
33
+SPLIT_CODE = "%7E"
34
+EQ_CODE = "%3D"
35
+COL_CODE = "%3A"
31
 headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
36
 headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
37
+headers0 = headers2dict("""
38
+icy-metadata: 1
39
+Cache-Control: max-age=0
40
+Accept-Encoding: gzip, deflate
41
+User-Agent: GStreamer souphttpsrc libsoup/2.52.2
42
+Connection: Keep-Alive
43
+""")
32
 sessions = {}
44
 sessions = {}
33
 
45
 
34
 class StreamHandler(BaseHTTPRequestHandler):
46
 class StreamHandler(BaseHTTPRequestHandler):
35
 
47
 
36
     def do_HEAD(self):
48
     def do_HEAD(self):
49
+        print "**head"
37
         self.send_response(200)
50
         self.send_response(200)
38
-        self.send_header("Server", "StreamProxy")
39
-        self.send_header("Content-type", "text/html")
51
+        self.send_header("Server", "playstreamproxy")
52
+        if ".m3u8" in self.path.lower():
53
+            ct = "application/vnd.apple.mpegurl"
54
+        elif ".ts" in self.path.lower():
55
+            ct = "video/MP2T"
56
+        elif ".mp4" in ".ts" in self.path.lower():
57
+            ct = "video/mp4"
58
+        else:
59
+            ct = "text/html"
60
+        self.send_header("Content-type", ct)
40
         self.end_headers()
61
         self.end_headers()
41
 
62
 
42
     def do_GET(self):
63
     def do_GET(self):
43
-        """Respond to a GET request."""
44
-        SPLIT_CHAR = "~"
45
-        SPLIT_CODE = "%7E"
46
-        EQ_CODE = "%3D"
47
-        COL_CODE = "%3A"
48
-        self.log_message("get_url: \n%s", self.path)
64
+        """Respond to a GET request"""
65
+        self.log_message("\n\n"+40*"#"+"\nget_url: \n%s", self.path)
49
         p = self.path.split("~")
66
         p = self.path.split("~")
50
-        url = urllib.unquote(p[0][1:])
67
+        #url = urllib.unquote(p[0][1:])
68
+        url = p[0][1:]
51
         url = url.replace(COL_CODE, ":")
69
         url = url.replace(COL_CODE, ":")
52
-        headers = headers2dict("""
53
-        User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A366 Safari/600.1.4
54
-        """)
70
+        headers = self.headers.dict
71
+        headers = {} # TODO
72
+        headers["host"] = urlparse.urlparse(url).hostname
55
         if len(p)>1:
73
         if len(p)>1:
56
             for h in p[1:]:
74
             for h in p[1:]:
57
-                headers[h.split("=")[0]]=urllib.unquote(h.split("=")[1])
58
-        #self.fetch_offline(self.wfile)
75
+                k = h.split("=")[0].lower()
76
+                v = urllib.unquote(h.split("=")[1])
77
+                headers[k]=v
78
+        if DEBUG:
79
+            print "url=%s"%url
80
+            print "Original request headers + url headers:"
81
+            print_headers(headers)
82
+
83
+        self.protocol_version = 'HTTP/1.1'
84
+
85
+        # TODO fetch selection
59
         try:
86
         try:
60
-            self.fetch_url2(self.wfile, url, headers)
87
+            if ".lattelecom.tv/" in url: # lattelecom.tv hack
88
+                self.fetch_ltc(self.wfile, url, headers)
89
+            elif "filmas.lv" in url or "viaplay" in url: #  HLS session/decode filmas.lv in url:
90
+                self.fetch_url2(self.wfile, url, headers)
91
+            else: # plain fetch
92
+                self.fetch_url(self.wfile, url, headers)
61
         except Exception as e:
93
         except Exception as e:
62
             print "Got Exception: ", str(e)
94
             print "Got Exception: ", str(e)
95
+            import traceback
96
+            traceback.print_exc()
97
+
98
+    ### Remote server request procedures ###
63
 
99
 
64
     def fetch_offline(self,wfile):
100
     def fetch_offline(self,wfile):
101
+        print "** Fetch offline"
65
         self.send_response(200)
102
         self.send_response(200)
66
-        self.send_header("Server", "StreamProxy")
103
+        self.send_header("Server", "playstreamproxy")
67
         self.send_header("Content-type", "video/mp4")
104
         self.send_header("Content-type", "video/mp4")
68
         self.end_headers()
105
         self.end_headers()
69
         self.wfile.write(open("offline.mp4", "rb").read())
106
         self.wfile.write(open("offline.mp4", "rb").read())
70
-        self.wfile.close()
107
+        #self.wfile.close()
108
+
109
+    def fetch_url(self,wfile,url,headers):
110
+        if DEBUG:
111
+            print "\n***********************************************************"
112
+            print "fetch_url: \n%s"%url
113
+            print "**Server request headers: "
114
+            print_headers(headers)
115
+        #if ".lattelecom.tv/" in url and EQ_CODE in url:
116
+        #    url = url.replace(EQ_CODE,"=")
117
+        r = requests.get(url,headers = headers)
118
+        code = r.status_code
119
+        if DEBUG:
120
+            print "** Server/proxy response, code = %s"%code
121
+            print_headers(r.headers)
122
+        if not code in (200,206):
123
+            print "***Error, code=%s",code
124
+            self.send_response(code)
125
+            self.send_headers(r.headers)
126
+            wfile.close()
127
+            return
128
+        self.send_response(code)
129
+        self.send_headers(r.headers)
130
+        CHUNK_SIZE = 1024*4
131
+        for chunk in r.iter_content(CHUNK_SIZE):
132
+            try:
133
+                wfile.write(chunk)
134
+            except Exception as e:
135
+                print "Exception: ", str(e)
136
+                wfile.close()
137
+                return
138
+        if DEBUG: print "**File downloaded"
139
+        wfile.close()
140
+        # time.sleep(1)
141
+        return
142
+
143
+    def fetch_ltc(self, wfile, url, headers):
144
+        if DEBUG:
145
+            print "\n***********************************************************"
146
+            print "fetch_url2: \n%s"%url
147
+        #self.log_message("fetch_filmas: \n%s", url)
148
+        #self.log_message("headers: %s", headers)
149
+        base_url = hls_base(url)
150
+        if DEBUG: print "base_url=",base_url
151
+        if base_url not in sessions:
152
+            if DEBUG: print "New session"
153
+            sessions[base_url] = {}
154
+            sessions[base_url]["session"] = requests.Session()
155
+            #sessions[base_url]["session"].headers = {}
156
+            sessions[base_url]["key"] = binascii.a2b_hex(headers["key"]) if "key" in headers and headers["key"] else None
157
+        ses = sessions[base_url]["session"]
158
+        key = sessions[base_url]["key"]
159
+        ses.headers.clear()
160
+        ses.headers.update(headers0)
161
+        ses.headers.update(headers)
162
+        ses.headers["Connection"]="Keep-Alive"
163
+        if DEBUG:
164
+            print "**Server request headers: "
165
+            print_headers(ses.headers)
166
+        for t in range(3):
167
+            r = ses.get(url, stream=True, verify=False)
168
+            code = r.status_code #r.status_code
169
+            if DEBUG:
170
+                print "\n\n=====================================\n**Server response:", code #r.status_code
171
+                print "**Server response headers: "
172
+                print_headers(r.headers)
173
+            if code in (200,2016): break
174
+        if not (code in (200,206)):
175
+            print "***Error, code=%s"%code
176
+            self.send_response(code)
177
+            self.send_headers(r.headers)
178
+            wfile.close()
179
+            #self.fetch_offline(wfile)
180
+            return
181
+
182
+        ### Start of return formin and sending
183
+        self.send_response(200)
184
+        #headers2 = del_headers(r.headers,["Content-Encoding",'Transfer-Encoding',"Connection",'content-range',"range"])
185
+        headers2  = {"server":"playstreamproxy", "content-type":"text/html"}
186
+
187
+        # Content-Type: application/vnd.apple.mpegurl (encrypted)
188
+        if r.headers["content-type"] == "application/vnd.apple.mpegurl":
189
+            content = r.content
190
+            content = r.content.replace(base_url,"")
191
+            content = re.sub("#EXT-X-KEY:METHOD=AES-128.+\n", "", content, 0, re.IGNORECASE | re.MULTILINE)
192
+            headers2["content-type"] = "application/vnd.apple.mpegurl"
193
+            headers2["content-length"] = "%s"%len(content)
194
+            r.headers["content-length"] = "%s"%len(content)
195
+            #headers2['content-range'] = 'bytes 0-%s/%s'%(len(content)-1,len(content))
196
+            #self.send_headers(headers2)
197
+            self.send_headers(r.headers)
198
+            wfile.write(content)
199
+            wfile.close()
200
+
201
+        # Content-Type: video/MP2T (encrypted)
202
+        elif r.headers["content-type"] == "video/MP2T" and key:
203
+            print "Decode video/MP2T"
204
+            content = r.content
205
+            from Crypto.Cipher import AES
206
+            iv = content[:16]
207
+            d = AES.new(key, AES.MODE_CBC, iv)
208
+            content = d.decrypt(content[16:])
209
+            headers2["content-type"] = "video/MP2T"
210
+            headers2["content-length"] = "%s"% (len(content))
211
+            #headers2['content-range'] = 'bytes 0-%s/%s' % (len(content) - 1, len(content))
212
+            print content[0:16]
213
+            print "Finish decode"
214
+            self.send_headers(headers2)
215
+            wfile.write(content)
216
+            wfile.close()
217
+
218
+        else:
219
+            print "Return regular content"
220
+            headers2["content-type"]  = r.headers["content-type"]
221
+            if "content-length" in r.headers:
222
+                headers2["content-length"] = r.headers["content-length"]
223
+            self.send_headers(r.headers)
224
+            CHUNK_SIZE = 4 * 1024
225
+            for chunk in r.iter_content(CHUNK_SIZE):
226
+                try:
227
+                    #print "#",
228
+                    wfile.write(chunk)
229
+                except Exception as e:
230
+                    print "Exception: ", str(e)
231
+                    return
232
+            if DEBUG: print "File downloaded = "
233
+            wfile.close()
234
+            #time.sleep(1)
235
+            return
71
 
236
 
72
 
237
 
73
     def fetch_url2(self, wfile, url, headers):
238
     def fetch_url2(self, wfile, url, headers):
74
-        if DEBUG: print "\n***********************************************************"
75
-        self.log_message("fetch_url: \n%s", url)
239
+        if DEBUG:
240
+            print "\n***********************************************************"
241
+            print "fetch_url2: \n%s"%url
242
+        #self.log_message("fetch_filmas: \n%s", url)
76
         #self.log_message("headers: %s", headers)
243
         #self.log_message("headers: %s", headers)
77
-
78
-        base_url = "/".join(url.split("/")[0:-1])
244
+        base_url = hls_base(url)
245
+        if DEBUG: print "base_url=",base_url
79
         if base_url not in sessions:
246
         if base_url not in sessions:
80
-            sessions[base_url]={}
247
+            if DEBUG: print "New session"
248
+            sessions[base_url] = {}
81
             sessions[base_url]["session"] = requests.Session()
249
             sessions[base_url]["session"] = requests.Session()
250
+            #sessions[base_url]["session"].headers = {}
82
             sessions[base_url]["key"] = binascii.a2b_hex(headers["key"]) if "key" in headers and headers["key"] else None
251
             sessions[base_url]["key"] = binascii.a2b_hex(headers["key"]) if "key" in headers and headers["key"] else None
83
-            #cj = cookielib.CookieJar()
84
-            #sessions[base_url] = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
85
-        else:
86
-            if "key" in headers and headers["key"]: sessions[base_url]["key"] = binascii.a2b_hex(headers["key"])
87
         ses = sessions[base_url]["session"]
252
         ses = sessions[base_url]["session"]
88
         key = sessions[base_url]["key"]
253
         key = sessions[base_url]["key"]
89
-
90
-        if DEBUG: print "**Request headers: "
254
+        ses.headers.clear()
255
+        ses.headers.update(headers0)
91
         ses.headers.update(headers)
256
         ses.headers.update(headers)
92
-        #ses.addheaders=[]
93
-        for h in ses.headers:
94
-            #ses.addheaders.append((h,headers[h]))
95
-            if DEBUG: print h,"=",ses.headers[h]
96
-        r = ses.get(url, stream=True,verify=False)
97
-        #r = ses.open(url)
98
-        code = r.status_code #r.status_code
99
-        if DEBUG: print "**Response:", code #r.status_code
100
-        if DEBUG: print "**Response headers: "
101
-        for h in r.headers:
102
-            if DEBUG: print h,"=",r.headers[h]
103
-        self.send_response(code)
104
-
105
-        print code
106
-        if code <> 200:
107
-            self.fetch_offline(wfile)
257
+        ses.headers["Connection"]="Keep-Alive"
258
+        if DEBUG:
259
+            print "**Server request headers: "
260
+            print_headers(ses.headers)
261
+        for t in range(3):
262
+            r = ses.get(url, stream=True, verify=False)
263
+            code = r.status_code #r.status_code
264
+            if DEBUG:
265
+                print "\n\n=====================================\n**Server response:", code #r.status_code
266
+                print "**Server response headers: "
267
+                print_headers(r.headers)
268
+            if code in (200,2016): break
269
+        if not (code in (200,206)):
270
+            print "***Error, code=%s"%code
271
+            self.send_response(code)
272
+            self.send_headers(r.headers)
273
+            wfile.close()
274
+            #self.fetch_offline(wfile)
108
             return
275
             return
109
 
276
 
110
-        if DEBUG: print "**Return headers:"
111
-        headers2 = {}
112
-        for h in r.headers:
113
-            if h.lower() in ("user-agent","server","transfer-encoding","content-encoding","connection"):
114
-                if DEBUG: print h," - skipped"
115
-                continue
116
-            else:
117
-                headers2[h] = r.headers[h]
118
-                if DEBUG:print h,"=",r.headers[h]
277
+        ### Start of return formin and sending
278
+        self.send_response(200)
279
+        #headers2 = del_headers(r.headers,["Content-Encoding",'Transfer-Encoding',"Connection",'content-range',"range"])
280
+        headers2  = {"server":"playstreamproxy", "content-type":"text/html"}
119
 
281
 
120
-        # Content-Type: application/vnd.apple.mpegurl
121
-        if r.headers["Content-Type"] == "application/vnd.apple.mpegurl" and key:
282
+        # Content-Type: application/vnd.apple.mpegurl (encrypted)
283
+        if r.headers["content-type"] == "application/vnd.apple.mpegurl":
122
             content = r.content
284
             content = r.content
123
-            content = r.content.replace(base_url+"/","")
285
+            content = r.content.replace(base_url,"")
124
             content = re.sub("#EXT-X-KEY:METHOD=AES-128.+\n", "", content, 0, re.IGNORECASE | re.MULTILINE)
286
             content = re.sub("#EXT-X-KEY:METHOD=AES-128.+\n", "", content, 0, re.IGNORECASE | re.MULTILINE)
125
-            headers2["Content-Length"] = "%s"%len(content)
126
-            self.send_headers(headers2)
287
+            headers2["content-type"] = "application/vnd.apple.mpegurl"
288
+            headers2["content-length"] = "%s"%len(content)
289
+            r.headers["content-length"] = "%s"%len(content)
290
+            #headers2['content-range'] = 'bytes 0-%s/%s'%(len(content)-1,len(content))
291
+            #self.send_headers(headers2)
292
+            self.send_headers(r.headers)
127
             wfile.write(content)
293
             wfile.write(content)
294
+            wfile.close()
128
 
295
 
129
-        # Content-Type: video/MP2T
130
-        elif r.headers["Content-Type"] == "video/MP2T" and key:
296
+        # Content-Type: video/MP2T (encrypted)
297
+        elif r.headers["content-type"] == "video/MP2T" and key:
298
+            print "Decode video/MP2T"
131
             content = r.content
299
             content = r.content
132
             from Crypto.Cipher import AES
300
             from Crypto.Cipher import AES
133
             iv = content[:16]
301
             iv = content[:16]
134
             d = AES.new(key, AES.MODE_CBC, iv)
302
             d = AES.new(key, AES.MODE_CBC, iv)
135
             content = d.decrypt(content[16:])
303
             content = d.decrypt(content[16:])
136
-            headers2["Content-Length"] = "%s"%len(content)
304
+            headers2["content-type"] = "video/MP2T"
305
+            headers2["content-length"] = "%s"% (len(content))
306
+            #headers2['content-range'] = 'bytes 0-%s/%s' % (len(content) - 1, len(content))
307
+            print content[0:16]
308
+            print "Finish decode"
137
             self.send_headers(headers2)
309
             self.send_headers(headers2)
138
             wfile.write(content)
310
             wfile.write(content)
311
+            wfile.close()
139
 
312
 
140
         else:
313
         else:
141
-            self.send_headers(headers2)
314
+            print "Return regular content"
315
+            headers2["content-type"]  = r.headers["content-type"]
316
+            if "content-length" in r.headers:
317
+                headers2["content-length"] = r.headers["content-length"]
318
+            self.send_headers(r.headers)
142
             CHUNK_SIZE = 4 * 1024
319
             CHUNK_SIZE = 4 * 1024
143
-            if code == 200:
144
-                #while True:
145
-                    #chunk = r.read(CHUNK_SIZE)
146
-                    #if not chunk:
147
-                        #break
148
-                    #wfile.write(chunk)
149
-                #pass
150
-                #wfile.close()
151
-                for chunk in r.iter_content(1024):
152
-                    try:
153
-                        #print "#",
154
-                        wfile.write(chunk)
155
-                    except Exception as e:
156
-                        print "Exception: ", str(e)
157
-                        return
158
-                if DEBUG: print "  = file downloaded = "
159
-                time.sleep(1)
160
-        self.wfile.close()
320
+            for chunk in r.iter_content(CHUNK_SIZE):
321
+                try:
322
+                    #print "#",
323
+                    wfile.write(chunk)
324
+                except Exception as e:
325
+                    print "Exception: ", str(e)
326
+                    return
327
+            if DEBUG: print "File downloaded = "
328
+            wfile.close()
329
+            #time.sleep(1)
330
+            return
161
 
331
 
162
     def send_headers(self,headers):
332
     def send_headers(self,headers):
333
+        #if DEBUG:
334
+            #print "**Return headers: "
335
+            #print_headers(headers)
163
         for h in headers:
336
         for h in headers:
164
             self.send_header(h, headers[h])
337
             self.send_header(h, headers[h])
165
         self.end_headers()
338
         self.end_headers()
168
 class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
341
 class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
169
     """Handle requests in a separate thread."""
342
     """Handle requests in a separate thread."""
170
 
343
 
171
-def start():
172
-    httpd = ThreadedHTTPServer((HOST_NAME, PORT_NUMBER), StreamHandler)
344
+def start(host = HOST_NAME, port = PORT_NUMBER):
345
+    httpd = ThreadedHTTPServer((host, port), StreamHandler)
173
     print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
346
     print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
174
     try:
347
     try:
175
         httpd.serve_forever()
348
         httpd.serve_forever()
307
     def run(self):
480
     def run(self):
308
         start()
481
         start()
309
 
482
 
483
+def print_headers(headers):
484
+    for h in headers:
485
+        print "%s: %s"%(h,headers[h])
486
+
487
+def del_headers(headers0,tags):
488
+    headers = headers0.copy()
489
+    for t in tags:
490
+        if t in headers:
491
+            del headers[t]
492
+        if t.lower() in headers:
493
+            del headers[t.lower()]
494
+    return headers
495
+
496
+def hls_base(url):
497
+    url2 = url.split("?")[0]
498
+    url2 = "/".join(url2.split("/")[0:-1])+ "/"
499
+    return url2
500
+
310
 if __name__ == "__main__":
501
 if __name__ == "__main__":
311
     daemon = ProxyDaemon("/var/run/playstreamproxy.pid")
502
     daemon = ProxyDaemon("/var/run/playstreamproxy.pid")
312
     if len(sys.argv) == 2:
503
     if len(sys.argv) == 2:

+ 16
- 15
readme.md View File

4
 Enigma2 plugin to to play various online streams (mostly Latvian).  
4
 Enigma2 plugin to to play various online streams (mostly Latvian).  
5
 Stream sources are in  subfolder "sources", and can be added/customized
5
 Stream sources are in  subfolder "sources", and can be added/customized
6
 Currently implemented:
6
 Currently implemented:
7
-- Main menu and manual stream definition in streams.cfg file
8
-- **replay.lsm.lv** media portal content (Latvian TV)
9
-- **skaties.lv** media portal content (MTG media portal Latvian version), other countries MTG contents are available through customizing main menu (country=xx) 
10
-- **lattelecom.tv(shortcut.lv)** media portal content
11
-- **play24.lv** media portal content
12
-- **viaplay.lv**
13
-- **cinemalive.tv**
14
-- **kinofilmnet.lv**
15
-- **dom.tv**
16
-- **BBC iPlayer**
17
-- **Euronews**
18
-- **filmon.tv** media portal content
19
-- **ustvnow.tv**
20
-- **serialguru.ru**
21
-- **filmix.net**
7
+- Main menu and manual stream definition in sources/streams.cfg file
8
+- **replay.lsm.lv** - Latvian TV live streams and archive (Latvian)
9
+- **skaties.lv** - skaties.lv streams (Latvian LNT, TV3, TV3+, TV6, TV2)
10
+- **lattelecom.tv(shortcut.lv)** - live, archive and video (Latvian, English, Russian), account required
11
+- **play24.lv** - RigaTV24 live and archive (Latvian)
12
+- **viaplay.lv** - video streams (partly working, only non-DRM - Latvian, Russian)
13
+- **LMT straume** - LMT video streams (Latvian)
14
+- **cinemalive.tv** - movies and series in Latvian (partly working)
15
+- **movieplace.lv** - movies and series in Latvian (partly working)
16
+- **dom.tv** - live and archive of Latvian and Russion TV channels, registration required
17
+- **BBC iPlayer** - BBC live and archive, UK IP address (VPN) required
18
+- **Euronews** - Euronews live and archive
19
+- **filmon.tv** - media portal content
20
+- **ustvnow.tv** - USA live TV (only SD), registration required
21
+- **filmix.net** - movies and series in Russian
22
+- **serialguru.ru** - movies and series in Russian (partly working)
22
 
23
 
23
 ---
24
 ---
24
 Copyright (c) 2016 ivars777 (ivars777@gmail.com)  
25
 Copyright (c) 2016 ivars777 (ivars777@gmail.com)  

+ 1
- 0
resolver.py View File

123
     url = "https://goo.gl/yMTzqf"
123
     url = "https://goo.gl/yMTzqf"
124
     url = "http://cdn.kapnob.ru/video/5e67c8b1ad018ffa/iframe"
124
     url = "http://cdn.kapnob.ru/video/5e67c8b1ad018ffa/iframe"
125
     url = "http://kodik.cc/video/10830/4269a802d1a9d9bdc53fe38488d53a52/720p"
125
     url = "http://kodik.cc/video/10830/4269a802d1a9d9bdc53fe38488d53a52/720p"
126
+    url = "https://www.youtube.com/embed/RUyQ_JJ6A84?rel=0&fs=1&wmode=transparent"
126
     streams = resolve(url)
127
     streams = resolve(url)
127
     if not streams:
128
     if not streams:
128
         print "No streams found"
129
         print "No streams found"

BIN
resolvers/hdgo.pyc View File


BIN
resolvers/hqqresolver.pyc View File


BIN
resolvers/kapnob.pyc View File


BIN
resolvers/kodik.pyc View File


BIN
resolvers/openload3.pyc View File


+ 7
- 6
sources/SourceBase.py View File

142
         return source,data,path,plist,clist,params,qs
142
         return source,data,path,plist,clist,params,qs
143
 
143
 
144
 def stream_type(data):
144
 def stream_type(data):
145
-    if "::" in data:
146
-        data = data.split("::")[1]
147
     data = data.lower()
145
     data = data.lower()
148
     m = re.search(r"^(\w+)://", data)
146
     m = re.search(r"^(\w+)://", data)
149
     prefix = m.group(1) if m else ""
147
     prefix = m.group(1) if m else ""
150
-    if prefix in ("http","https") and ".m3u8" in data:
151
-        return "hls"
152
-    elif prefix == "http":
153
-        return "http"
148
+    if prefix in ("http","https"):
149
+        if ".m3u8" in data:
150
+            return "hls"
151
+        elif ".mpd" in data:
152
+            return "dash"
153
+        else:
154
+            return "http"
154
     else:
155
     else:
155
         return prefix
156
         return prefix
156
 
157
 

BIN
sources/SourceBase.pyc View File


+ 1
- 1
sources/cinemalive.py View File

27
     
27
     
28
 class Source(SourceBase):
28
 class Source(SourceBase):
29
     
29
     
30
-    def __init__(self,country=""):
30
+    def __init__(self,country="",cfg_path=None):
31
         self.name = "cinemalive"
31
         self.name = "cinemalive"
32
         self.title = "cinemalive.tv"
32
         self.title = "cinemalive.tv"
33
         self.img = "picons/cinemalive.png" #"https://cinemalive.tv/assets/img/logo.png"
33
         self.img = "picons/cinemalive.png" #"https://cinemalive.tv/assets/img/logo.png"

BIN
sources/cinemalive.pyc View File


+ 10
- 4
sources/config.py View File

12
 os.path.dirname(os.path.abspath(__file__))
12
 os.path.dirname(os.path.abspath(__file__))
13
 class Source(SourceBase):
13
 class Source(SourceBase):
14
 
14
 
15
-    def __init__(self,country="lv"):
15
+    def __init__(self,country="lv",cfg_path=None):
16
         self.name = "config"
16
         self.name = "config"
17
         self.country=country
17
         self.country=country
18
         cur_directory = os.path.dirname(os.path.abspath(__file__))
18
         cur_directory = os.path.dirname(os.path.abspath(__file__))
19
-        self.streams_file = os.path.join(cur_directory,"streams.cfg")
19
+        if not cfg_path: cfg_path = cur_directory
20
+        self.streams_file = os.path.join(cfg_path,"streams.cfg")
20
         self.lists = collections.OrderedDict()
21
         self.lists = collections.OrderedDict()
21
         self.titles = {}
22
         self.titles = {}
22
         self.read_streams()
23
         self.read_streams()
40
                 self.lists[name] = []
41
                 self.lists[name] = []
41
             else:
42
             else:
42
                 if line[0] in ("#"): continue
43
                 if line[0] in ("#"): continue
43
-                items = tuple(line.strip().split("|"))
44
+                items = line.strip().split("|")
44
                 if not items[0]: continue
45
                 if not items[0]: continue
45
                 if len(items)==1:
46
                 if len(items)==1:
46
                     self.titles[name] = items[0]
47
                     self.titles[name] = items[0]
47
                 else:
48
                 else:
49
+                    if len(items) == 4:
50
+                        items[3] = items[3].replace("\\n","\n")
48
                     self.lists[name].append(items)
51
                     self.lists[name].append(items)
49
 
52
 
50
     def write_streams(self):
53
     def write_streams(self):
51
         f = open(self.streams_file,"w")
54
         f = open(self.streams_file,"w")
52
         for l in self.lists.keys():
55
         for l in self.lists.keys():
53
             f.write("[%s]\n"%l)
56
             f.write("[%s]\n"%l)
57
+            t = self.get_title(l)
58
+            if t<>l:
59
+                f.write("%s\n"%t)
54
             for item in self.lists[l]:
60
             for item in self.lists[l]:
55
-                f.write("%s|%s|%s|%s\n"%(item[0],item[1],item[2],item[3]))
61
+                f.write("%s|%s|%s|%s\n"%(item[0].replace("\n",""),item[1],item[2],item[3].replace("\n","\\n")))
56
             f.write("\n")
62
             f.write("\n")
57
         f.close()
63
         f.close()
58
 
64
 

BIN
sources/config.pyc View File


+ 3
- 2
sources/euronews.py View File

22
 
22
 
23
 class Source(SourceBase):
23
 class Source(SourceBase):
24
 
24
 
25
-    def __init__(self,language="en"):
25
+    def __init__(self,language="en",cfg_path=None):
26
         self.name = "euronews"
26
         self.name = "euronews"
27
         self.title = "Euronews"
27
         self.title = "Euronews"
28
         self.img = "http://pbs.twimg.com/profile_images/732665354242150400/tZsCnjuh_400x400.jpg"
28
         self.img = "http://pbs.twimg.com/profile_images/732665354242150400/tZsCnjuh_400x400.jpg"
34
         """)
34
         """)
35
         #self.language=language
35
         #self.language=language
36
         cur_directory = os.path.dirname(os.path.abspath(__file__))
36
         cur_directory = os.path.dirname(os.path.abspath(__file__))
37
-        self.config_file = os.path.join(cur_directory,self.name+".cfg")
37
+        if not cfg_path: cfg_path = cur_directory
38
+        self.config_file = os.path.join(cfg_path,self.name+".cfg")
38
         self.options = OrderedDict([("language","en")])
39
         self.options = OrderedDict([("language","en")])
39
         self.options_read()
40
         self.options_read()
40
         self.vid={"1": "News", "2": "European Affairs", "3": "Lifestyle", "4": "Knowledge"}
41
         self.vid={"1": "News", "2": "European Affairs", "3": "Lifestyle", "4": "Knowledge"}

BIN
sources/euronews.pyc View File


+ 10
- 5
sources/filmix.py View File

27
 
27
 
28
 class Source(SourceBase):
28
 class Source(SourceBase):
29
 
29
 
30
-    def __init__(self,country=""):
30
+    def __init__(self,country="",cfg_path=None):
31
         self.name = "filmix"
31
         self.name = "filmix"
32
         self.title = "filmix.me"
32
         self.title = "filmix.me"
33
         self.img = "http://cs5324.vk.me/g33668783/a_903fcc63.jpg"
33
         self.img = "http://cs5324.vk.me/g33668783/a_903fcc63.jpg"
104
             m = re.search('<p itemprop="description"[^>]+>([^<]+)<', r, re.DOTALL)
104
             m = re.search('<p itemprop="description"[^>]+>([^<]+)<', r, re.DOTALL)
105
             desc = desc0 =  util.unescape(m.group(1).strip()) if m else ""
105
             desc = desc0 =  util.unescape(m.group(1).strip()) if m else ""
106
             vid = plist[-1]
106
             vid = plist[-1]
107
-            js = self.get_movie_info(vid)
107
+            m = re.search(r"meta_key = \['(\w+)', '(\w+)', '(\w+)'\]", r, re.IGNORECASE)
108
+            key = m.group(3) if m else ""
109
+            js = self.get_movie_info(vid,key)
108
             translations = js["message"]["translations"]["flash"]
110
             translations = js["message"]["translations"]["flash"]
109
             for pl  in translations:
111
             for pl  in translations:
110
                 if translations[pl].startswith("http"):
112
                 if translations[pl].startswith("http"):
228
         video_link = m.group(1)
230
         video_link = m.group(1)
229
         series = True if video_link == '{video-link}' else False
231
         series = True if video_link == '{video-link}' else False
230
         vid = plist[1]
232
         vid = plist[1]
231
-        js = self.get_movie_info(vid)
233
+        m = re.search(r"meta_key = \['(\w+)', '(\w+)', '(\w+)'\]", r, re.IGNORECASE)
234
+        key = m.group(3) if m else ""
235
+        js = self.get_movie_info(vid,key)
232
         translations = js["message"]["translations"]["flash"]
236
         translations = js["message"]["translations"]["flash"]
233
         for pl in translations:
237
         for pl in translations:
234
             if translations[pl].startswith("http"):
238
             if translations[pl].startswith("http"):
282
         result = self._http_request(url,params,headers=headers)
286
         result = self._http_request(url,params,headers=headers)
283
         return result
287
         return result
284
 
288
 
285
-    def get_movie_info(self,vid):
289
+    def get_movie_info(self,vid,key=""):
286
         headers = headers2dict("""
290
         headers = headers2dict("""
287
     User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0
291
     User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0
288
     Accept: application/json, text/javascript, */*; q=0.01
292
     Accept: application/json, text/javascript, */*; q=0.01
290
     Content-Type: application/x-www-form-urlencoded; charset=UTF-8
294
     Content-Type: application/x-www-form-urlencoded; charset=UTF-8
291
     X-Requested-With: XMLHttpRequest
295
     X-Requested-With: XMLHttpRequest
292
     Referer: https://filmix.me/play/%s
296
     Referer: https://filmix.me/play/%s
297
+    Cookie: ad_win12=1;
293
     """%vid )
298
     """%vid )
294
-        post_data = {"post_id":vid}
299
+        post_data = {"post_id":vid,"key=":key}
295
         r = util.post("https://filmix.me/api/movies/player_data", data=post_data, headers = headers)
300
         r = util.post("https://filmix.me/api/movies/player_data", data=post_data, headers = headers)
296
         if not r:
301
         if not r:
297
             raise Exception("Can not get movie info")
302
             raise Exception("Can not get movie info")

BIN
sources/filmix.pyc View File


+ 1
- 1
sources/filmon.py View File

29
 
29
 
30
 class Source(SourceBase):
30
 class Source(SourceBase):
31
 
31
 
32
-    def __init__(self,country="lv"):
32
+    def __init__(self,country="lv",cfg_path=None):
33
         self.name = "filmon"
33
         self.name = "filmon"
34
         self.title = "FilmOn"
34
         self.title = "FilmOn"
35
         self.img = "http://behindthegloves.com/wp-content/uploads/2016/01/FilmOn-logo1.jpg"
35
         self.img = "http://behindthegloves.com/wp-content/uploads/2016/01/FilmOn-logo1.jpg"

BIN
sources/filmon.pyc View File


+ 3
- 2
sources/iplayer.py View File

24
 
24
 
25
 class Source(SourceBase):
25
 class Source(SourceBase):
26
 
26
 
27
-    def __init__(self):
27
+    def __init__(self,cfg_path=None):
28
         self.name = "iplayer"
28
         self.name = "iplayer"
29
         self.title = "BBC iPlayer"
29
         self.title = "BBC iPlayer"
30
         self.img = "http://www.userlogos.org/files/logos/inductiveload/BBC_iPlayer_logo.png"
30
         self.img = "http://www.userlogos.org/files/logos/inductiveload/BBC_iPlayer_logo.png"
58
             "s4cpbs":"http://www.lyngsat-logo.com/hires/ss/s4c_uk.png"
58
             "s4cpbs":"http://www.lyngsat-logo.com/hires/ss/s4c_uk.png"
59
         }
59
         }
60
         cur_directory = os.path.dirname(os.path.abspath(__file__))
60
         cur_directory = os.path.dirname(os.path.abspath(__file__))
61
-        self.config_file = os.path.join(cur_directory,self.name+".cfg")
61
+        if not cfg_path: cfg_path = cur_directory
62
+        self.config_file = os.path.join(cfg_path,self.name+".cfg")
62
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
63
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
63
         self.options_read()
64
         self.options_read()
64
 
65
 

BIN
sources/iplayer.pyc View File


+ 100
- 109
sources/lmt.py View File

9
     import json
9
     import json
10
 except:
10
 except:
11
     import simplejson as json
11
     import simplejson as json
12
-import urllib2, urllib
12
+import requests
13
 import datetime, re, sys,os
13
 import datetime, re, sys,os
14
 import ConfigParser
14
 import ConfigParser
15
 from collections import OrderedDict
15
 from collections import OrderedDict
20
 except:
20
 except:
21
     sys.path.insert(0,'..')
21
     sys.path.insert(0,'..')
22
     import util
22
     import util
23
+from YouTubeVideoUrl import YouTubeVideoUrl
23
 
24
 
24
 headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
25
 headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
25
 import HTMLParser
26
 import HTMLParser
44
 """)
45
 """)
45
         self.url = "http://straume.lmt.lv/lv/"
46
         self.url = "http://straume.lmt.lv/lv/"
46
 
47
 
47
-
48
     ######### Entry point ########
48
     ######### Entry point ########
49
     def get_content(self, data):
49
     def get_content(self, data):
50
         print "[lmt] get_content:", data
50
         print "[lmt] get_content:", data
56
             content.extend([
56
             content.extend([
57
                 ("Meklēt", "lmt::meklet?q={0}","","Meklēt"),
57
                 ("Meklēt", "lmt::meklet?q={0}","","Meklēt"),
58
                 ("Straumes", "lmt::straumes","","Tiešraides un aktuāli video"),
58
                 ("Straumes", "lmt::straumes","","Tiešraides un aktuāli video"),
59
-                ("TV", "lmt::tv","","TV tiešraides (tikai LMT tīklā)"),
59
+                #("TV", "lmt::tv","","TV tiešraides (tikai LMT tīklā)"),
60
                 ("Jaunākie", "lmt::video/jaunakie?videoPage=1", "", "Visu žanru jaunākie video"),
60
                 ("Jaunākie", "lmt::video/jaunakie?videoPage=1", "", "Visu žanru jaunākie video"),
61
                 ("Sports", "lmt::video/sports?videoPage=1", "", "Sports"),
61
                 ("Sports", "lmt::video/sports?videoPage=1", "", "Sports"),
62
                 ("Kultūra un māksla", "lmt::video/kultura?videoPage=1", "", "Kultūra un māksla"),
62
                 ("Kultūra un māksla", "lmt::video/kultura?videoPage=1", "", "Kultūra un māksla"),
63
                 ("Konferences", "lmt::video/konferences?videoPage=1", "", "Konferences"),
63
                 ("Konferences", "lmt::video/konferences?videoPage=1", "", "Konferences"),
64
                 ("Raidījumi", "lmt::video/raidijumi?videoPage=1", "", "Raidījumi"),
64
                 ("Raidījumi", "lmt::video/raidijumi?videoPage=1", "", "Raidījumi"),
65
                 ("Notikumi", "lmt::video/notikumi?videoPage=1", "", "Notikumi"),
65
                 ("Notikumi", "lmt::video/notikumi?videoPage=1", "", "Notikumi"),
66
-                ("Filmas un seriāli", "lmt::filmas?videoPage=1", "", "Filmas un seriāli"),
66
+                ("Filmas un seriāli", "lmt::video/filmas?videoPage=1", "", "Filmas un seriāli"),
67
                 ("Dažādi video", "lmt::video/dazadi?videoPage=1", "", "Dažādi video"),
67
                 ("Dažādi video", "lmt::video/dazadi?videoPage=1", "", "Dažādi video"),
68
+                ("Viedtelevīzija", "lmt::video/viedtelevizija?videoPage=1", "", "Viedtelevīzija"),
68
             ])
69
             ])
69
             return content
70
             return content
70
 
71
 
71
-        elif clist in ("meklet","video", "filmas"):
72
+        elif clist in ("meklet","video", "straumes","video-saraksts"):
72
             r=self.call(data)
73
             r=self.call(data)
73
-            result = re.findall('<a href="([^"]+)"> (.+?) </a></div>.+?> (.+?)</div>', r, re.DOTALL)
74
-            for item in result:
75
-                title = item[1].replace("<b>","").replace("</b>","")
76
-                data2 = item[0].replace("http://movieplace.lv/","")
77
-                img = self.img
78
-                desc = item[2].replace("<b>","").replace("</b>","")
79
-                content.append((title,self.name+"::"+data2,img,desc))
80
-            if '<span>&raquo;</span>' in r:
81
-                m = re.search("p=(\d+)",data)
74
+            result = re.findall('<a class="video-picture" (.+?)</li>', r, re.IGNORECASE | re.MULTILINE)
75
+            for r2 in result:
76
+                m = re.search('<a class="video-title" href="/lv/([^"]+)">([^<]+)<', r2)
77
+                title = m.group(2)
78
+                data2 = m.group(1)
79
+                m = re.search("([^ ]+) 2x", r2)
82
                 if m:
80
                 if m:
83
-                    page = int(m.group(1))+1
84
-                    data2 = re.sub(r"p=\d+", r"p=%s"%page, data)
85
-                    content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))
86
-            return content
87
-
88
-        # Filmu saraksti ##
89
-        elif clist in ["load","dir"] and len(plist)<=3:
90
-            if clist == "jaunakas":
91
-                r = self.call("")
92
-            else:
93
-                r = self.call(data)
94
-            #r = r.decode("cp1251").encode("utf8")
95
-            if clist == "load":
96
-                result = re.findall(r' <a href="/([^"]+)" alt="([^"]+)"><img src="/([^"]+)" title="([^"]+)">.+?<div class="years">([^<]+)</div>\s+<div class="country">([^<]+)</div>', r, re.DOTALL)
97
-            else:
98
-                result = re.findall(r' <a href="/([^"]+)" alt="([^"]+)"><img src="/([^"]+)" title="[^"]+">.+?<span>([^<]+)</span>\s*<div class="country">([^<]+)</div>', r, re.IGNORECASE | re.DOTALL)
99
-            for item in result:
100
-                title = item[1]+" [%s]"%item[4] if clist=="load" else item[1]+" / %s [%s]"%(item[3],item[4])
101
-                img = "http://movieplace.lv/"+item[2]
102
-                data2 = item[0]
103
-                desc = "%s\n%s"%(title,item[5]) if clist=="load" else title
81
+                    img = m.group(1)
82
+                else:
83
+                    m = re.search('<img src="([^"]+)', r2)
84
+                    img = m.group(1) if m else ""
85
+                m = re.search('<span class="playlist-overlay">([^<]+)</span>', r2)
86
+                overlay = m.group(1) if m else ""
87
+                m = re.search('<span class="badge badge-[^>]+>([^<]+)(<[^>]+>([^<]+))*</span>', r2, re.IGNORECASE)
88
+                badge = ""
89
+                if m:
90
+                    badge = m.group(1)
91
+                    if m.group(3):
92
+                        badge = badge + m.group(3)
93
+                categories = re.findall('<span class="category-title">([^<]+)</span>', r2)
94
+                categories = "".join(categories)
95
+                if overlay:
96
+                    title = "%s [%s]"%(title,overlay)
97
+                if badge:
98
+                    title = "%s [%s]"%(title,badge)
99
+                desc = title
100
+                if categories:
101
+                    desc = desc + "\n"+ categories
104
                 content.append((title,self.name+"::"+data2,img,desc))
102
                 content.append((title,self.name+"::"+data2,img,desc))
105
-            m = re.search('<[ab] class="swchItemA*1"( href="/([^"]+)" onclick="[^"]+")*><span>([^<]+)</span></[ab]> </span>', r, re.DOTALL)
103
+            m = re.search("videoPage=(\d+)",data)
106
             if m:
104
             if m:
107
-                if m.group(1):
108
-                    page = int(re.search("\d+$",data).group())
109
-                    page = page+1
110
-                    data2 = re.sub("\d$","%s"%page,data)
111
-                    content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))
105
+                page = int(m.group(1))+1
106
+                data2 = re.sub(r"videoPage=\d+", r"videoPage=%s"%page, data)
107
+                content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))
108
+            #print content
112
             return content
109
             return content
113
 
110
 
114
-        ### Seriāls ###
115
-        elif clist=="dir" and len(plist)==4:
116
-            r = self.call(path)
117
-            title0 = re.search('<h2 class="title" itemprop="name">(.+?)</h2>', r, re.DOTALL).group(1)
118
-            m = re.search(r'<span>VALODA:</span> <b><em itemprop="alternativeHeadline"><a href="[^"]*" class="entAllCats">([^<]+)</a></em></b></div>\s+?<div><span>SEZONA:</span> <b>([^<]+)</b></div>', r, re.IGNORECASE | re.DOTALL)
119
-            if m:
120
-                title0 = "%s / Season %s [%s]"%(title0,m.group(2),m.group(1))
121
-            desc0 = title0
122
-            img0 = "http://movieplace.lv" + re.search('<img src="(.+?)".+?itemprop="image">', r, re.DOTALL).group(1)
123
-            #TODO
124
-            result = re.findall(r'<summary>([^<]+)</summary><iframe src="https://openload\.co/embed/[^/]+/"', r, re.DOTALL)
125
-            i = 1
126
-            for item in result:
127
-                title = title0+" - " + item
128
-                data2 = data+"?e=%s"%i
129
-                img = img0
130
-                desc = desc0
131
-                content.append((title,self.name+"::"+data2,img,desc))
132
-                i += 1
133
-            return content
134
 
111
 
135
         ### kaut kas neparedzets ###
112
         ### kaut kas neparedzets ###
136
         else:
113
         else:
138
 
115
 
139
     def is_video(self,data):
116
     def is_video(self,data):
140
         source,data,path,plist,clist,params,qs = self.parse_data(data)
117
         source,data,path,plist,clist,params,qs = self.parse_data(data)
141
-        if clist=="dir" and len(plist) == 4 and "e"in qs: # sērija
142
-            return True
143
-        elif clist=="load" and len(plist) == 4:
118
+        if not clist in ("meklet","video", "straumes","video-saraksts","home"):
144
             return True
119
             return True
145
-        else:
146
-            return False
147
 
120
 
148
     def call(self, data,params=None,headers=None,lang=""):
121
     def call(self, data,params=None,headers=None,lang=""):
149
         if not headers: headers = self.headers
122
         if not headers: headers = self.headers
150
         url = self.url+data
123
         url = self.url+data
151
-        result = self._http_request(url,params,headers=headers)
124
+        r = requests.get(url,headers = headers)
125
+        return r.content
126
+        #result = self._http_request(url,params,headers=headers)
152
         return result
127
         return result
153
 
128
 
154
     def get_streams(self,data):
129
     def get_streams(self,data):
155
-        print "[movieplace] get_streams:", data
130
+        print "[lmt] get_streams:", data
156
         if not self.is_video(data):
131
         if not self.is_video(data):
157
             return []
132
             return []
158
         source,data,path,plist,clist,params,qs = self.parse_data(data)
133
         source,data,path,plist,clist,params,qs = self.parse_data(data)
159
         r = self.call(path)
134
         r = self.call(path)
160
-        if clist=="load":
161
-            m = re.search('<h2 class="title" itemprop="name">([^<]+)</h2>', r, re.DOTALL)
162
-            title = re.search('<itemprop="name">(.+?)</itemprop="name">', r, re.DOTALL).group(1)
163
-            m = re.search(r'<div role="tabpanel" class="tab-pane fade in active" id="heading-tab4">\s*(.+?)\s*</div>', r, re.DOTALL)
164
-            desc = m.group(1) if m else title
165
-            m = re.search('<meta property="og:image" content="([^"]+)" />', r, re.DOTALL)
166
-            img = m.group(1) if m else ""
167
-            rr = []
168
-            for m in re.finditer("(RU|ENG|LAT|LAT SUB)<BR( /)*>.*?>?<BR( /)*>.*?<iframe", r, re.IGNORECASE | re.DOTALL):
169
-                if len(rr)>0:
170
-                    rr[-1]["end"] = m.start()
171
-                rr.append({"lang":m.group(1),"start":m.start(),"end":len(r)})
172
-            streams = []
173
-            for m in re.finditer(r'src="(https*://(goo\.gl|songs2dl|kodik|cdn\.kapnob|hqq|openload|sv1.servkino|vidwatch|online\.kinozz).+?)"', r, re.IGNORECASE | re.DOTALL):
174
-                url = m.group(1)
175
-                lang = "?"
176
-                for rrr in rr:
177
-                    if m.start()>rrr["start"] and m.start()<rrr["end"]:
178
-                        lang = rrr["lang"]
179
-                        break
180
-                for s in resolver.resolve(url):
181
-                    s["name"] = title
182
-                    s["desc"] = desc
183
-                    s["img"] = img
184
-                    s["type"] = self.stream_type(s["url"])
185
-                    s["lang"] = lang
186
-                    streams.append(s)
187
-            return streams
135
+        title = re.search("<h1>(.+?)</h1", r, re.IGNORECASE).group(1)
136
+        m = re.search('<a class="category-title".+?[^>]+>([^<]+)<', r, re.IGNORECASE | re.DOTALL)
137
+        categories = m.group(1) if m else ""
138
+        m = re.search('<span class="category-title">([^<]+)</span>.+?</p>', r, re.IGNORECASE | re.DOTALL)
139
+        if m:
140
+            categories = categories + m.group(1)
141
+        if categories:
142
+            tite = "%s [%s]"%(title,categories)
143
+        img = re.search('<meta property="twitter:image" content="([^"]+)">', r, re.IGNORECASE | re.DOTALL).group(1)
144
+        desc = title + "\n" + re.search('<meta property="og:description" content="([^"]+)">', r, re.IGNORECASE | re.DOTALL).group(1)
145
+        m = re.search('file: "([^"]+)"', r, re.IGNORECASE)
146
+        if m:
147
+            data2 = m.group(1)
148
+            stream = util.item()
149
+            stream["name"] = title
150
+            stream["url"] = data2
151
+            stream["img"] = img
152
+            stream["desc"] = desc
153
+            stream["resolver"] = "lmt"
154
+            return [stream]
155
+        elif re.search('src="http*://www.youtube.com/embed/(\w+).*"',r):
156
+            m = re.search('src="http*://www.youtube.com/embed/(\w+).*"',r)
157
+            video_id = m.group(1)
158
+            #http://www.youtube.com/embed/RUyQ_JJ6A84?rel=0&fs=1&wmode=transparent
159
+            data2 = YouTubeVideoUrl().extract(video_id)
160
+            s = util.item()
161
+            s["name"] = title
162
+            s["url"] = data2
163
+            s["desc"] = desc
164
+            s["img"] = img
165
+            s["resolver"] = "lmt"
166
+            return [s]
167
+        elif 'src="http://cdn.tiesraides.lv/lmtstraume.lv/' in r:
168
+            m = re.search('src="(http://cdn\.tiesraides\.lv/[^"]+)"',r)
169
+            url = m.group(1)
170
+            # src="http://cdn.tiesraides.lv/lmtstraume.lv/live-record2-ip/40?c=614127284dcd58d8a84afcf498a3ac7a&v=1405"
171
+            r = self._http_request(url)
172
+            #http://edge-telia2.tiesraides.lv/live-record2/lmtstraume.lv.40_1/manifest.f4m
173
+            m = re.search("'(http://.+?\.m3u8)'",r)
174
+            data2 = m.group(1) if m else ""
175
+            s = util.item()
176
+            s["name"] = title
177
+            s["url"] = data2
178
+            s["desc"] = desc
179
+            s["img"] = img
180
+            s["resolver"] = "lmt"
181
+            return [s]
182
+        else:
183
+            raise Exception("No stream found")
188
 
184
 
189
 
185
 
190
-        elif clist=="dir" and "e" in qs: # serialā sērija
191
-            #TODO
192
-            result = re.findall(r'<summary>([^<]+)</summary><iframe src="([^"]+)"', r, re.DOTALL)
193
-            i = int(qs["s"])-1
194
-            url0 = result[i][1]
195
-            title = title + " - " + result[i][0]
196
-        else:
197
-            #iframe src="https://openload.co/embed/wlw6Vl9zwL0/"
198
-            result = re.findall(r'<iframe src="([^"]+)"', r, re.DOTALL)
199
-            if not result:
200
-                return []
201
-            url0 = result[0]
202
-        return streams
186
+                # streams = resolver.resolve(url)
187
+            # for s in streams:
188
+            #     s["name"] = title
189
+            #     s["desc"] = desc
190
+            #     s["img"] = img
191
+            #     streams.append(s)
192
+            # return streams
193
+
203
 
194
 
204
 if __name__ == "__main__":
195
 if __name__ == "__main__":
205
     country= "lv"
196
     country= "lv"

+ 35
- 16
sources/ltc.py View File

6
 # Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
6
 # Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
 #
7
 #
8
 import sys, os, os.path, re, sys
8
 import sys, os, os.path, re, sys
9
-import urllib,urllib2
9
+import urllib,urllib2,urlparse
10
 from xml.sax.saxutils import unescape,escape
10
 from xml.sax.saxutils import unescape,escape
11
 from urllib import quote, unquote
11
 from urllib import quote, unquote
12
 import datetime
12
 import datetime
24
 
24
 
25
 class Source(SourceBase):
25
 class Source(SourceBase):
26
 
26
 
27
-    def __init__(self):
27
+    def __init__(self,cfg_path=None):
28
         self.name = "ltc"
28
         self.name = "ltc"
29
         self.title = "Shortcut.lv (lattelecom.tv)"
29
         self.title = "Shortcut.lv (lattelecom.tv)"
30
         self.img = "picons/shortcut.png"
30
         self.img = "picons/shortcut.png"
65
         self.today2 = self.today.strftime("%d.%m.%Y")
65
         self.today2 = self.today.strftime("%d.%m.%Y")
66
 
66
 
67
         cur_directory = os.path.dirname(os.path.abspath(__file__))
67
         cur_directory = os.path.dirname(os.path.abspath(__file__))
68
-        self.config_file = os.path.join(cur_directory,self.name+".cfg")
68
+        if not cfg_path: cfg_path = cur_directory
69
+        self.config_file = os.path.join(cfg_path,self.name+".cfg")
69
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
70
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
70
         self.options_read()
71
         self.options_read()
71
 
72
 
822
         captions = sorted(captions,key=lambda item: item["order"],reverse=True)
823
         captions = sorted(captions,key=lambda item: item["order"],reverse=True)
823
         for s in streams_xml:
824
         for s in streams_xml:
824
             for server in servers:
825
             for server in servers:
826
+                stream = util.item()
825
                 server2 = self.load_balancer(server)
827
                 server2 = self.load_balancer(server)
826
                 url = "http://%s/mobile-vod/%s/playlist.m3u8?resource_id=%s&auth_token=%s"%(server2,s[0],resource_id,token)
828
                 url = "http://%s/mobile-vod/%s/playlist.m3u8?resource_id=%s&auth_token=%s"%(server2,s[0],resource_id,token)
827
-                stream = util.item()
829
+                # TODO Engima2 gstreamer vajag lai padod playlist nevis chunklist
830
+                # r3 = self._http_request(url)
831
+                # sss = re.findall(r"#EXT-X-STREAM-INF:.*?BANDWIDTH=(\d+).*?\n(.+?)$", r3, re.IGNORECASE | re.MULTILINE)
832
+                # if sss:
833
+                #     url2 = sss[0][1]
834
+                #     stream["headers"] = {"Referer":url}
835
+                #     if url2.startswith("http"):
836
+                #         url = url2
837
+                #     else:
838
+                #         url = util.hls_base(url)+url2
828
                 stream["url"]=url
839
                 stream["url"]=url
829
                 stream["lang"]=s[1]
840
                 stream["lang"]=s[1]
830
                 stream["quality"]=s[2]
841
                 stream["quality"]=s[2]
1019
 
1030
 
1020
     if len(sys.argv)>1 and  not "ltc::" in sys.argv[1]:
1031
     if len(sys.argv)>1 and  not "ltc::" in sys.argv[1]:
1021
 
1032
 
1022
-        vid = sys.argv[1]
1023
-        print "login - %s"%c.login("ivars777","xxx")
1024
-        vid = "1069"
1025
-        vid = "1462566072086"
1026
-        channelid="101"
1027
-        vid = "1350462656767"
1033
+        vid = vid2 = sys.argv[1]
1034
+        password = sys.argv[2]
1035
+        print "login - %s"%c.login("ivars777",password)
1036
+        #vid = "1069"
1037
+        #vid = "1462566072086"
1038
+        #channelid="101"
1039
+        #vid = "1350462656767"
1028
         #data = c.get_stream_url(vid,"vod")
1040
         #data = c.get_stream_url(vid,"vod")
1029
         #call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",data["stream"]])
1041
         #call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",data["stream"]])
1030
-        pass
1042
+        #pass
1031
 
1043
 
1032
-        print "login2 - %s"%c.login2("ivars777","xxx")
1044
+        print "login2 - %s"%c.login2("ivars777",password)
1033
         #vid2 = "animation/ultimate_avengers_ii"
1045
         #vid2 = "animation/ultimate_avengers_ii"
1034
         #vid2 = "animation/ice_age"
1046
         #vid2 = "animation/ice_age"
1035
-        vid2 = "tiesraide/ltv1"
1047
+        #vid2 = "tiesraide/ltv1"
1036
         #vid2 = "arhivs/1456521417815"
1048
         #vid2 = "arhivs/1456521417815"
1037
-        data = c.get_stream_url2(vid2)
1038
-        print data
1049
+        streams = c.get_stream_url2(vid2)
1050
+        stream = streams[-1]
1051
+        print
1039
         #for s in data:
1052
         #for s in data:
1040
-        call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",data["stream"]])
1053
+        #call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",stream["url"]])
1054
+        cmd = ["ffplay.exe",
1055
+               "-headers","Referer:%s"%(quote(stream["headers"]["Referer"])),
1056
+               "-headers","Connection:Keep-Alive",
1057
+               stream["url"]]
1058
+        print " ".join(cmd)
1059
+        call(cmd)
1041
         pass
1060
         pass
1042
 
1061
 
1043
 
1062
 

BIN
sources/ltc.pyc View File


+ 1
- 1
sources/movieplace.py View File

28
 
28
 
29
 class Source(SourceBase):
29
 class Source(SourceBase):
30
 
30
 
31
-    def __init__(self, country=""):
31
+    def __init__(self, country="",cfg_path=None):
32
         self.name = "movieplace"
32
         self.name = "movieplace"
33
         self.title = "MoviePlace.lv"
33
         self.title = "MoviePlace.lv"
34
         self.img = "http://movieplace.lv/images/logo.png"
34
         self.img = "http://movieplace.lv/images/logo.png"

BIN
sources/movieplace.pyc View File


+ 1
- 1
sources/mtgplay.py View File

36
 
36
 
37
 class Source(SourceBase):
37
 class Source(SourceBase):
38
     
38
     
39
-    def __init__(self,country="lv"):
39
+    def __init__(self,country="lv",cfg_path=None):
40
         self.name = "mtgplay"
40
         self.name = "mtgplay"
41
         self.title = "Skaties.lv (TV3)"
41
         self.title = "Skaties.lv (TV3)"
42
         self.img = "http://skaties.lv/touch-icon-192x192.png"
42
         self.img = "http://skaties.lv/touch-icon-192x192.png"

BIN
sources/mtgplay.pyc View File


+ 1
- 1
sources/play24.py View File

24
     
24
     
25
 class Source(SourceBase):
25
 class Source(SourceBase):
26
     
26
     
27
-    def __init__(self,country="lv"):
27
+    def __init__(self,country="lv",cfg_path=None):
28
         self.name = "play24"
28
         self.name = "play24"
29
         self.title = "Play24.lv"
29
         self.title = "Play24.lv"
30
         self.img = "http://play24.lv/images/play24-logo-black.png"
30
         self.img = "http://play24.lv/images/play24-logo-black.png"

BIN
sources/play24.pyc View File


+ 1
- 1
sources/replay.py View File

26
 
26
 
27
 class Source(SourceBase):
27
 class Source(SourceBase):
28
 
28
 
29
-    def __init__(self,country="lv"):
29
+    def __init__(self,country="lv",cfg_path=None):
30
         self.name = "replay"
30
         self.name = "replay"
31
         self.title = "Replay.lv (LTV)"
31
         self.title = "Replay.lv (LTV)"
32
         self.img = "http://replay.lsm.lv/apple-touch-icon.png"
32
         self.img = "http://replay.lsm.lv/apple-touch-icon.png"

BIN
sources/replay.pyc View File


+ 1
- 1
sources/serialguru.py View File

21
 
21
 
22
 class Source(SourceBase):
22
 class Source(SourceBase):
23
 
23
 
24
-    def __init__(self,country=""):
24
+    def __init__(self,country="",cfg_path=None):
25
         self.name = "serialguru"
25
         self.name = "serialguru"
26
         self.title = "SerialGURU.ru"
26
         self.title = "SerialGURU.ru"
27
         self.img = "http://serialguru.ru/images/xlogo_new.png.pagespeed.ic.0sre2_2OJN.png"
27
         self.img = "http://serialguru.ru/images/xlogo_new.png.pagespeed.ic.0sre2_2OJN.png"

BIN
sources/serialguru.pyc View File


+ 2
- 0
sources/streams.cfg View File

10
 Shortcut (lattelecom.tv)|ltc::home|https://kursors.lv/wp-content/uploads/2016/07/Shortcut-logo.png|lattelecom TV, arhīves un video
10
 Shortcut (lattelecom.tv)|ltc::home|https://kursors.lv/wp-content/uploads/2016/07/Shortcut-logo.png|lattelecom TV, arhīves un video
11
 Play24.lv (Riga24TV)|play24::home|http://play24.lv/images/play24-logo-black.png|play24.lv (Riga24TV)tiešraide un arhīvs
11
 Play24.lv (Riga24TV)|play24::home|http://play24.lv/images/play24-logo-black.png|play24.lv (Riga24TV)tiešraide un arhīvs
12
 viaplay.lv|viaplay::home|https://yt3.ggpht.com/-noVdjbNR-V8/AAAAAAAAAAI/AAAAAAAAAAA/yZ9XNP5urLY/s900-c-k-no-mo-rj-c0xffffff/photo.jpg|Viaplay.lv - filmas latviešu, krievu u.c. valodās
12
 viaplay.lv|viaplay::home|https://yt3.ggpht.com/-noVdjbNR-V8/AAAAAAAAAAI/AAAAAAAAAAA/yZ9XNP5urLY/s900-c-k-no-mo-rj-c0xffffff/photo.jpg|Viaplay.lv - filmas latviešu, krievu u.c. valodās
13
+LMT straume|lmt::home|http://www.lob.lv/images/logo/lmt_straume_vert_rgb.png|LMT straume - dažādi video latviesu valodā
13
 TVDom.tv|tvdom::home|https://tvdom.tv/front/assets/images/logo.png|PBK tiešraides un arhīvs
14
 TVDom.tv|tvdom::home|https://tvdom.tv/front/assets/images/logo.png|PBK tiešraides un arhīvs
14
 BBC iPlayer|iplayer::home|http://www.userlogos.org/files/logos/inductiveload/BBC_iPlayer_logo.png|BBC live streams and arhive
15
 BBC iPlayer|iplayer::home|http://www.userlogos.org/files/logos/inductiveload/BBC_iPlayer_logo.png|BBC live streams and arhive
15
 Euronews|euronews::home|http://pbs.twimg.com/profile_images/732665354242150400/tZsCnjuh_400x400.jpg|Euronews live streams and archive
16
 Euronews|euronews::home|http://pbs.twimg.com/profile_images/732665354242150400/tZsCnjuh_400x400.jpg|Euronews live streams and archive
22
 MTGPlay|config::mtg|https://www.mtg.com/wp-content/uploads/2015/11/MTG-Logo-Medium-Red-PNG.png|Other countries MTG media portals content
23
 MTGPlay|config::mtg|https://www.mtg.com/wp-content/uploads/2015/11/MTG-Logo-Medium-Red-PNG.png|Other countries MTG media portals content
23
 Filmas.lv|filmas::home|https://www.filmas.lv/wp-content/uploads/2013/06/LVfilmas-logo-jauns21.png|Filmas.lv - Latvijas filmas
24
 Filmas.lv|filmas::home|https://www.filmas.lv/wp-content/uploads/2013/06/LVfilmas-logo-jauns21.png|Filmas.lv - Latvijas filmas
24
 
25
 
26
+
25
 [my_tv]
27
 [my_tv]
26
 My Tv
28
 My Tv
27
 ..return|back|default|Atgriezties atpakaļ
29
 ..return|back|default|Atgriezties atpakaļ

+ 3
- 2
sources/tvdom.py View File

25
 
25
 
26
 class Source(SourceBase):
26
 class Source(SourceBase):
27
 
27
 
28
-    def __init__(self,country="lv"):
28
+    def __init__(self,country="lv",cfg_path=None):
29
         self.name = "tvdom"
29
         self.name = "tvdom"
30
         self.title = "TVDom.tv"
30
         self.title = "TVDom.tv"
31
         self.img = "https://tvdom.tv/front/assets/images/logo.png"
31
         self.img = "https://tvdom.tv/front/assets/images/logo.png"
37
         self.token = None
37
         self.token = None
38
 
38
 
39
         cur_directory = os.path.dirname(os.path.abspath(__file__))
39
         cur_directory = os.path.dirname(os.path.abspath(__file__))
40
-        self.config_file = os.path.join(cur_directory,self.name+".cfg")
40
+        if not cfg_path: cfg_path = cur_directory
41
+        self.config_file = os.path.join(cfg_path,self.name+".cfg")
41
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
42
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
42
         self.options_read()
43
         self.options_read()
43
 
44
 

BIN
sources/tvdom.pyc View File


+ 3
- 2
sources/ustvnow.py View File

29
 
29
 
30
 class Source(SourceBase):
30
 class Source(SourceBase):
31
 
31
 
32
-    def __init__(self,country="lv"):
32
+    def __init__(self,country="lv",cfg_path=None):
33
         self.name = "ustvnow"
33
         self.name = "ustvnow"
34
         self.title = "USTVNow"
34
         self.title = "USTVNow"
35
         self.img = "http://watch.ustvnow.com/assets/ustvnow/img/ustvnow_og_image.png"
35
         self.img = "http://watch.ustvnow.com/assets/ustvnow/img/ustvnow_og_image.png"
39
         self.country=country
39
         self.country=country
40
         self.token = ""
40
         self.token = ""
41
         cur_directory = os.path.dirname(os.path.abspath(__file__))
41
         cur_directory = os.path.dirname(os.path.abspath(__file__))
42
-        self.config_file = os.path.join(cur_directory,self.name+".cfg")
42
+        if not cfg_path: cfg_path = cur_directory
43
+        self.config_file = os.path.join(cfg_path,self.name+".cfg")
43
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
44
         self.options = OrderedDict([("user","lietotajs"),("password","parole")])
44
         self.options_read()
45
         self.options_read()
45
 
46
 

BIN
sources/ustvnow.pyc View File


+ 1
- 1
sources/viaplay.cfg View File

1
 [viaplay]
1
 [viaplay]
2
 user = ivars777@gmail.com
2
 user = ivars777@gmail.com
3
 password = kaskade7
3
 password = kaskade7
4
-device = 
4
+device = 14e06be92a07f9936749b560394669b7f32d3540-499b21d2-e6ec-4973-b57c-6db7cf43bdd0=c27f3554-13f4-40ca-a11a-6d97f832613a
5
 
5
 

+ 11
- 4
sources/viaplay.py View File

32
 
32
 
33
 class Source(SourceBase):
33
 class Source(SourceBase):
34
 
34
 
35
-    def __init__(self,language="en"):
35
+    def __init__(self,language="en",cfg_path=None):
36
         self.name = "viaplay"
36
         self.name = "viaplay"
37
         self.title = "viaplay.lv"
37
         self.title = "viaplay.lv"
38
         self.img = "https://yt3.ggpht.com/-noVdjbNR-V8/AAAAAAAAAAI/AAAAAAAAAAA/yZ9XNP5urLY/s900-c-k-no-mo-rj-c0xffffff/photo.jpg"
38
         self.img = "https://yt3.ggpht.com/-noVdjbNR-V8/AAAAAAAAAAI/AAAAAAAAAAA/yZ9XNP5urLY/s900-c-k-no-mo-rj-c0xffffff/photo.jpg"
46
         """)
46
         """)
47
         #self.language=language
47
         #self.language=language
48
         cur_directory = os.path.dirname(os.path.abspath(__file__))
48
         cur_directory = os.path.dirname(os.path.abspath(__file__))
49
-        self.config_file = os.path.join(cur_directory,self.name+".cfg")
49
+        if not cfg_path: cfg_path = cur_directory
50
+        self.config_file = os.path.join(cfg_path,self.name+".cfg")
50
         self.options = OrderedDict([("user","change_user"),("password","change_password"),("device","")])
51
         self.options = OrderedDict([("user","change_user"),("password","change_password"),("device","")])
51
         self.options_read()
52
         self.options_read()
52
         self.device = self.options["device"]
53
         self.device = self.options["device"]
91
 Upgrade-Insecure-Requests: 1
92
 Upgrade-Insecure-Requests: 1
92
 Content-Type: application/x-www-form-urlencoded
93
 Content-Type: application/x-www-form-urlencoded
93
 """)
94
 """)
94
-        url = "https://viaplay.lv/tdi/login/nav/formular?csrfToken=%s"%self.csrfToken
95
+        url = "https://viaplay.lv/tdi/login/nav/form?csrfToken=%s"%self.csrfToken
96
+        #      https://viaplay.lv/tdi/login/nav/form?_infuse=1&_ts=1490554674901&csrfToken=
95
         params = "nav_redirectUri=https%3A%2F%2Fviaplay.lv%2F&nav_email={}&nav_password={}".format(urllib.quote(user),urllib.quote(password))
97
         params = "nav_redirectUri=https%3A%2F%2Fviaplay.lv%2F&nav_email={}&nav_password={}".format(urllib.quote(user),urllib.quote(password))
98
+        #         nav_redirectUri=https%3A%2F%2Fviaplay.lv%2F&nav_email=ivars777%40gmail.com&nav_password=kaskade7&nav_remember=true
96
         headers["Cookie"] = "ott_cookies_confirmed=1; PLAY_SESSION=%s;"%self.play_session
99
         headers["Cookie"] = "ott_cookies_confirmed=1; PLAY_SESSION=%s;"%self.play_session
97
         if self.device:
100
         if self.device:
98
             headers["Cookie"] += "ott_dids=%s"%self.device
101
             headers["Cookie"] += "ott_dids=%s"%self.device
101
         if not "Set-Cookie" in r.headers:
104
         if not "Set-Cookie" in r.headers:
102
             self.play_session = None
105
             self.play_session = None
103
             return False
106
             return False
107
+        if not "ott_web_sac" in r.cookies:
108
+            self.play_session = None
109
+            return False
104
         self.ott = r.cookies["ott_web_sac"]
110
         self.ott = r.cookies["ott_web_sac"]
105
 
111
 
106
         ### Dabu iekārtas ID ###
112
         ### Dabu iekārtas ID ###
325
         streams = []
331
         streams = []
326
         url = "https://viaplay.lv/"+data
332
         url = "https://viaplay.lv/"+data
327
         r = self._http_request(url)
333
         r = self._http_request(url)
328
-        if clist=="series":
334
+        if clist in ("series","livestream"): # TODO nerāda overtime u.c.
329
             m = re.search(r'<h1 class="is-bottom-sticked is-size-h2">(.+?)</h1>.*?<h2 class="is-size-h4">.*?<p class="is-size-h6 is-strong is-bottom-sticked">(.+?)<div class="toggler-content">\s+<p>(.+?)</p>', r, re.DOTALL)
335
             m = re.search(r'<h1 class="is-bottom-sticked is-size-h2">(.+?)</h1>.*?<h2 class="is-size-h4">.*?<p class="is-size-h6 is-strong is-bottom-sticked">(.+?)<div class="toggler-content">\s+<p>(.+?)</p>', r, re.DOTALL)
330
             if not m:
336
             if not m:
331
                 raise Exception("Problem getting video information")
337
                 raise Exception("Problem getting video information")
374
             stype = "DASH" if "dash" in s["type"] else "HLS"
380
             stype = "DASH" if "dash" in s["type"] else "HLS"
375
             if "drm" in s: ###
381
             if "drm" in s: ###
376
                 # TODO, encrypted stream
382
                 # TODO, encrypted stream
383
+                raise Exception("Can not play DRM protected stream!\nOnly local and Russian content available without DRM")
377
                 continue
384
                 continue
378
             url = s["src"]
385
             url = s["src"]
379
             #urlp = util.streamproxy_encode(s["src"])
386
             #urlp = util.streamproxy_encode(s["src"])

BIN
sources/viaplay.pyc View File


+ 13
- 3
util.py View File

173
         #'!decodebin!autovideosink'
173
         #'!decodebin!autovideosink'
174
     ]
174
     ]
175
     cmd3 = ["ffplay.exe",url]
175
     cmd3 = ["ffplay.exe",url]
176
-    cmd = cmd1 if url.startswith("https") else cmd2
177
-    ret = call(cmd3)
176
+    cmd = cmd3 if url.startswith("https") else cmd2
177
+    ret = call(cmd)
178
     #if ret:
178
     #if ret:
179
         #a = raw_input("*** Error, continue")
179
         #a = raw_input("*** Error, continue")
180
     return
180
     return
205
     fname0 = re.sub("['""]","",fname0)
205
     fname0 = re.sub("['""]","",fname0)
206
     return fname0
206
     return fname0
207
 
207
 
208
+
209
+def hls_base(url):
210
+    url2 = url.split("?")[0]
211
+    url2 = "/".join(url2.split("/")[0:-1])+ "/"
212
+    return url2
213
+
208
 def stream_change(stream):
214
 def stream_change(stream):
209
     #return stream # TODO
215
     #return stream # TODO
210
     if "resolver" in stream and stream["resolver"] in ("viaplay","hqq","filmas") or \
216
     if "resolver" in stream and stream["resolver"] in ("viaplay","hqq","filmas") or \
678
     #url = "http://localhost:88/https://walterebert.com/playground/video/hls/ts/480x270.m3u8?token=xxxx~User-Agent=Enigma2~Cookie=xxxxx"
684
     #url = "http://localhost:88/https://walterebert.com/playground/video/hls/ts/480x270.m3u8?token=xxxx~User-Agent=Enigma2~Cookie=xxxxx"
679
     url = "http://hyt4d6.vkcache.com/secip/0/UMQ3q2gNjTlOPnEVm3iTiA/ODAuMjMyLjI0MC42/1479610800/hls-vod-s3/flv/api/files/videos/2015/09/11/144197748923a22.mp4.m3u8http://hyt4d6.vkcache.com/secip/0/Y-ZA1qRm8toplc0dN_L6_w/ODAuMjMyLjI0MC42/1479654000/hls-vod-s3/flv/api/files/videos/2015/09/11/144197748923a22.mp4.m3u8"
685
     url = "http://hyt4d6.vkcache.com/secip/0/UMQ3q2gNjTlOPnEVm3iTiA/ODAuMjMyLjI0MC42/1479610800/hls-vod-s3/flv/api/files/videos/2015/09/11/144197748923a22.mp4.m3u8http://hyt4d6.vkcache.com/secip/0/Y-ZA1qRm8toplc0dN_L6_w/ODAuMjMyLjI0MC42/1479654000/hls-vod-s3/flv/api/files/videos/2015/09/11/144197748923a22.mp4.m3u8"
680
     headers = {"User-Agent":"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/47.0.2526.70 Mobile/13C71 Safari/601.1.46"}
686
     headers = {"User-Agent":"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/47.0.2526.70 Mobile/13C71 Safari/601.1.46"}
687
+    url = "http://str1e.lattelecom.tv/mobile-vod/mp4:sf_fantastic_beasts_and_where_to_find_them_en_hd.mp4/playlist.m3u8?resource_id=fantastic_beasts_and_where_to_find_them&auth_token=6NAvMFDG+rYTAc4hb5JeL2bmsaRR7bAE23M6KDmhKYOGyXoo0gDpJUE9scYy+nQmfbgk03cWMe9MuXWSH1GqwolEk2jOQ/8Mrg7tOdbwrA8zM7nmkfCZPqQkwajZN4mfSJQVKHqXqJ8="
688
+    headers={}
689
+    print url
681
     urlp = streamproxy_encode(url,headers)
690
     urlp = streamproxy_encode(url,headers)
682
     print urlp
691
     print urlp
692
+    url2,headers2 = streamproxy_decode(urlp)
693
+    #print url2 - 2
683
     player(urlp)
694
     player(urlp)
684
-
685
     pass
695
     pass
686
 
696
 
687
 
697