Browse Source

lmt.lv u.c.

Ivars 5 years ago
parent
commit
23c5c37028
12 changed files with 1092 additions and 851 deletions
  1. 31
    30
      addon.py
  2. 4
    2
      addon.xml
  3. 30
    0
      changelog.md
  4. 62
    23
      context_download.py
  5. 1
    1
      get_version.py
  6. 44
    42
      kmake.bat
  7. 753
    633
      project.wpr
  8. 13
    3
      resources/language/English/strings.xml
  9. 1
    1
      resources/lib/content
  10. 17
    4
      resources/settings.xml
  11. 9
    2
      service.py
  12. 127
    110
      wingdbstub.py

+ 31
- 30
addon.py View File

@@ -1,15 +1,15 @@
1 1
 # -*- coding: utf-8 -*-
2
+
2 3
 try:
3 4
     import wingdbstub
5
+    #import ptvsd
6
+    #ptvsd.enable_attach("debug", address = ('127.0.0.1', 5678))
7
+    #ptvsd.wait_for_attach()
8
+    print "[playstream] Remote debug started"
4 9
 except:
10
+    print "[playstream] Remote debug skipped"
5 11
     pass
6
-#try:
7
-#    import ptvsd
8
-#    ptvsd.enable_attach("debug")
9
-#    ptvsd.wait_for_attach()
10
-#except:
11
-#    pass
12
-import os,os.path,sys, glob, shutil, re
12
+import os, os.path,sys, glob, shutil, re
13 13
 import urllib, traceback
14 14
 try:
15 15
     import cPickle as pickle
@@ -33,6 +33,7 @@ use_storage = plugin.get_setting("general_use_storage",bool) # TODO vajag noček
33 33
 storage_ttl = plugin.get_setting("general_ttl",int)
34 34
 use_proxy = plugin.get_setting("general_proxy_use",bool)
35 35
 proxy_url = plugin.get_setting("general_proxy_url",str)
36
+proxy_remote_key = plugin.get_setting("general_proxy_remote_key",str)
36 37
 playlist = plugin.get_setting("general_playlist",str)
37 38
 download_dir = plugin.get_setting("general_download_dir",str)
38 39
 view_mode = plugin.get_setting("general_view_mode",str)
@@ -41,7 +42,6 @@ streams_file_remote = plugin.get_setting("general_streams_file_remote",str)
41 42
 use_streams_file_remote = plugin.get_setting("general_use_streams_file_remote",bool)
42 43
 download_overwrite = plugin.get_setting("general_download_overwrite",bool)
43 44
 
44
-
45 45
 #storage_path = os.path.join(plugin.storage_path,"sources.p")
46 46
 if use_storage:
47 47
     try:
@@ -74,19 +74,17 @@ if use_storage and storage is not None and "sources" in storage:
74 74
 else:
75 75
     print "[playstream] Create sources objects"
76 76
     if use_streams_file_remote:
77
-        try:
78
-            sources = ContentSources.ContentSources(sources_directory, streams_file_remote)
79
-        except Exception as e:
80
-            try:
81
-                sources = ContentSources.ContentSources(sources_directory, streams_file)
82
-                plugin.notify("Remote streams file is not available, fallback to local")
83
-            except Exception as e:
84
-                plugin.notify(e.message)
77
+        cfg_file = streams_file_remote
78
+        cfg_file2 = streams_file
85 79
     else:
86
-        try:
87
-            sources = ContentSources.ContentSources(sources_directory, streams_file)
88
-        except Exception as e:
89
-            plugin.notify(e.message)
80
+        cfg_file = streams_file
81
+        cfg_file2 = None
82
+    try:
83
+        sources = ContentSources.ContentSources(sources_directory, cfg_file, cfg_file2)
84
+    except Exception as e:
85
+        xbmc.executebuiltin("Notification(Error,%s" % e.message)
86
+        xbmc.executebuiltin("XBMC.ActivateWindow(Home)")
87
+    #plugin.notify("Using streams file '%s'" % sources.plugins["config"].streams_file)
90 88
 
91 89
 for source in sources.plugins:
92 90
     if not ("options" in dir(sources.plugins[source]) and sources.plugins[source].options): continue
@@ -100,7 +98,6 @@ for source in sources.plugins:
100 98
     sources.plugins[source].options_write(options)
101 99
 
102 100
 
103
-
104 101
 @plugin.route(".+" )
105 102
 def main():
106 103
     global prefix
@@ -229,8 +226,9 @@ def play_video(streams):
229 226
     if len(streams)>1:
230 227
         slist = []
231 228
         for s in streams:
232
-            slist.append("%s [%s,%s]"%(s["name"],s["quality"],s["lang"]))
233
-        res = xbmcgui.Dialog().select("Select stream",slist, useDetails=True) if not CLI_MODE else 0
229
+            name2 = "%s,%s" % (s["quality"],s["lang"]) if s["lang"] else s["quality"]
230
+            slist.append("[%s] %s"%(name2, s["name"]))
231
+        res = xbmcgui.Dialog().select("Select stream",slist) if not CLI_MODE else 0
234 232
         #res = xbmcgui.Dialog().contextmenu(slist) if not CLI_MODE else 0
235 233
         stream = streams[res]
236 234
     else:
@@ -238,12 +236,15 @@ def play_video(streams):
238 236
     subfiles = []
239 237
     #stream = util.stream_chamge(stream)
240 238
     if use_proxy:
