Ivars 7 years ago
parent
commit
8d00e5b601
10 changed files with 726 additions and 445 deletions
  1. 7
    5
      addon.py
  2. 1
    1
      addon.xml
  3. 12
    1
      changelog.md
  4. 220
    25
      context_download.py
  5. 11
    9
      context_menu.py
  6. 4
    0
      deploy.bat
  7. 25
    12
      kmake.bat
  8. 444
    390
      project.wpr
  9. 1
    1
      resources/settings.xml
  10. 1
    1
      version.txt

+ 7
- 5
addon.py View File

@@ -21,6 +21,7 @@ storage_ttl = plugin.get_setting("general_ttl",int)
21 21
 use_proxy = plugin.get_setting("general_proxy_use",bool)
22 22
 proxy_url = plugin.get_setting("general_proxy_url",str)
23 23
 playlist = plugin.get_setting("general_playlist",str)
24
+download_dir = plugin.get_setting("general_download_dir",str)
24 25
 
25 26
 #storage_path = os.path.join(plugin.storage_path,"sources.p")
26 27
 if use_storage:
@@ -127,10 +128,11 @@ def get_list(data):
127 128
         is_playable = True if  sources.is_video(item[1]) else False
128 129
         img = item[2].decode("utf8") if isinstance(item[2],str) else item[2]
129 130
         desc = item[3].decode("utf8") if isinstance(item[3],str) else item[3]
131
+        #print title.encode("utf8"),data2,img
130 132
         context_menu = [
131
-            ("Add to PlayStream playlist",
132
-             u'RunScript(special://home/addons/%s/context_menu.py,"playlist","%s","%s","%s","%s")' % (
133
-                 plugin.id, title, data2, playlist, proxy_url)),
133
+            #("Add to PlayStream playlist",
134
+            # u'RunScript(special://home/addons/%s/context_menu.py,"playlist","%s","%s","%s","%s")' % (
135
+            #     plugin.id, title, data2, playlist, proxy_url)),
134 136
             ("Add to PlayStream favorites",
135 137
                 u'RunScript(special://home/addons/%s/context_menu.py,"add","%s","%s","%s","%s")'%(
136 138
                 plugin.id, title,  data2 ,img, desc)),
@@ -138,8 +140,8 @@ def get_list(data):
138 140
                 u'RunScript(special://home/addons/%s/context_menu.py,"delete","%s","%s","%s","%s")' % (
139 141
                 plugin.id, title, data2, img, desc)),
140 142
             ("Download",
141
-             u'RunScript(special://home/addons/%s/context_menu.py,"download","%s","%s","%s","%s")' % (
142
-                plugin.id, title, data2, img, desc)),
143
+             u'RunScript(special://home/addons/%s/context_download.py,"download","%s","%s","%s")' % (
144
+                plugin.id, title, data2, download_dir)),
143 145
         ]
