Ivars 7 years ago
parent
commit
2667700540
2 changed files with 303 additions and 8 deletions
  1. 10
    8
      ContentSources.py
  2. 293
    0
      Downloader.py

+ 10
- 8
ContentSources.py View File

@@ -165,15 +165,11 @@ class ContentSources(object):
165 165
         else:
166 166
             return None
167 167
 
168
-if __name__ == "__main__":
169 168
 
170
-    show_hidden = False
171
-    sources = ContentSources("sources")
172
-    if len(sys.argv)>1:
173
-        data= sys.argv[1]
174
-    else:
175
-        data = "config::home"
176 169
 
170
+def run(data="config::home"):
171
+
172
+    sources = ContentSources("sources")
177 173
     #options = sources.options_read("ltc")
178 174
     #print options
179 175
     history = []
@@ -259,4 +255,10 @@ if __name__ == "__main__":
259 255
             traceback.print_exc()
260 256
             raw_input("Continue?")
261 257
 
262
-
258
+if __name__ == "__main__":
259
+    show_hidden = False
260
+    if len(sys.argv)>1:
261
+        data= sys.argv[1]
262
+    else:
263
+        data = "config::home"
264
+    run(data)

+ 293
- 0
Downloader.py View File

@@ -0,0 +1,293 @@
1
+#from boxbranding import getMachineBrand, getMachineName
2
+import sys,os, os.path, re
3
+import urlparse, requests
4
+from twisted.web import client
5
+from twisted.internet import reactor, defer, ssl
6
+
7
+USER_AGENT = "Enigma2 HbbTV/1.1.1 (+PVR+RTSP+DL;OpenATV;;;)"
8
+
9
+#####################################################################################################
10
+class HTTPProgressDownloader(client.HTTPDownloader):
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_video(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
+