241
-        if "resolver" in stream and stream["resolver"] in ("hqq","filmas") or \
242
-            "surl" in stream and re.search(r"http*://(hqq|goo\.gl)",stream["surl"]) or \
243
-            "lattelecom.tv/mobile-vod/" in stream["url"]: # TODO
244
-            #re.search(r"http*://.+?lattelecom\.tv/.+?auth_token=[^=]+=", stream["url"]):
245
-            stream["url"] = util.streamproxy_encode(stream["url"],stream["headers"],proxy_url)
246
-            stream["headers"] = {}
239
+        # if "resolver" in stream and stream["resolver"] in ("hqq","filmas") or \
240
+        #    "surl" in stream and re.search(r"http*://(hqq|goo\.gl)",stream["surl"]) or \
241
+        #    "lattelecom.tv/mobile-vod/" in stream["url"]: # TODO
242
+        3    #re.search(r"http*://.+?lattelecom\.tv/.+?auth_token=[^=]+=", stream["url"]):
243
+        #    stream["url"] = util.streamproxy_encode(stream["url"],stream["headers"],proxy_url)
244
+        #    stream["headers"] = {}
245
+        qs = {"key": proxy_remote_key} if proxy_remote_key else None
246
+        stream["url"] = util.streamproxy_encode3("playstream", stream["url"], proxy_url, stream["headers"] )
247
+        stream["headers"] = None
247 248
     if stream["headers"]:
248 249
         hh = []
249 250
         for k in stream["headers"]:
@@ -303,7 +304,7 @@ def get_view_mode(vm):
303 304
             "Banner": 501,
304 305
             "FanArt": 502
305 306
         },