144 146
         items.append({
145 147
             "label": title,

+ 1
- 1
addon.xml View File

@@ -1,5 +1,5 @@
1 1
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
-<addon version="0.1.44" id="plugin.video.playstream" name="PlayStream" provider-name="ivars777"  >
2
+<addon version="0.1.50" id="plugin.video.playstream" name="PlayStream" provider-name="ivars777"  >
3 3
   <requires>
4 4
     <import addon="xbmc.python" version="2.1.0"/>
5 5
     <import addon="script.module.requests" />

+ 12
- 1
changelog.md View File

@@ -1,5 +1,16 @@
1
+
2
+**0.1.50** (12.12.2017)
3
+- [change] Labojumi atbilstoši PlayStream 0.7o
4
+- [feature] Testa variants video lejupielādei
5
+
6
+**0.1.46** (14.10.2017)
7
+- [change] Labojumi atbilstoši PlayStream 0.7k
8
+
9
+**0.1.45** (05.10.2017)
10
+- [change] Salabots ltc, replay, cnemalive u.c.
11
+
1 12
 **0.1.44** (03.09.2017)
2
-- [change] Sakārtotas pikonas
13
+- [change] Sakārtotas pikonas, u.c.
3 14
 
4 15
 **0.1.43** (03.09.2017)
5 16
 - [bugfix] Salaboti video avoti atbilstoši PlayStream 0.7e (lattelekom u.c.)

+ 220
- 25
context_download.py View File

@@ -1,9 +1,11 @@
1
-import sys, os, urllib2, re
1
+import sys, os, urllib2, re, requests
2
+#CLI_MODE = True
2 3
 from kodiswift import xbmc, xbmcgui, CLI_MODE
3 4
 from kodiswift import Plugin, storage
4
-from resources.lib.content import util, ContentSources, Downloader
5
-from twisted.web import client
6
-from twisted.internet import reactor, defer
5
+from resources.lib.content import util, ContentSources
6
+#from resources.lib.content import Downloader
7
+#from twisted.web import client
8
+#from twisted.internet import reactor, defer
7 9
 
8 10
 #plugin = Plugin()
9 11
 #plugin.load_addon_settings()
@@ -21,24 +23,191 @@ download_dir = sys.argv[4]
21 23
 cur_directory = os.path.dirname(__file__)
22 24
 sources_directory = os.path.join(cur_directory,"resources","lib", "content", "sources")
23 25
 sources = ContentSources.ContentSources(sources_directory)
24
-if not sources.is_video(data):
25
-    print "It is not video link"
26
-    sys.exit(1)
27 26
 
28
-streams = sources.get_streams(data)
27
+def main():
28
+    if not sources.is_video(data):
29
+        print "It is not video link"
30
+        notify("It is not video link")
31
+        sys.exit(1)
29 32
 
30
-if not CLI_MODE:
31
-    ret = xbmcgui.Dialog().select("Select stream",streams)
32
-else:
33
-    ret = 0
34
-stream = streams[ret]
35
-#output =  stream["name"].replace("\\"," ").replace(":"," ").replace("|"," ")
36
-output = re.sub("[\\/\n\r\t,:\?\|'~\.]","_",title)
33
+    streams = sources.get_streams(data)
37 34
 
38
-for sub in stream["subs"]:
39
-    suburl = sub["url"]
40
-    slang = "_" + sub["lang"] if sub["lang"] else ""
35
+    if not CLI_MODE:
36
+        ret = 0
37
+        #ret = xbmcgui.Dialog().select("Select stream",streams) # TODO
38
+    else:
39
+        ret = 0
40
+    stream = streams[ret]
41
+    #output =  stream["name"].replace("\\"," ").replace(":"," ").replace("|"," ")
42
+    output = re.sub("[\\/\n\r\t,:\?\|'~\.]","_",title)
43
+    if isinstance(output, str):
44
+        output = output.decode("utf8")
45
+
46
+    for sub in stream["subs"]:
47
+        suburl = sub["url"]
48
+        slang = "_" + sub["lang"] if sub["lang"] else ""
49
+        download_sub(suburl, output+slang)
50
+
51
+    if "nfo" in stream and stream["nfo"]:
52
+        nfofile = os.path.join(download_dir, output+".nfo")
53
+        with open(nfofile,"w") as f:
54
+            nfo_txt = util.nfo2xml(stream["nfo"])
55
+            f.write(nfo_txt)
56
+
57
+    download_video(stream["url"], os.path.join(download_dir, output), stream["headers"])
58
+
59
+    #d = Downloader.download_video(stream["url"], os.path.join(download_dir, output), stream["headers"])
60
+    #reactor.run()
61
+
62
+
63
+    #xbmcgui.Dialog().ok("Info","Start download")
64
+
65
+    #mode = "a" if os.path.exists("context_menu.log") else "w"
66
+    #with open("context_menu.log", mode) as f:
67
+    #    f.write("%s %s %s %s", sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
68
+
69
+
70
+def download_video(url,output,headers=None):
71
+    #output = stream["name"].replace("\\"," ").replace(":"," ").replace("|"," ")
72
+    if not headers:
73
+        headers = {"user-agent":"Enigma2"}
74
+    try:
75
+        h = get_header(url,headers=headers)
76
+        mtype = h.get("content-type")
77
+        ext,stream_type = get_ext(mtype)
78
+    except Exception as e:
79
+        ext,stream_type = (".ts","hls")
80
+    output = output+ext
81
+    if stream_type == "hls":
82
+        download_hls(url, output, headers=headers)
83
+    else:
84
+        download_file(url, output, headers=headers)
85
+
86
+
87
+def download_hls(url, title, download_dir="", headers=None, overwrite=True, limit=None):
88
+    UA = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0"
89
+    if not headers:
90
+        headers = {"User-Agent" : UA}
91
+    key = headers["key"] if "key" in headers else ""
92
+#    if not "User-Agent" in headers:
93
+#        headers["User-Agent"] = UA
94
+    tsname = os.path.join(download_dir,title)
95
+
96
+    notify("Download started - %s" % title)
97
+    print "Start download"
98
+    print url
99
+    try:
100
+        r = requests.get(url,headers=headers)
101
+    except Exception as e:
102
+        raise Exception("Cannot open manifsest file - %s"%url)
103
+    if not r.content.startswith("#EXTM3U"):
104
+        raise Exception("Not valid manifest file - %s" % url)
105
+    streams = re.findall(r"#EXT-X-STREAM-INF:.*?BANDWIDTH=(\d+).*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
106
+    i = 0
107
+    while streams:
108
+        if i > 4: break
109
+        sorted(streams, key=lambda item: int(item[0]), reverse=True)
110
+        base_url = "/".join(url.split("?")[0].split("/")[:-1])+"/"
111
+        url = streams[0][1]
112
+        if not url.startswith("http"):
113
+            url = base_url + url
114
+        print url
115
+        try:
116
+            r = requests.get(url, headers=headers)
117
+        except Exception as e:
118
+            raise Exception("Cannot open manifsest file - %s"%url)
119
+        i += 1
120
+        streams = re.findall(r"#EXT-X-STREAM-INF:.*?BANDWIDTH=(\d+).*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
121
+    ts_list = re.findall(r"#EXTINF:([\d\.]+),.*?\n(.+?)$", r.content, re.IGNORECASE | re.MULTILINE)
122
+    base_url = "/".join(url.split("/")[:-1])+"/"
41 123
 
124
+    if not len(ts_list):
125
+        raise Exception("Cannot read fragment list in  manifsest file - %s"%url)
126
+
127
+    ts_num = 0
128
+    type = "vod" if "#EXT-X-ENDLIST" in r.content else "live"
129
+    currentbytes = 0.0
130
+    totalbytes = -1
131
+    currenttime = 0.0
132
+    totaltime = sum(map(float,zip(*ts_list)[0]))
133
+    #ts_file = open(outputfile, "wb")
134
+
135
+    if isinstance(tsname, str):
136
+        tsname = tsname.decode("utf8")
137
+    tsfile = open(tsname,"wb")
138
+    for ts in ts_list:
139
+        url2 = ts[1]
140
+        #print "Downloading ", url2
141
+        #fname = os.path.join(download_dir,url2.split("/")[-1])
142
+        if not url2.startswith("http"):
143
+            url2 = base_url + url2
144
+
145
+        r = requests.get(url2, headers=headers, verify=False)
146
+        content = r.content
147
+        if key:
148
+            from Crypto.Cipher import AES
149
+            key2 = binascii.a2b_hex(key)
150
+            iv = content[:16]
151
+            d = AES.new(key2, AES.MODE_CBC, iv)
152
+            content = d.decrypt(content[16:])
153
+        #with open(fname,"wb") as f:
154
+            #f.write(content)
155
+        tsfile.write(content)
156
+
157
+        content_length = len(content)
158
+        currentbytes += content_length
159
+        currenttime += float(ts_list[ts_num][0])
160
+        totalbytes = currentbytes * totaltime / currenttime
161
+        ts_num += 1
162
+        #print "Fragment %s downloaded (%s)"%(self.ts_num,len(content))
163
+        progress = float(currentbytes)/float(totalbytes)*100
164
+        print "%.1f%% (%i/%i)"%(progress,currentbytes,totalbytes)
165
+
166
+        if type == "vod":
167
+            if ts_num >= len(ts_list) or (limit and currenttime>limit):
168
+                break
169
+        else:
170
+            if limit and currenttime>limit: # TODO
171
+                break
172
+
173
+    print "Finished"
174
+    notify("Download finished - %s" % title)
175
+
176
+    tsfile.close()
177
+
178
+
179
+def download_file(url, title, download_dir="", headers=None, overwrite=True, limit=None):
180
+    UA = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0"
181
+    if not headers:
182
+        headers = {"User-Agent" : UA}
183
+    key = headers["key"] if "key" in headers else ""
184
+#    if not "User-Agent" in headers:
185
+#        headers["User-Agent"] = UA
186
+    fname = os.path.join(download_dir,title)
187
+    if isinstance(fname, str):
188
+        fname = fname.decode("utf8")
189
+
190
+    notify("Download started - %s" % title)
191
+    print "Start download"
192
+    print url
193
+    try:
194
+        r = requests.get(url,headers=headers, stream=True)
195
+    except Exception as e:
196
+        raise Exception("Cannot open url - %s"%url)
197
+    currentbytes = 0.0
198
+    totalbytes = int(r.headers["content-length"])
199
+
200
+    with open(fname, 'wb') as fd:
201
+        for chunk in r.iter_content(chunk_size=1024*1024):
202
+            fd.write(chunk)
203
+            currentbytes += len(chunk)
204
+            progress = float(currentbytes)/float(totalbytes)*100
205
+            print "%.1f%% (%i/%i)"%(progress,currentbytes,totalbytes)
206
+
207
+    print "Finished"
208
+    notify("Download finished - %s" % title)
209
+
210
+def download_sub(suburl, output):
42 211
     try:
43 212
         subs = urllib2.urlopen(suburl).read()
44 213
     except:
@@ -54,21 +223,47 @@ for sub in stream["subs"]:
54 223
         else:
55 224
             subext = ""
56 225
         if subext:
57
-            subfile = cunicode(os.path.join(download_dir, output+slang+subext))
226
+            subfile = cunicode(os.path.join(download_dir, output+subext))
58 227
             with open(subfile,"w") as f:
59 228
                 f.write(subs)
60 229
     else:
61 230
         print "\n Error downloading subtitle %s"%suburl
62 231
 
63
-d = Downloader.download_video(stream["url"], os.path.join(download_dir, output), stream["headers"])
64
-reactor.run()
65 232
 
66 233
 
234
+def get_header(url,headers=None):
235
+    r = requests.head(url,headers=headers)
236
+    return r.headers
67 237
 
238
+def get_ext(mtype):
239
+    stype = "http"
240
+    if mtype in ("vnd.apple.mpegURL","application/x-mpegURL",'application/x-mpegurl',"application/vnd.apple.mpegurl"):
241
+        return ".ts","hls"
242
+    elif mtype in ("application/dash+xml"):
243
+        return ".ts","dash" # TODO dash stream type  could be different !
244
+    elif mtype in ("video/mp4"):
245
+        return ".mp4","http"
246
+    elif mtype in ("video/MP2T","video/mp2t"):
247
+        return ".ts","http"
248
+    elif mtype in ("video/x-flv"):
249
+        return ".flv","http"
250
+    elif mtype in ("video/quicktime"):
251
+        return ".mov","http"
252
+    elif mtype in ("video/x-msvideo"):
253
+        return ".avi","http"
254
+    elif mtype in ("video/x-ms-wmv"):
255
+        return ".wmv","http"
256
+    elif mtype in ("video/x-matroska"):
257
+        return ".mkv","http"
258
+    else:
259
+        return ".mp4","http"
68 260
 
69 261
 
70
-#xbmcgui.Dialog().ok("Info","Start download")
262
+def notify(text, title="Info", time=10000):
263
+    if isinstance(text, unicode):
264
+        text = text.encode("utf8")
265
+    #xbmc.executebuiltin('Notification(Hello World,This is a simple example of notifications,5000,/script.hellow.world.png)')
266
+    xbmc.executebuiltin('Notification(%s, %s, %d, %s)'%("Info", text, time, xbmcgui.NOTIFICATION_INFO))
71 267
 
72
-#mode = "a" if os.path.exists("context_menu.log") else "w"
73
-#with open("context_menu.log", mode) as f:
74
-#    f.write("%s %s %s %s", sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
268
+if __name__ == '__main__':
269
+    main()

+ 11
- 9
context_menu.py View File

@@ -1,8 +1,7 @@
1 1
 import sys, os, os.path
2 2
 from kodiswift import xbmc, xbmcgui, CLI_MODE
3 3
 from kodiswift import Plugin, storage
4
-from  resources.lib.sources.config import Source
5
-from resources.lib import util
4
+from resources.lib.content import util
6 5
 
7 6
 
8 7
 #plugin = Plugin()
@@ -10,16 +9,19 @@ from resources.lib import util
10 9
 #playlist = plugin.get_setting("general_playlist",str)
11 10
 #proxy_url = plugin.get_setting("general_proxy_url",str)
12 11
 
13
-# PlaysTream streams.cfg
14
-cfg = Source()
15
-lists = cfg.get_lists()
16
-titles = [cfg.get_title(name) for name in lists]
17 12
 cmd = sys.argv[1]
18 13
 #title = sys.argv[2]
19 14
 #data = sys.argv[3]
20 15
 #img = sys.argv[4]
21 16
 #desc = sys.argv[5]
22 17
 
18
+if cmd in ("add", "delete"):
19
+    from resources.lib.content.sources.config import Source
20
+    cfg = Source()
21
+    lists = cfg.get_lists()
22
+    titles = [cfg.get_title(name) for name in lists]
23
+
24
+
23 25
 if cmd == "add":
24 26
     if not CLI_MODE:
25 27
         ret = xbmcgui.Dialog().select("Select menu",titles)
@@ -28,6 +30,9 @@ if cmd == "add":
28 30
     cfg.add_item(lists[ret],sys.argv[2:])
29 31
     cfg.write_streams()
30 32
 
33
+elif cmd == "delete":
34
+    xbmcgui.Dialog().ok("Info","Not yet implemented!")
35
+
31 36
 elif cmd == "playlist":
32 37
     title = sys.argv[2]
33 38
     data = sys.argv[3]
@@ -47,9 +52,6 @@ elif cmd == "playlist":
47 52
     pl.close()
48 53
     #plugin.notify("Item '%s' added to %s"%(title,playlist), "Info", 10000, xbmcgui.NOTIFICATION_INFO)
49 54
 
50
-elif cmd == "delete":
51
-    xbmcgui.Dialog().ok("Info","Not yet implemented!")
52
-
53 55
 elif cmd == "download":
54 56
     xbmcgui.Dialog().ok("Info","Not yet implemented!")
55 57
 

+ 4
- 0
deploy.bat View File

@@ -11,6 +11,7 @@ changelog.md
11 11
 addon.xml
12 12
 addon.py
13 13
 context_menu.py
14
+context_download.py
14 15
 service.py
15 16
 icon.png
16 17
 kodiswift\*.py
@@ -23,6 +24,7 @@ resources\lib\content\__init__.py
23 24
 resources\lib\content\ContentSources.py
24 25
 resources\lib\content\playstreamproxy.py
25 26
 resources\lib\content\util.py
27
+resources\lib\content\run.py
26 28
 resources\lib\content\resolver.py
27 29
 resources\lib\content\demjson.py
28 30
 resources\lib\content\ordereddict.py
@@ -60,6 +62,8 @@ resources\lib\content\resolvers\youtuberesolver.py
60 62
 resources\picons\*
61 63
 ) do echo f | xcopy /y   %%f %TARGET%%%f
62 64
 
65
+xcopy /y /q resources\lib\content\picons\* %TARGET%resources\picons\
66
+
63 67
 rem xcopy /y /d addon_data\settings.xml "C:\Users\user\AppData\Roaming\Kodi\userdata\addon_data\plugin.video.playstream\settings.xml"
64 68
 
65 69
 pause

+ 25
- 12
kmake.bat View File

@@ -1,9 +1,16 @@
1
-@echo on
2
-:=== Parameters ===
1
+@echo off
2
+
3
+:--- Pull content submodule ---
4
+pushd resources\lib\content
5
+git checkout .
6
+git pull
7
+popd
3 8
 
9
+:=== Parameters ===
4 10
 python get_version.py addon.xml >version.txt
5 11
 set /p ver=<version.txt
6 12
 echo %ver%
13
+pause
7 14
 
8 15
 set prog=PlayStream
9 16
 set pack_name=plugin.video.playstream
@@ -32,6 +39,7 @@ changelog.md
32 39
 addon.xml
33 40
 addon.py
34 41
 context_menu.py
42
+context_download.py
35 43
 service.py
36 44
 icon.png
37 45
 kodiswift\*.py
@@ -46,6 +54,7 @@ resources\lib\content\playstreamproxy.py
46 54
 resources\lib\content\Downloader.py
47 55
 resources\lib\content\resolver.py
48 56
 resources\lib\content\util.py
57
+resources\lib\content\run.py
49 58
 resources\lib\content\demjson.py
50 59
 resources\lib\content\ordereddict.py
51 60
 resources\lib\content\sources\__init__.py
@@ -78,8 +87,12 @@ resources\lib\content\resolvers\openload3.py
78 87
 resources\lib\content\resolvers\hdgo.py
79 88
 resources\lib\content\resolvers\kapnob.py
80 89
 resources\lib\content\resolvers\kodik.py
90
+resources\lib\content\resolvers\cloudsany.py
81 91
 resources\lib\content\resolvers\youtuberesolver.py
82 92
 ) do echo f| xcopy %%f %pack_name%\%%f
93
+
94
+xcopy /y /q resources\lib\content\picons\* %pack_name%\resources\picons\
95
+
83 96
 pause
84 97
 
85 98
 if exist  %release_dir%%pack_name%-%ver%.zip rm %release_dir%%pack_name%-%ver%.zip
@@ -90,16 +103,16 @@ copy  %release_dir%%pack_name%-%ver%.zip  %repo_dir%%pack_name%\%pack_name%-%ver
90 103
 python -c "import hashlib; print hashlib.md5(open(r'%repo_dir%%pack_name%\%pack_name%-%ver%.zip','r').read()).hexdigest()" >%repo_dir%%pack_name%\%pack_name%-%ver%.zip.md5
91 104
 
92 105
 git add %release_dir%%pack_name%-%ver%.zip
93
-if not ()==(%1%) (
94
-git commit -m %ver%
95
-git tag %ver%
96
-git push
97
-)
98 106
 
107
+if not ()==(%1%) (
108
+    git commit -m %ver%
109
+    git tag -d "%ver%"
110
+    git tag %ver%
111
+    git push
99 112
 
100
-pushd  %repo_dir%..
101
-call update_repo.bat
102
-xcopy /s /y repo %feed_dir%
103
-popd
113
+    pushd  %repo_dir%..
114
+    call update_repo.bat
115
+    xcopy /s /y repo %feed_dir%
116
+    popd
117
+)
104 118
 pause
105
-

+ 444
- 390
project.wpr
File diff suppressed because it is too large
View File


+ 1
- 1
resources/settings.xml View File

@@ -3,7 +3,7 @@
3 3
 
4 4
     <category label="40000">
5 5
         <setting id="general_use_storage" label="50010" type="bool" default="false" />
6
-        <setting id="general_ttl" label="50011" type="int" default="60" />
6
+        <setting id="general_ttl" label="50011" type="number" default="60" />
7 7
         <setting id="general_download_dir" label="50012" type="folder" default="download" />
8 8
         <setting id="general_playlist" label="50016" type="file" default="playstream.m3u" />
9 9
         <setting id="general_proxy" label="50013" type="bool" default="false" />

+ 1
- 1
version.txt View File

@@ -1 +1 @@
1
-0.1.44
1
+0.1.50