306
-            "skin.estuary.isl": {
307
+        "skin.estuary.isl": {
307 308
             "None": None,
308 309
             "List": 50,
309 310
             "Poster": 51,

+ 4
- 2
addon.xml View File

@@ -1,10 +1,12 @@
1 1
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
-<addon version="0.1.83" id="plugin.video.playstream" name="PlayStream" provider-name="ivars777"  >
2
+<addon version="0.2.15" 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" />
6 6
     <import addon="script.module.simplejson" />
7
-    <import addon="script.module.pycryptodome" optional="true"/>
7
+    <import addon="script.module.pytz" />
8
+    <!-- <import addon="script.module.twisted" optional="true"/> -->
9
+    pytz
8 10
   </requires>
9 11
   <extension point="xbmc.python.pluginsource" library="addon.py">
10 12
     <provides>video</provides>

+ 30
- 0
changelog.md View File

@@ -1,3 +1,33 @@
1
+**0.2.15** (06.10.2019):
2
+- [fix] salabots lmt.lv (Facebook video)
3
+- [fix] content autonomā git projektā
4
+
5
+**0.2.13** (24.02.2019):
6
+- [feature] playstreamproxy nodrošina m3u8
7
+- [fix] salabots TVDom
8
+
9
+**0.1.98** (23.12.2018):
10
+- [fix] salabots filmix, tvplay
11
+
12
+**0.1.97** (05.12.2018):
13
+- [fix] pielabots ltc arhīvs (bildes, kārtība nfo)
14
+- [fix] download pielabots
15
+
16
+**0.1.96** (02.11.2018):
17
+- [fix] papildināts lmt (delfi video)
18
+
19
+**0.1.94** (12.10   .2018):
20
+- [fix] salabots replay
21
+
22
+**0.1.94** (04.09.2018):
23
+- [fix] salabots TVPlay (raidījumu kategorijas)
24
+
25
+**0.1.93** (04.09.2018):
26
+- [feature] pievienota iespēja spēlēt strīmus no enigma2 satelītuztvērēja
27
+
28
+**0.1.91** (21.08.2018):
29
+- [feature] pievienots jaunais tvplay (nepareiza mime-type dēļ nerādās bildes)
30
+
1 31
 **0.1.77** (28.06.2018):
2 32
 - [change] salabots, lai strādā ar Kodi 18 (alpha2)
3 33
 

+ 62
- 23
context_download.py View File

@@ -18,9 +18,20 @@ import traceback
18 18
 #from twisted.internet import reactor, defer
19 19
 
20 20
 plugin = Plugin(addon_id="plugin.video.playstream")
21
+prefix = "plugin://%s/" % plugin.id
21 22
 #plugin.load_addon_settings()
22 23
 use_storage = plugin.get_setting("general_use_storage",bool) # TODO vajag nočekot vai nav labāk lietot pickle
24
+storage_ttl = plugin.get_setting("general_ttl",int)
25
+use_proxy = plugin.get_setting("general_proxy_use",bool)
26
+proxy_url = plugin.get_setting("general_proxy_url",str)
27
+playlist = plugin.get_setting("general_playlist",str)
28
+#download_dir = plugin.get_setting("general_download_dir",str)
29
+view_mode = plugin.get_setting("general_view_mode",str)
30
+streams_file = plugin.get_setting("general_streams_file",str)
31
+streams_file_remote = plugin.get_setting("general_streams_file_remote",str)
32
+use_streams_file_remote = plugin.get_setting("general_use_streams_file_remote",bool)
23 33
 overwrite = plugin.get_setting("general_download_overwrite",bool)
34
+
24 35
 sleep_time = 5  # TODO jāliek iekš parametriem
25 36
 
26 37
 cunicode = lambda s: s.decode("utf8") if isinstance(s, str) else s
@@ -28,9 +39,9 @@ cstr = lambda s: s.encode("utf8") if isinstance(s, unicode) else s
28 39
 
29 40
 #print "argv=",sys.argv
30 41
 cmd = sys.argv[1]
31
-title = sys.argv[2]
32
-data = sys.argv[3]
33
-download_dir = sys.argv[4]
42
+title = urllib.unquote(sys.argv[2])
43
+data = urllib.unquote(sys.argv[3])
44
+download_dir = urllib.unquote(sys.argv[4])
34 45
 #overwrite = sys.argv[5] if len(sys.argv)>5 else False
35 46
 
36 47
 queue_dir = os.path.join(xbmc.translatePath("special://temp"), "download_queue") if not CLI_MODE else "download_queue"
@@ -54,8 +65,20 @@ if cmd in ["download2", "download3"] and not CLI_MODE:
54 65
 
55 66
 cur_directory = os.path.dirname(__file__)
56 67
 sources_directory = os.path.join(cur_directory,"resources","lib", "content", "sources")
57
-sources = ContentSources.ContentSources(sources_directory)
58
-
68
+#sources = ContentSources.ContentSources(sources_directory)
69
+#print "[playstream] Create sources objects"
70
+if use_streams_file_remote:
71
+    cfg_file = streams_file_remote
72
+    cfg_file2 = streams_file
73
+else:
74
+    cfg_file = streams_file
75
+    cfg_file2 = None
76
+try:
77
+    sources = ContentSources.ContentSources(sources_directory, cfg_file, cfg_file2)
78
+except Exception as e:
79
+    #xbmc.executebuiltin("Notification(Error,%s" % e.message)
80
+    #xbmc.executebuiltin("XBMC.ActivateWindow(Home)")
81
+    plugin.notify(e.message)
59 82
 
60 83
 def main():
61 84
     if not sources.is_video(data):  # Folderis
@@ -89,14 +112,17 @@ def download_video(title, data, download_dir, overwrite, cb_notify=None, num=Non
89 112
     if len(streams)>1 and not num:
90 113
         slist = []
91 114
         for s in streams:
92
-            slist.append("%s [%s,%s]"%(s["name"],s["quality"],s["lang"]))
115
+            name2 = "%s,%s" % (s["quality"],s["lang"]) if s["lang"] else s["quality"]
116
+            slist.append("[%s] %s"%(name2, s["name"]))
93 117
         res = xbmcgui.Dialog().select("Select stream",slist) if not CLI_MODE else 0
94 118
         #res = xbmcgui.Dialog().contextmenu(slist) if not CLI_MODE else 0
95 119
         stream = streams[res]
96 120
     else:
97 121
         stream = streams[0]
98
-
99
-    download_stream(stream, download_dir, overwrite, notify, num=num)
122
+    try:
123
+        download_stream(stream, download_dir, overwrite, notify, num=num)
124
+    except Exception as e:
125
+        notify(e.message, title="Error")
100 126
 
101 127
     #d = Downloader.download_video(stream["url"], os.path.join(download_dir, output), stream["headers"])
102 128
     #reactor.run()
@@ -122,12 +148,14 @@ def notify(text, title="Info", time=10000):
122 148
 ####################################################################################################
123 149
 
124 150
 def download_stream(stream, download_dir, overwrite=True, cb_notify=None, num=False):
151
+    UA = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0"
152
+
125 153
     #output =  stream["name"].replace("\\"," ").replace(":"," ").replace("|"," ")
126 154
     url = stream["url"]
127 155
     title = stream["name"].strip()
128 156
     output = file.make_fname(title)
129 157
 
130
-    headers = stream["headers"] if "headers" in stream and stream["headers"] else {"user-agent":"Enigma2"}
158
+    headers = stream["headers"] if "headers" in stream and stream["headers"] else {"user-agent":UA}
131 159
     try:
132 160
         h = get_header(url,headers=headers)
133 161
         mtype = h.get("content-type")
@@ -215,9 +243,13 @@ def download_hls(url, outputfile, headers=None, overwrite=True, limit=None, cb_n
215 243
 #    if not "User-Agent" in headers:
216 244
 #        headers["User-Agent"] = UA
217 245
     #outputfile = file.join(download_dir,output)
246
+    if "/" in outputfile:
247
+        output = outputfile.split('/')[-1]
248
+    else:
249
+        output = outputfile.split('\\')[-1]
218 250
 
219
-    if cb_notify: cb_notify("Download started - %s" % outputfile)
220
-    print "Download started - %s" % outputfile, url
251
+    if cb_notify: cb_notify("Download started - %s" % output)
252
+    print "Download started - %s" % output
221 253
     #print url
222 254
 
223 255
 
@@ -277,17 +309,20 @@ def download_hls(url, outputfile, headers=None, overwrite=True, limit=None, cb_n
277 309
         r = requests.get(url2, headers=headers, verify=False)
278 310
         content = r.content
279 311
         if key:
280
-            #from Crypto.Cipher import AES
281
-            import pyaes
312
+            try:
313
+                from Cryptodome.Cipher import AES
314
+            except:
315
+                raise Exception("No Crypto or Cryptodome library")
316
+            #import pyaes
282 317
             iv = content[:16]
283 318
             #key2 = binascii.a2b_hex(key)
284
-            #d = AES.new(key, AES.MODE_CBC, iv)
285
-            d = pyaes.AESModeOfOperationCBC(key, iv = iv)
286
-            content2 = ""
287
-            for i in range(16, len(content), 16):
288
-                content2 += d.decrypt(content[i:i+16])
289
-            content = content2
290
-            #content = d.decrypt(content[16:])
319
+            d = AES.new(key, AES.MODE_CBC, iv)
320
+            #d = pyaes.AESModeOfOperationCBC(key, iv = iv)
321
+            #content2 = ""
322
+            #for i in range(16, len(content), 16):
323
+            #    content2 += d.decrypt(content[i:i+16])
324
+            #content = content2
325
+            content = d.decrypt(content[16:])
291 326
             #d = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv))
292 327
             #content = d.feed(content[16:])
293 328
         tsfile.write(content)
@@ -319,7 +354,7 @@ def download_hls(url, outputfile, headers=None, overwrite=True, limit=None, cb_n
319 354
         progress = float(currentbytes)/float(totalbytes)*100
320 355
         msg = "%.1f%% (%iMB/%iMB)"%(progress,currentbytes / 1024 / 1024,totalbytes / 1024 / 1024)
321 356
         #print msg
322
-        #notify(msg)
357
+        notify(msg)
323 358
 
324 359
         if type == "vod":
325 360
             if ts_num >= len(ts_list) or (limit and currenttime>limit):
@@ -343,9 +378,13 @@ def download_file(url, outputfile, headers=None, overwrite=True, limit=None, cb_
343 378
 #    if not "User-Agent" in headers:
344 379
 #        headers["User-Agent"] = UA
345 380
     #fname = file.join(download_dir,output)
381
+    if "/" in outputfile:
382
+        output = outputfile.split('/')[-1]
383
+    else:
384
+        output = outputfile.split('\\')[-1]
346 385
 
347
-    if cb_notify: cb_notify("Download started - %s" % outputfile)
348
-    print "Download started - %s" % outputfile
386
+    if cb_notify: cb_notify("Download started - %s" % output)
387
+    print "Download started - %s" % output
349 388
     #print url
350 389
     try:
351 390
         r = requests.get(url,headers=headers, stream=True)

+ 1
- 1
get_version.py View File

@@ -1,2 +1,2 @@
1 1
 import re,sys
2
-print re.search('<addon version="([^"]+)',open(sys.argv[1]).read()).group(1)
2
+print re.search('<addon.+?%s="([^"]+)'%sys.argv[2],open(sys.argv[1]).read()).group(1)

+ 44
- 42
kmake.bat View File

@@ -1,38 +1,37 @@
1 1
 @echo off
2 2
 
3
-:--- Pull content submodule ---
4
-pushd resources\lib\content
5
-:git commit -a -m a
6
-:git checkout .
7
-:git pull
8
-popd
9
-
10 3
 :=== Parameters ===
11
-python get_version.py addon.xml >version.txt
4
+python -c "import re,sys; print re.search('<addon.+?version=""([^""]+)""',open('addon.xml').read()).group(1)" >version.txt
12 5
 set /p ver=<version.txt
13
-echo %ver%
14
-rem pause
15
-
16
-set prog=PlayStream
17
-set pack_name=plugin.video.playstream
18
-set desc=Play online streams from various sources, mostly Latvian
6
+python -c "import re,sys; print re.search('<addon.+?id=""([^""]+)""',open('addon.xml').read()).group(1)" >id.txt
7
+set /p pack_name=<id.txt
8
+echo %pack_name% %ver%
9
+pause
19 10
 
20
-set ipk_dir=ipkg\
21 11
 set release_dir=release\
22
-set repo_dir=..\repo\
23
-set feed_dir=q:\web\repo\
12
+set repo_dir=q:\web\repo\
13
+:set feed_dir=q:\web\repo\
24 14
 
25 15
 set AR=\MinGW\bin\ar.exe
26 16
 set TAR=\MinGW\msys\1.0\bin\tar.exe
17
+set CP=\MinGW\msys\1.0\bin\cp.exe
18
+set ZIP=
27 19
 rem set ZIP=\Program Files (x86)\Gow\bin\zip.exe
28 20
 
29
-:=== data files ===
21
+
22
+:=== Pull content submodule ===
23
+pushd resources\lib\content
24
+:git commit -a -m labojumi
25
+git checkout .
26
+git pull
27
+popd
28
+
29
+
30
+:=== Create package zip file ===
30 31
 if exist "%pack_name%" rm -r -f "%pack_name%"
31 32
 mkdir "%pack_name%""
32 33
 if not exist %release_dir% mkdir %release_dir%
33
-if not exist %repo_dir% mkdir %repo_dir%
34
-if not exist  %repo_dir%%pack_name% mkdir  %repo_dir%%pack_name%
35
-
34
+echo Copying files to %pack_name%
36 35
 
37 36
 for %%f in (
38 37
 readme.md
@@ -71,7 +70,7 @@ resources\lib\content\sources\iplayer.py
71 70
 resources\lib\content\sources\movieplace.py
72 71
 resources\lib\content\sources\ltc.py
73 72
 resources\lib\content\sources\mtgplay.py
74
-resources\lib\content\sources\play24.py
73
+resources\lib\content\sources\xtv.py
75 74
 resources\lib\content\sources\replay.py
76 75
 resources\lib\content\sources\lmt.py
77 76
 resources\lib\content\sources\serialguru.py
@@ -79,6 +78,8 @@ resources\lib\content\sources\tvdom.py
79 78
 resources\lib\content\sources\ustvnow.py
80 79
 resources\lib\content\sources\viaplay.py
81 80
 resources\lib\content\sources\filmas.py
81
+resources\lib\content\sources\tvplay.py
82
+resources\lib\content\sources\enigma2.py
82 83
 resources\lib\content\sources\YouTubeVideoUrl.py
83 84
 resources\lib\content\sources\jsinterp.py
84 85
 resources\lib\content\sources\swfinterp.py
@@ -96,29 +97,30 @@ resources\lib\content\resolvers\youtuberesolver.py
96 97
 
97 98
 xcopy /y /q resources\lib\content\picons\* %pack_name%\resources\picons\
98 99
 
99
-pause
100
-
100
+echo Creating %release_dir%%pack_name%-%ver%.zip
101 101
 if exist  %release_dir%%pack_name%-%ver%.zip rm %release_dir%%pack_name%-%ver%.zip
102
-rem zip  -r %release_dir%%pack_name%-%ver%.zip %pack_name%
103
-"C:\Program Files\WinRAR\winrar.exe" a -afzip -r %release_dir%%pack_name%-%ver%.zip %pack_name%
104
-copy  addon.xml  %repo_dir%%pack_name%\addon.xml /Y
105
-copy  %release_dir%%pack_name%-%ver%.zip  %repo_dir%%pack_name%\%pack_name%-%ver%.zip /Y
106
-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
102
+7za a -r -tzip -bb0 -bsp1 -bso0 %release_dir%%pack_name%-%ver%.zip %pack_name%
103
+if exist %pack_name% rm -r -f %pack_name%
107 104
 
105
+if ()==(%1%) (
106
+    GOTO :EOF
107
+)
108
+:=== Add to git ===
109
+echo Adding to git
108 110
 git add %release_dir%%pack_name%-%ver%.zip
109
-rm -r -f "%pack_name%"
111
+git commit -m %ver%
112
+git tag -d %ver%
113
+git tag %ver%
114
+git push
110 115
 
111
-if not ()==(%1%) (
112
-    git commit -m %ver%
113
-    git tag -d "%ver%"
114
-    git tag %ver%
115
-    git push
116 116
 
117
-    pushd  %repo_dir%..
118
-    call update_repo.bat
119
-    rem xcopy /s /y /u repo %feed_dir%
120
-    robocopy repo %feed_dir%  addons.* /copy:dat
121
-    robocopy repo\%pack_name% q:\web\repo\%pack_name% /copy:DAT /xo
122
-    popd
117
+:=== Copy package to repo ===
118
+echo Copying  %release_dir%%pack_name%-%ver%.zip to %repo_dir%%pack_name%
119
+if not exist %repo_dir% mkdir %repo_dir%
120
+if not exist  %repo_dir%%pack_name% mkdir  %repo_dir%%pack_name%
121
+cp  addon.xml  %repo_dir%%pack_name%\addon.xml -fpv
122
+cp  %release_dir%%pack_name%-%ver%.zip  %repo_dir%%pack_name%\%pack_name%-%ver%.zip -fpv
123
+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
124
+python ..\generate_repo.py %repo_dir%
123 125
 )
124
-pause
126
+

+ 753
- 633
project.wpr
File diff suppressed because it is too large
View File


+ 13
- 3
resources/language/English/strings.xml View File

@@ -4,6 +4,7 @@
4 4
   <string id="30000">PlayStream</string>
5 5
 
6 6
   <string id="40000">General settings</string>
7
+  <string id="40021">Playstreamproxy</string>
7 8
   <string id="40001">Replay.lv</string>
8 9
   <string id="40002">Skaties.lv</string>
9 10
   <string id="40003">Shortcut.tv</string>
@@ -19,6 +20,7 @@
19 20
   <string id="40013">USTVNow</string>
20 21
   <string id="40014">FilmOn</string>
21 22
   <string id="40015">MTGPlay</string>
23
+  <string id="40016">Enigma2</string>
22 24
 
23 25
   <string id="50001">Username</string>
24 26
   <string id="50002">Password</string>
@@ -28,10 +30,18 @@
28 30
   <string id="50010">Save plugin status between call</string>
29 31
   <string id="50011">Time to live for status</string>
30 32
   <string id="50012">Download directory</string>
33
+
31 34
   <string id="50013">Start local proxy server</string>
32 35
   <string id="50014">Local proxy server port</string>
33
-  <string id="50017">Use proxy for problematic streams</string>
34
-  <string id="50015">Stream proxy url</string>
36
+  <string id="50027">Local proxy secret key</string>
37
+  <string id="50028">Redirect streams</string>
38
+  <string id="50029">User multithreading</string>
39
+  <string id="50030">Threads</string>
40
+
41
+  <string id="50017">Use playstreamproxy for content</string>
42
+  <string id="50015">Playstreamproxy url</string>
43
+  <string id="50031">Playstreamproxy secret key</string>
44
+
35 45
   <string id="50016">Playlist file (m3u)</string>
36 46
   <string id="50020">Autostart PlayStream</string>
37 47
   <string id="50021">View Mode</string>
@@ -39,5 +49,5 @@
39 49
   <string id="50023">Remote content config file location/name (ftp or http)</string>
40 50
   <string id="50024">Use remote content config file</string>
41 51
   <string id="50025">Overwrite existing files while downloading</string>
42
-
52
+  <string id="50026">Enigma2 hostname or ip address</string>
43 53
 </strings>

+ 1
- 1
resources/lib/content

@@ -1 +1 @@
1
-Subproject commit 7da47c23d003c1768ed11b517df52d1fdb299398
1
+Subproject commit b1add8217bbb6c551559f91ed78b905d27281731

+ 17
- 4
resources/settings.xml View File

@@ -9,13 +9,20 @@
9 9
         <setting id="general_download_dir" label="50012" type="folder" default="download" />
10 10
         <setting id="general_download_overwrite" label="50025" type="bool" default="false" />
11 11
         <setting id="general_playlist" label="50016" type="file" default="playstream.m3u" />
12
-        <setting id="general_proxy" label="50013" type="bool" default="false" />
13
-        <setting id="general_port" label="50014" type="number" default="8880" />
14
-        <setting id="general_proxy_use" label="50017" type="bool" default="false" />
15
-        <setting id="general_proxy_url" label="50015" type="text" default="http://localhost:8880/" />
16 12
         <setting id="general_streams_file" label="50022" type="file" default="streams.cfg" />
17 13
         <setting id="general_streams_file_remote" label="50023" type="file" default="ftp://user:password@hostname/hdd/streams.cfg" />
18 14
         <setting id="general_use_streams_file_remote" label="50024" type="bool" default="false" />
15
+        <setting id="general_proxy_use" label="50017" type="bool" default="false" />
16
+        <setting id="general_proxy_url" label="50015" type="text" default="http://localhost:8880/" />
17
+        <setting id="general_proxy_remote_key" label="50031" type="text" default="" />
18
+    </category>
19
+    <category label="40021">
20
+        <setting id="general_proxy" label="50013" type="bool" default="false" />
21
+        <setting id="general_port" label="50014" type="number" default="8880" />
22
+        <setting id="general_proxy_key" label="50027" type="text" default="" />
23
+        <setting id="general_proxy_redirect" label="50028" type="bool" default="true" />
24
+        <setting id="general_proxy_multithread" label="50029" type="bool" default="false" />
25
+        <setting id="general_proxy_workers" label="50030" type="number" default="10" />
19 26
     </category>
20 27
 
21 28
     <category label="40003">
@@ -55,5 +62,11 @@
55 62
         <setting type="sep" />
56 63
     </category>
57 64
 
65
+   <category label="40016">
66
+        <setting id="enigma2_host" label="50026" type="text" default="" />
67
+        <setting id="enigma2_user" label="50001" type="text" default="" />
68
+        <setting id="enigma2_password" label="50002" type="text" default="change password" />
69
+        <setting type="sep" />
70
+    </category>
58 71
 
59 72
 </settings>

+ 9
- 2
service.py View File

@@ -27,11 +27,18 @@ if autostart:
27 27
 
28 28
 port = plugin.get_setting("general_port",int)
29 29
 start_proxy = plugin.get_setting("general_proxy",bool)
30
-host = "localhost"
30
+redirect = plugin.get_setting("general_proxy_redirect",bool)
31
+multithread = plugin.get_setting("general_proxy_multithread",bool)
32
+workers = plugin.get_setting("general_proxy_workers",int)
33
+key = plugin.get_setting("general_proxy_key",str)
34
+host = "0.0.0.0"
31 35
 if start_proxy:
32 36
     plugin.notify("Starting playstreamproxy","Info",10000, xbmcgui.NOTIFICATION_INFO)
33 37
     try:
34
-        playstreamproxy.start(host, port)
38
+        if multithread:
39
+            playstreamproxy.start2(host, port, redirect, key, workers)
40
+        else:
41
+            playstreamproxy.start(host, port, redirect, key)
35 42
     except Exception as e:
36 43
         xbmcgui.Dialog().ok("Error starting playstreamproxyserver",unicode(e))
37 44
         #plugin.notify(unicode(e), "Error", 10000, xbmcgui.NOTIFICATION_ERROR)

+ 127
- 110
wingdbstub.py View File

@@ -1,54 +1,50 @@
1 1
 #########################################################################
2
-""" wingdbstub.py    -- Debug stub for debuggifying Python programs
2
+""" wingdbstub.py -- Start debugging Python programs from outside of Wing
3 3
 
4
-Copyright (c) 1999-2001, Archaeopteryx Software, Inc.  All rights reserved.
4
+Copyright (c) 1999-2018, Archaeopteryx Software, Inc.  All rights reserved.
5 5
 
6 6
 Written by Stephan R.A. Deibel and John P. Ehresman
7 7
 
8 8
 Usage:
9 9
 -----
10 10
 
11
-This is the file that Wing DB users copy into their python project 
12
-directory if they want to be able to debug programs that are launched
13
-outside of the IDE (e.g., CGI scripts, in response to a browser page
14
-load).
15
-
16
-To use this, edit the configuration values below to match your 
17
-Wing installation and requirements of your project.
18
-
19
-Then, add the following line to your code:
11
+This file is used to initiate debug in Python code without launching
12
+that code from Wing.  To use it, edit the configuration values below,
13
+start Wing and configure it to listen for outside debug connections,
14
+and then add the following line to your code:
20 15
 
21 16
   import wingdbstub
22 17
 
23
-Debugging will start immediately after this import statements.
24
-
25
-Next make sure that your IDE is running and that it's configured to accept
26
-connections from the host the debug program will be running on.
27
-
28
-Now, invoking your python file should run the code within the debugger.
29
-Note, however, that Wing will not stop in the code unless a breakpoint
30
-is set.
31
-
32
-If the debug process is started before the IDE, or is not listening
33
-at the time this module is imported then the program will run with
34
-debugging until an attach request is seen.  Attaching only works 
35
-if the .wingdebugpw file is present; see the manual for details.
18
+Debugging will start immediately after this import statement is reached.
36 19
 
37
-On win32, you either need to edit WINGHOME in this script or
38
-pass in an environment variable called WINGHOME that points to
39
-the Wing installation directory.
20
+For details, see Debugging Externally Launched Code in the manual.
40 21
 
41 22
 """
42 23
 #########################################################################
43 24
 
44 25
 import sys
26
+orig_sys_modules = list(sys.modules.keys())
27
+orig_sys_path = list(sys.path)
28
+orig_meta_path = list(sys.meta_path)
29
+
45 30
 import os
46
-import imp
31
+if sys.version_info >= (3, 7):
32
+  import importlib
33
+else:
34
+  import imp
35
+
36
+#------------------------------------------------------------------------
37
+# Required configuration values (some installers set this automatically)
47 38
 
39
+# This should be the full path to your Wing installation.  On OS X, use 
40
+# the full path of the Wing application bundle, for example 
41
+# /Applications/WingPro.app.  When set to None, the environment variable 
42
+# WINGHOME is used instead.  
43
+WINGHOME = r"C:\Program Files (x86)\Wing Pro 7.0"
48 44
 
49 45
 #------------------------------------------------------------------------
50
-# Default configuration values:  Note that the named environment 
51
-# variables, if set, will override these settings.
46
+# Optional configuration values:  The named environment variables, if set, 
47
+# will override these settings.
52 48
 
53 49
 # Set this to 1 to disable all debugging; 0 to enable debugging
54 50
 # (WINGDB_DISABLED environment variable)
@@ -60,30 +56,33 @@ kWingDebugDisabled = 0
60 56
 kWingHostPort = 'localhost:50005'
61 57
 
62 58
 # Port on which to listen for connection requests, so that the
63
-# IDE can (re)attach to the debug process after it has started.
59
+# IDE can attach or reattach to the debug process after it has started.
64 60
 # Set this to '-1' to disable listening for connection requests.
65 61
 # This is only used when the debug process is not attached to
66 62
 # an IDE or the IDE has dropped its connection. The configured
67 63
 # port can optionally be added to the IDE's Common Attach Hosts
68
-# preference. Note that a random port is used instead if this 
69
-# port is already in use!
64
+# preference. Note that a random port is used instead if the given 
65
+# port is already in use.
70 66
 # (WINGDB_ATTACHPORT environment variable)
71 67
 kAttachPort = '50015'
72 68
 
73 69
 # Set this to a filename to log verbose information about the debugger
74 70
 # internals to a file.  If the file does not exist, it will be created
75 71
 # as long as its enclosing directory exists and is writeable.  Use 
76
-# "<stderr>" or "<stdout>".  Note that "<stderr>" may cause problems 
77
-# on win32 if the debug process is not running in a console.
72
+# "<stderr>" or "<stdout>" to write to stderr or stdout.  Note that 
73
+# "<stderr>" may cause problems on Windows if the debug process is not 
74
+# running in a console.
78 75
 # (WINGDB_LOGFILE environment variable)
79 76
 kLogFile = None
80 77
 
81
-# Set to get a tremendous amount of logging from the debugger internals
82
-# (WINGDB_LOGVERYVERBOSE)
78
+# Produce a tremendous amount of logging from the debugger internals.
79
+# Do not set this unless instructed to do so by Wingware support.  It
80
+# will slow the debugger to a crawl.
81
+# (WINGDB_LOGVERYVERBOSE environment variable)
83 82
 kLogVeryVerbose = 0
84 83
 
85 84
 # Set this to 1 when debugging embedded scripts in an environment that
86
-# creates and reuses a Python instance across multiple script invocations:  
85
+# creates and reuses a Python instance across multiple script invocations.  
87 86
 # It turns off automatic detection of program quit so that the debug session
88 87
 # can span multiple script invocations.  When this is turned on, you may
89 88
 # need to call ProgramQuit() on the debugger object to shut down the
@@ -92,58 +91,44 @@ kLogVeryVerbose = 0
92 91
 # only the first one will be able to debug unless it terminates debug
93 92
 # and the environment variable WINGDB_ACTIVE is unset before importing
94 93
 # this module in the second or later Python instance.  See the Wing
95
-# IDE manual for details.
94
+# manual for details.
95
+# (WINGDB_EMBEDDED environment variable)
96 96
 kEmbedded = 0
97 97
 
98 98
 # Path to search for the debug password file and the name of the file
99
-# to use.  The password file contains the encryption type and connect 
100
-# password for all connections to the IDE and must match the wingdebugpw
101
-# file in the profile dir used by the IDE.  Any entry of '$<winguserprofile>' 
102
-# is replaced by the wing user profile directory for the user that the 
103
-# current process is running as
99
+# to use.  The password file contains a security token for all 
100
+# connections to the IDE and must match the wingdebugpw file in the 
101
+# User Settngs directory used by the IDE. '$<winguserprofile>' 
102
+# is replaced by the User Settings directory for the user that
103
+# is running the process.
104 104
 # (WINGDB_PWFILEPATH environment variable)
105 105
 kPWFilePath = [os.path.dirname(__file__), '$<winguserprofile>']
106 106
 kPWFileName = 'wingdebugpw'
107 107
 
108
-# Whether to exit if the debugger fails to run or to connect with an IDE
109
-# for whatever reason
108
+# Whether to exit when the debugger fails to run or to connect with the IDE
109
+# By default, execution continues without debug or without connecting to
110
+# the IDE.
111
+# (WINGDB_EXITONFAILURE environment variable)
110 112
 kExitOnFailure = 0
111 113
 
112 114
 #------------------------------------------------------------------------
113 115
 # Find Wing debugger installation location
114 116
 
115
-# Edit this to point to your Wing installation or set to None to use env WINGHOME
116
-# On OS X this must be set to name of the Wing application bundle
117
-# (for example, /Applications/WingIDE.app)
118
-WINGHOME = None
119
-
120
-if sys.hexversion >= 0x03000000:
121
-  def has_key(o, key):
122
-    return key in o
123
-else:
124
-  def has_key(o, key):
125
-    return o.has_key(key)
126
-    
127 117
 # Check environment:  Must have WINGHOME defined if still == None
128 118
 if WINGHOME == None:
129
-  if has_key(os.environ, 'WINGHOME'):
119
+  if 'WINGHOME' in os.environ:
130 120
     WINGHOME=os.environ['WINGHOME']
131 121
   else:
132
-    sys.stdout.write("*******************************************************************\n")
133
-    sys.stdout.write("Error: Could not find Wing installation!  You must set WINGHOME or edit\n")
134
-    sys.stdout.write("wingdbstub.py where indicated to point it to the location where\n")
135
-    sys.stdout.write("Wing is installed.\n")
136
-    sys.exit(1)
137
-
138
-kPWFilePath.append(WINGHOME)
139
-
140
-# The user settings dir where per-user settings & patches are located.  Will be
141
-# set from environment if left as None
142
-kUserSettingsDir = None
143
-if kUserSettingsDir is None:
144
-  kUserSettingsDir = os.environ.get('WINGDB_USERSETTINGS')
122
+    msg = '\n'.join(["*******************************************************************", 
123
+                     "Error: Could not find Wing installation!  You must set WINGHOME or edit", 
124
+                     "wingdbstub.py where indicated to point it to the location where", 
125
+                     "Wing is installed.\n"])
126
+    sys.stderr.write(msg)
127
+    raise ImportError(msg)
145 128
   
146
-def _FindActualWingHome(winghome):
129
+WINGHOME = os.path.expanduser(WINGHOME)
130
+
131
+def NP_FindActualWingHome(winghome):
147 132
   """ Find the actual directory to use for winghome.  Needed on OS X
148 133
   where the .app directory is the preferred dir to use for WINGHOME and
149 134
   .app/Contents/MacOS is accepted for backward compatibility. """
@@ -168,28 +153,38 @@ def _FindActualWingHome(winghome):
168 153
   
169 154
   return winghome
170 155
   
171
-def _ImportWingdb(winghome, user_settings=None):
172
-  """ Find & import wingdb module. """
156
+WINGHOME = NP_FindActualWingHome(WINGHOME)
157
+os.environ['WINGHOME'] = WINGHOME
158
+
159
+# Path used to find the wingdebugpw file
160
+kPWFilePath.append(WINGHOME)
161
+
162
+#-----------------------------------------------------------------------
163
+def NP_LoadModuleFromBootstrap(winghome, modname):
164
+  """Load a module from the installation bootstrap directory.  Assumes that
165
+  imports don't change sys.path.  The modules are unloaded from sys.modules
166
+  so they can be loaded again later from an update."""
173 167
   
174
-  try:
175
-    exec_dict = {}
176
-    execfile(os.path.join(winghome, 'bin', '_patchsupport.py'), exec_dict)
177
-    find_matching = exec_dict['FindMatching']
178
-    dir_list = find_matching('bin', winghome, user_settings)
179
-  except Exception:
180
-    dir_list = []
181
-  dir_list.extend([os.path.join(winghome, 'bin'), os.path.join(winghome, 'src')])
182
-  for path in dir_list:
183
-    try:
184
-      f, p, d = imp.find_module('wingdb', [path])
185
-      try:
186
-        return imp.load_module('wingdb', f, p, d)
187
-      finally:
188
-        if f is not None:
189
-          f.close()
190
-      break
191
-    except ImportError:
192
-      pass
168
+  # Limited to simple module loads
169
+  assert '.' not in modname
170
+  
171
+  orig_sys_path = sys.path[:]
172
+  orig_modules = set(sys.modules)
173
+  
174
+  dirname = winghome + '/bootstrap'
175
+  sys.path.insert(0, dirname)
176
+  
177
+  code = 'import %s' % modname
178
+  exec(code)
179
+  
180
+  new_modules = set(sys.modules)
181
+  new_modules.difference_update(orig_modules)
182
+  for mod in new_modules:
183
+    del sys.modules[mod]
184
+    
185
+  sys.path = orig_sys_path
186
+  
187
+  return locals()[modname]
193 188
 
194 189
 #------------------------------------------------------------------------
195 190
 # Set debugger if it hasn't been set -- this is to handle module reloading
@@ -199,16 +194,18 @@ try:
199 194
 except NameError:
200 195
   debugger = None
201 196
   
197
+#-----------------------------------------------------------------------
202 198
 # Unset WINGDB_ACTIVE env if it was inherited from another process
203 199
 # XXX Would be better to be able to call getpid() on dbgtracer but can't access it yet
204 200
 if 'WINGDB_ACTIVE' in os.environ and os.environ['WINGDB_ACTIVE'] != str(os.getpid()):
205 201
   del os.environ['WINGDB_ACTIVE']
206 202
 
203
+#-----------------------------------------------------------------------
207 204
 # Start debugging if not disabled and this module has never been imported
208 205
 # before
209
-if (not kWingDebugDisabled and debugger is None
210
-    and not has_key(os.environ, 'WINGDB_DISABLED') and 
211
-    not has_key(os.environ, 'WINGDB_ACTIVE')):
206
+if (not kWingDebugDisabled and debugger is None and
207
+    'WINGDB_DISABLED' not in os.environ and 
208
+    'WINGDB_ACTIVE' not in os.environ):
212 209
 
213 210
   exit_on_fail = 0
214 211
   
@@ -238,35 +235,54 @@ if (not kWingDebugDisabled and debugger is None
238 235
     embedded = int(os.environ.get('WINGDB_EMBEDDED', kEmbedded))
239 236
   
240 237
     # Obtain debug password file search path
241
-    if has_key(os.environ, 'WINGDB_PWFILEPATH'):
238
+    if 'WINGDB_PWFILEPATH' in os.environ:
242 239
       pwfile_path = os.environ['WINGDB_PWFILEPATH'].split(os.pathsep)
243 240
     else:
244 241
       pwfile_path = kPWFilePath
245 242
     
246 243
     # Obtain debug password file name
247
-    if has_key(os.environ, 'WINGDB_PWFILENAME'):
244
+    if 'WINGDB_PWFILENAME' in os.environ:
248 245
       pwfile_name = os.environ['WINGDB_PWFILENAME']
249 246
     else:
250 247
       pwfile_name = kPWFileName
251 248
     
252
-    # Load wingdb.py
253
-    actual_winghome = _FindActualWingHome(WINGHOME)
254
-    wingdb = _ImportWingdb(actual_winghome, kUserSettingsDir)
255
-    if wingdb == None:
256
-      sys.stdout.write("*******************************************************************\n")
257
-      sys.stdout.write("Error: Cannot find wingdb.py in $(WINGHOME)/bin or $(WINGHOME)/src\n")
258
-      sys.stdout.write("Error: Please check the WINGHOME definition in wingdbstub.py\n")
259
-      sys.exit(2)
249
+    # Set up temporary log for errors from merge importer Setup
250
+    class CTmpLog:
251
+      def __init__(self):
252
+        self.fErrors = []
253
+      def write(self, s):
254
+        self.fErrors.append(s)
255
+    tmp_log = CTmpLog()
256
+    
257
+    # Set up the meta importer; everything after this point can be update
258
+    bootstrap_utils = NP_LoadModuleFromBootstrap(WINGHOME, 'bootstrap_utils')
259
+    winghome, user_settings = bootstrap_utils.NP_SetupWingHomeModule(WINGHOME)
260
+    meta = bootstrap_utils.NP_CreateMetaImporter(WINGHOME, user_settings, 'dbg',
261
+                                                 tmp_log)
262
+    import _winghome
263
+    _winghome.kWingHome = winghome
264
+    _winghome.kUserSettings = user_settings
260 265
     
261 266
     # Find the netserver module and create an error stream
262
-    netserver = wingdb.FindNetServerModule(actual_winghome, kUserSettingsDir)
263
-    err = wingdb.CreateErrStream(netserver, logfile, very_verbose_log)
267
+    from debug.tserver import startdebug
268
+    netserver = startdebug.FindNetServerModule(WINGHOME, user_settings)
269
+    err = startdebug.CreateErrStream(netserver, logfile, very_verbose_log)
264 270
     
265
-    # Start debugging
271
+    # Write any temporary log entries
272
+    for s in tmp_log.fErrors:
273
+      err.write(s)
274
+      
275
+    # Create debug server
266 276
     debugger = netserver.CNetworkServer(host, port, attachport, err, 
267 277
                                         pwfile_path=pwfile_path,
268 278
                                         pwfile_name=pwfile_name,
269 279
                                         autoquit=not embedded)
280
+    
281
+    # Restore module and path environment
282
+    from debug.tserver import startdebug
283
+    startdebug.RestoreEnvironment(orig_sys_modules, orig_sys_path, orig_meta_path)
284
+    
285
+    # Start debugging
270 286
     debugger.StartDebug(stophere=0)
271 287
     os.environ['WINGDB_ACTIVE'] = str(os.getpid())
272 288
     if debugger.ChannelClosed():
@@ -278,6 +294,7 @@ if (not kWingDebugDisabled and debugger is None
278 294
     else:
279 295
       pass
280 296
 
297
+#-----------------------------------------------------------------------
281 298
 def Ensure(require_connection=1, require_debugger=1):
282 299
   """ Ensure the debugger is started and attempt to connect to the IDE if
283 300
   not already connected.  Will raise a ValueError if: