Browse Source

Sākotnējais kommits

Ivars 8 years ago
parent
commit
eb30bf80e0

+ 235
- 0
ChoiceBox.py View File

@@ -0,0 +1,235 @@
1
+from Screens.Screen import Screen
2
+from Components.ActionMap import NumberActionMap
3
+from Components.Label import Label
4
+from Components.ChoiceList import ChoiceEntryComponent, ChoiceList
5
+from Components.Sources.StaticText import StaticText
6
+from Components.Pixmap import Pixmap
7
+import enigma
8
+
9
+class ChoiceBox2(Screen):
10
+    skin =  """
11
+<screen name="ChoiceBox2" position="center,center" size="720,480" title="ChoiceBox" zPosition="5">
12
+    <widget name="text" position="0,0" size="720,0" font="Regular;30"/>
13
+    <widget name="list" font="Regular;22" position="10,10" size="710,460" scrollbarMode="showOnDemand" enableWrapAround="1"/>
14
+    <applet type="onLayoutFinish">
15
+            self["list"].instance.setItemHeight(25)
16
+    </applet>
17
+
18
+</screen>
19
+"""
20
+    #<applet type="onLayoutFinish">
21
+    #self.autoResize()
22
+    #</applet>
23
+    
24
+    def __init__(self, session, title="", list=None, keys=None, selection=0, skin_name=None, text=""):
25
+        Screen.__init__(self, session)        
26
+        #self.setTitle(_("Choice Box"))
27
+        if not list: list = []
28
+        if not skin_name: skin_name = []
29
+
30
+
31
+        #if isinstance(skin_name, str):
32
+        #	skin_name = [skin_name]
33
+        #self.skinName = skin_name + ["ChoiceBox"]
34
+        self["text"] = Label()
35
+        self.var = ""
36
+
37
+        if title:
38
+            title = _(title)
39
+            if len(title) < 55 and title.find('\n') == -1:
40
+                Screen.setTitle(self, title)
41
+            elif title.find('\n') != -1:
42
+                temptext = title.split('\n')
43
+                if len(temptext[0]) < 55:
44
+                    Screen.setTitle(self, temptext[0])
45
+                    count = 2
46
+                    labeltext = ""
47
+                    while len(temptext) >= count:
48
+                        if labeltext:
49
+                            labeltext += '\n'
50
+                        labeltext = labeltext + temptext[count-1]
51
+                        count += 1
52
+                        print 'count',count
53
+                    self["text"].setText(labeltext)
54
+                else:
55
+                    self["text"] = Label(title)
56
+            else:
57
+                self["text"] = Label(title)
58
+        elif text:
59
+            self["text"] = Label(_(text))
60
+        self.list = []
61
+        self.summarylist = []
62
+        if keys is None:
63
+            self.__keys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ] + (len(list) - 10) * [""]
64
+        else:
65
+            self.__keys = keys + (len(list) - len(keys)) * [""]
66
+
67
+        self.keymap = {}
68
+        pos = 0
69
+        for x in list:
70
+            strpos = str(self.__keys[pos])
71
+            self.list.append(ChoiceEntryComponent(key = strpos, text = x))
72
+            if self.__keys[pos] != "":
73
+                self.keymap[self.__keys[pos]] = list[pos]
74
+            self.summarylist.append((self.__keys[pos], x[0]))
75
+            pos += 1
76
+        self["list"] = ChoiceList(list = self.list, selection = selection)
77
+        self["summary_list"] = StaticText()
78
+        self["summary_selection"] = StaticText()
79
+        self.updateSummary(selection)
80
+
81
+        self["actions"] = NumberActionMap(["WizardActions", "InputActions", "ColorActions"],
82
+                                          {
83
+                                              "ok": self.go,
84
+                                              "1": self.keyNumberGlobal,
85
+                                              "2": self.keyNumberGlobal,
86
+                                              "3": self.keyNumberGlobal,
87
+                                              "4": self.keyNumberGlobal,
88
+                                              "5": self.keyNumberGlobal,
89
+                                              "6": self.keyNumberGlobal,
90
+                                              "7": self.keyNumberGlobal,
91
+                                              "8": self.keyNumberGlobal,
92
+                                              "9": self.keyNumberGlobal,
93
+                                              "0": self.keyNumberGlobal,
94
+                                              "red": self.keyRed,
95
+                                              "green": self.keyGreen,
96
+                                              "yellow": self.keyYellow,
97
+                                              "blue": self.keyBlue,
98
+                                              "up": self.up,
99
+                                              "down": self.down,
100
+                                              "left": self.left,
101
+                                              "right": self.right
102
+                                              }, -1)
103
+
104
+        self["cancelaction"] = NumberActionMap(["WizardActions", "InputActions", "ColorActions"],
105
+                                               {
106
+                                                   "back": self.cancel,
107
+                                                   }, -1)
108
+        #self.onShown.append(self.onshow)
109
+
110
+    def autoResize(self):
111
+        desktop_w = enigma.getDesktop(0).size().width()
112
+        desktop_h = enigma.getDesktop(0).size().height()
113
+        count = len(self.list)
114
+        itemheight = self["list"].getItemHeight()
115
+        if count > 15:
116
+            count = 15
117
+        if not self["text"].text:
118
+            # move list
119
+            textsize = (520, 0)
120
+            listsize = (520, itemheight*count)
121
+            self["list"].instance.move(enigma.ePoint(0, 0))
122
+            self["list"].instance.resize(enigma.eSize(*listsize))
123
+        else:
124
+            textsize = self["text"].getSize()
125
+            if textsize[0] < textsize[1]:
126
+                textsize = (textsize[1],textsize[0]+10)
127
+            if textsize[0] > 520:
128
+                textsize = (textsize[0], textsize[1]+itemheight)
129
+            else:
130
+                textsize = (520, textsize[1]+itemheight)
131
+            listsize = (textsize[0], itemheight*count)
132
+            # resize label
133
+            self["text"].instance.resize(enigma.eSize(*textsize))
134
+            self["text"].instance.move(enigma.ePoint(10, 10))
135
+            # move list
136
+            self["list"].instance.move(enigma.ePoint(0, textsize[1]))
137
+            self["list"].instance.resize(enigma.eSize(*listsize))
138
+
139
+        wsizex = textsize[0]
140
+        wsizey = textsize[1]+listsize[1]
141
+        wsize = (wsizex, wsizey)
142
+        self.instance.resize(enigma.eSize(*wsize))
143
+
144
+        # center window
145
+        self.instance.move(enigma.ePoint((desktop_w-wsizex)/2, (desktop_h-wsizey)/2))
146
+
147
+    def left(self):
148
+        if len(self["list"].list) > 0:
149
+            while 1:
150
+                self["list"].instance.moveSelection(self["list"].instance.pageUp)
151
+                self.updateSummary(self["list"].l.getCurrentSelectionIndex())
152
+                if self["list"].l.getCurrentSelection()[0][0] != "--" or self["list"].l.getCurrentSelectionIndex() == 0:
153
+                    break
154
+
155
+    def right(self):
156
+        if len(self["list"].list) > 0:
157
+            while 1:
158
+                self["list"].instance.moveSelection(self["list"].instance.pageDown)
159
+                self.updateSummary(self["list"].l.getCurrentSelectionIndex())
160
+                if self["list"].l.getCurrentSelection()[0][0] != "--" or self["list"].l.getCurrentSelectionIndex() == 0:
161
+                    break
162
+
163
+    def up(self):
164
+        if len(self["list"].list) > 0:
165
+            while 1:
166
+                self["list"].instance.moveSelection(self["list"].instance.moveUp)
167
+                self.updateSummary(self["list"].l.getCurrentSelectionIndex())
168
+                if self["list"].l.getCurrentSelection()[0][0] != "--" or self["list"].l.getCurrentSelectionIndex() == 0:
169
+                    break
170
+
171
+    def down(self):
172
+        if len(self["list"].list) > 0:
173
+            while 1:
174
+                self["list"].instance.moveSelection(self["list"].instance.moveDown)
175
+                self.updateSummary(self["list"].l.getCurrentSelectionIndex())
176
+                if self["list"].l.getCurrentSelection()[0][0] != "--" or self["list"].l.getCurrentSelectionIndex() == len(self["list"].list) - 1:
177
+                    break
178
+
179
+    # runs a number shortcut
180
+    def keyNumberGlobal(self, number):
181
+        self.goKey(str(number))
182
+
183
+    # runs the current selected entry
184
+    def go(self):
185
+        cursel = self["list"].l.getCurrentSelection()
186
+        if cursel:
187
+            self.goEntry(cursel[0])
188
+        else:
189
+            self.cancel()
190
+
191
+    # runs a specific entry
192
+    def goEntry(self, entry):
193
+        if entry and len(entry) > 3 and isinstance(entry[1], str) and entry[1] == "CALLFUNC":
194
+            arg = entry[3]
195
+            entry[2](arg)
196
+        elif entry and len(entry) > 2 and isinstance(entry[1], str) and entry[1] == "CALLFUNC":
197
+            entry[2](None)
198
+        else:
199
+            self.close(entry)
200
+
201
+    # lookups a key in the keymap, then runs it
202
+    def goKey(self, key):
203
+        if self.keymap.has_key(key):
204
+            entry = self.keymap[key]
205
+            self.goEntry(entry)
206
+
207
+    # runs a color shortcut
208
+    def keyRed(self):
209
+        self.goKey("red")
210
+
211
+    def keyGreen(self):
212
+        self.goKey("green")
213
+
214
+    def keyYellow(self):
215
+        self.goKey("yellow")
216
+
217
+    def keyBlue(self):
218
+        self.goKey("blue")
219
+
220
+    def updateSummary(self, curpos=0):
221
+        pos = 0
222
+        summarytext = ""
223
+        for entry in self.summarylist:
224
+            if curpos-2 < pos < curpos+5:
225
+                if pos == curpos:
226
+                    summarytext += ">"
227
+                    self["summary_selection"].setText(entry[1])
228
+                else:
229
+                    summarytext += entry[0]
230
+                summarytext += ' ' + entry[1] + '\n'
231
+            pos += 1
232
+        self["summary_list"].setText(summarytext)
233
+
234
+    def cancel(self):
235
+        self.close(None)

+ 192
- 0
ContentSources.py View File

@@ -0,0 +1,192 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+
9
+import sys,os,re
10
+import urllib2
11
+sys.path.insert(0,os.path.dirname(os.path.abspath(__file__)))
12
+from  sources.SourceBase import stream_type
13
+import util
14
+stream0 = {'name': '', 'url': '', 'quality': '???', 'surl': '', 'subs': '', 'headers': {},"desc":"","img":"","lang":"","type":""}                   
15
+
16
+class ContentSources(object):
17
+    """Wrapper for content sources plugin"""
18
+
19
+    #----------------------------------------------------------------------
20
+    def __init__(self,plugin_path):
21
+        self.plugins = {}
22
+        self.error_content = [("..atpakaļ","back",None,"Kļūda, atgriezties atpakaļ")]
23
+        sys.path.insert(0, plugin_path)
24
+        for f in os.listdir(plugin_path):
25
+            fname, ext = os.path.splitext(f)
26
+            if fname == "__init__":continue
27
+            if ext == '.py':
28
+                mod = __import__(fname)
29
+                reload(mod)
30
+                if "Source" in dir(mod):
31
+                    self.plugins[fname] = mod.Source()
32
+        sys.path.pop(0)
33
+        cfg = self.plugins["config"]
34
+        for pl in self.plugins.keys():
35
+            found = False
36
+            for lst in cfg.get_lists():
37
+                for item in cfg.lists[lst]:
38
+                    if item[1].split("::")[0]==pl:
39
+                        found = True
40
+                        break
41
+                    if found: break
42
+            if not found:
43
+                title = self.plugins[pl].title if "title" in dir(self.plugins[pl]) else pl
44
+                img = self.plugins[pl].img if "img" in dir(self.plugins[pl]) else ""
45
+                desc = self.plugins[pl].desc if "desc" in dir(self.plugins[pl]) else title
46
+                cfg.add_item("home",(title,"%s::home"%pl,img,desc))
47
+                cfg.write_streams()
48
+
49
+    def get_content(self,data):        
50
+        source = data.split("::")[0]
51
+        if source in self.plugins:
52
+            content = self.plugins[source].get_content(data)
53
+            if content:
54
+                if isinstance(content,list):      
55
+                    for i,item in enumerate(content):
56
+                        item2=[]
57
+                        for el in item:
58
+                            if isinstance(el,unicode):
59
+                                el = el.encode("utf8")
60
+                            item2.append(el)
61
+                        content[i]=tuple(item2)
62
+                else:
63
+                    item2=[]
64
+                    for el in content:
65
+                        if isinstance(el,unicode):
66
+                            el = el.encode("utf8")
67
+                        item2.append(el)
68
+                    content=tuple(item2)
69
+                return content
70
+            else:
71
+                return self.error_content
72
+        else:
73
+            return self.error_content
74
+
75
+    def get_streams(self,data):
76
+        if stream_type(data):
77
+            if "::" in data:
78
+                data = data.split("::")[1]                    
79
+            content = self.get_content(data)
80
+            stream = stream0.copy()
81
+            stream["name"] = data
82
+            stream["url"] = data
83
+            stream["type"] = stream_type(data)
84
+            #stream["img"] = ""
85
+            #stream["desc"] = ""
86
+            return[stream]
87
+
88
+        if not self.is_video(data):
89
+            return []
90
+        source = data.split("::")[0]
91
+        if source in self.plugins:
92
+            streams = self.plugins[source].get_streams(data)
93
+            for s in streams:
94
+                for k in s:
95
+                    if isinstance(s[k],unicode):
96
+                        s[k] = s[k].encode("utf8")            
97
+            return streams
98
+        else:
99
+            return []
100
+
101
+    def stream_type(self,data):
102
+        return stream_type(data)
103
+
104
+    def is_video(self,data):
105
+        if self.stream_type(data):
106
+            return True
107
+        source = data.split("::")[0]
108
+        if source in self.plugins:
109
+            return self.plugins[source].is_video(data)
110
+        else:
111
+            return False
112
+
113
+    def options_read(self,source):
114
+        if source in self.plugins:
115
+            options = self.plugins[source].options_read()
116
+            if options:
117
+                return options
118
+            else:
119
+                return None
120
+        else:
121
+            return None
122
+
123
+    def options_write(self,source,options):
124
+        if source in self.plugins:
125
+            return self.plugins[source].options_write(options)
126
+        else:
127
+            return None
128
+
129
+if __name__ == "__main__":
130
+
131
+    sources = ContentSources("sources")
132
+    if len(sys.argv)>1:
133
+        data= sys.argv[1]
134
+    else:
135
+        data = "config::home"
136
+
137
+    #options = sources.options_read("ltc")
138
+    #print options
139
+    history = []
140
+    cur = ("Home",data,None,None)
141
+    content = sources.get_content(cur[1])
142
+
143
+    exit_loop = False
144
+    while True:
145
+        print
146
+        for i,item in enumerate(content):
147
+            s = "%i: %s - %s %s %s "%(i+1,item[0],item[1],item[2],item[3])
148
+            print s #.encode(sys.stdout.encoding,"replace")
149
+
150
+        while True:
151
+            a = raw_input("Enter numeber, q for exit: ")
152
+            if a in ("q","Q","x","X"): 
153
+                exit_loop = True
154
+                print "Exiting"
155
+                break
156
+            try:
157
+                n = int(a)
158
+                break
159
+            except:
160
+                print "Not number!"
161
+        if exit_loop: break 
162
+        cur2 = content[n-1]
163
+
164
+        data0 = cur2[1].split("::")[1] if "::" in cur2[1] else cur2[1]
165
+        if (not data0):
166
+            pass
167
+
168
+        elif cur2[1] == "back":
169
+            cur = history.pop()  
170
+        elif sources.is_video(cur2[1]):
171
+            if sources.stream_type(cur2[1]):
172
+                stream = stream0.copy()
173
+                stream["url"] = cur2[1]
174
+                stream["name"] = cur2[0]
175
+                streams = [stream]
176
+            else:
177
+                streams = sources.get_streams(cur2[1])
178
+            if streams:
179
+                util.play_video(streams)
180
+            else:
181
+                print "**No stream to play - %s "%(cur2[1])
182
+                raw_input("Press any key")
183
+            #import os
184
+            #os.system('"c:\Program Files (x86)\VideoLAN\VLC\vlc.exe" "%s"'%cur2[1])
185
+
186
+        else:
187
+            if "{0}" in cur2[1]:
188
+                a = raw_input("Enter value:")
189
+                cur2 = (cur2[0],cur2[1].format(a),cur2[2],cur2[3])           
190
+            history.append(cur)
191
+            cur = cur2
192
+        content = sources.get_content(cur[1])

+ 235
- 0
VideoDownload.py View File

@@ -0,0 +1,235 @@
1
+import os
2
+
3
+from enigma import eTimer, getDesktop
4
+from Components.ActionMap import ActionMap
5
+from Components.FileList import FileList
6
+from Components.Sources.List import List
7
+from Components.Sources.StaticText import StaticText
8
+from Components.Task import Task, Job, job_manager
9
+from Screens.Screen import Screen
10
+from Tools.Downloader import downloadWithProgress
11
+from Tools.Directories import fileExists
12
+from os import path
13
+
14
+class HLSDownloadJob(Job):
15
+	def __init__(self, url, destfile, title,downloadStop):
16
+		Job.__init__(self, title)
17
+		AddHLSProcessTask(self, url, destfile, title, downloadStop)
18
+
19
+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("")
95
+
96
+
97
+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
+	
200
+
201
+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)

+ 1531
- 0
YouTubeUi.py
File diff suppressed because it is too large
View File


+ 6281
- 0
demjson.py
File diff suppressed because it is too large
View File


+ 440
- 0
enigma2_api.py View File

@@ -0,0 +1,440 @@
1
+# coding: utf-8
2
+"""
3
+Enigma2 handling objects
4
+(c)Ivars 2013-2015, v0.2
5
+"""
6
+
7
+import sys, os, os.path
8
+from urllib import quote, unquote
9
+from record3 import Rec
10
+
11
+class DBServices(object):
12
+    """Dreambox services, stored in lamedb (E2)/services(E1) file handling class"""
13
+
14
+    def __init__(self,service_path=None,enigma=2):
15
+        self.enigma = enigma
16
+        if not service_path:
17
+            service_path = "enigma2" if sys.platform=="win32" else "/etc/enigma2"
18
+        self.service_path = service_path
19
+        #print service_path
20
+
21
+        if self.enigma == 2:
22
+            if not "ftp:" in service_path:
23
+                self.service_fname= os.path.join(service_path,"lamedb")
24
+                self.bouquets_fname = os.path.join(service_path, "bouquets.tv")
25
+            else:
26
+                sep = "" if service_path[-1]=="//" else "//"
27
+                self.service_fname= service_path+sep+"lamedb"
28
+                self.bouquets_fname = service_path+sep+"bouquets.tv"
29
+
30
+        else: # ENIGMA === "1":
31
+            self.service_fname = os.path.join(service_path,"services")
32
+            self.bouquets_fname = os.path.join(service_path, "userbouquets.tv.epl")
33
+        #print self.service_fname
34
+        self.services = []
35
+        self.index_sref= {}
36
+        self.index_chid = {}
37
+        self.index_name = {}
38
+        self.index_provider = {}
39
+        self.bouquets = []
40
+        self.bouquets_services = {}
41
+
42
+        self.transp = []
43
+        self.tp_index_tpid = {}
44
+        self.tp_index_pic ={}
45
+
46
+        self._load_services()
47
+        self._load_bouquets()
48
+
49
+    def _load_services(self):
50
+        #tpref - lamedb/transponders
51
+        # NS:TSID:ONID
52
+        #     s FREQ:SR:POLAR:FREC:49:2:0
53
+        #  X    X   X
54
+        #         D    D  D     D
55
+
56
+        # lref - lamedb/services
57
+        #	SID:NS:TSID:ONID:STYPE:UNUSED(channelnumber in enigma1)
58
+        #	0   1  2    3    4     5
59
+        #	X   X  X    X    D     D
60
+        # wref - bouquets/picon
61
+        #   REFTYPE:FLAGS:STYPE:SID:TSID:ONID:NS:PARENT_SID:PARENT_TSID:UNUSED
62
+        #   0       1     2     3   4    5    6  7          8           9
63
+        #   D       D     X     X   X    X    X  X          X           X
64
+
65
+        f_services = open(self.service_fname,"r") if not self.service_fname[0:4] == "ftp:" else urllib2.urlopen(self.service_fname)
66
+
67
+        line = f_services.readline()
68
+        if not "eDVB services" in line:
69
+            raise Exception("no correct lamedb file")
70
+        line = f_services.readline()
71
+
72
+        # Read transponders
73
+        i = 0
74
+        while True:
75
+            line = f_services.readline()
76
+            if  "end" in line: break
77
+            tp = Rec()
78
+            #tp = {}
79
+            ff = line.strip().split(":")
80
+            tp["ns"] = ns = pos_str2int(ff[0])
81
+            tp["tsid"] = tsid = int(ff[1],16)
82
+            tp["onid"] = onid = int(ff[2],16)
83
+            line = f_services.readline()
84
+            tp["params"] = params = line.strip().split(" ")[1]
85
+            ff = params.split(":")
86
+            tp["freq"] = freq = int(ff[0][:-3])
87
+            tp["polar"] = polar = int(ff[2])
88
+            tp["tpid"] = tpid = (ns,tsid,onid)
89
+            self.transp.append(tp)
90
+            self.tp_index_tpid[tpid] = i
91
+            self.tp_index_pic[(ns,freq,polar)] = i
92
+
93
+            line = f_services.readline()
94
+            i += 1
95
+
96
+        # Reading services
97
+        i= 0
98
+        line = f_services.readline()
99
+        while True:
100
+
101
+            line = f_services.readline()
102
+            if line[0:3] == "end": break
103
+            if not line: break
104
+            rec = Rec()
105
+            #rec = {}
106
+            rec["lref"] = line.lower().strip().decode("utf-8")
107
+
108
+            line = f_services.readline()
109
+            #line = line.replace('\xc2\x87', '').replace('\xc2\x86', '')
110
+            #line = decode_charset(line)
111
+            line = line.decode("utf-8")
112
+            rec["name"] = line.strip()
113
+
114
+            line = f_services.readline()
115
+            provider = ""
116
+            caid=[]
117
+            for p in line.split(","):
118
+                if p[0:2].lower() == "p:":
119
+                    provider = p[2:].strip()
120
+                elif p[0:2].lower() == "c:":
121
+                    caid.append(p[2:].strip())
122
+            if provider == "":
123
+                provider = "unknown"
124
+            rec["provider"] = provider.decode("utf-8")
125
+
126
+            rec["sref"] = lref2sref(rec["lref"])
127
+            r = lref_parse(rec["lref"])
128
+            rec["stype"] = r["stype"]
129
+            rec["chid"]  = (r["sid"],r["tsid"],r["onid"])
130
+            rec["caid"] = caid
131
+            self.services.append(rec)
132
+
133
+            self.index_sref[rec["sref"]] = i
134
+            self.index_chid[rec["chid"]] = i
135
+            name = rec["name"].lower()
136
+            if not self.index_name.has_key(name): self.index_name[name] = []
137
+            self.index_name[name].append(i)
138
+            provider = rec["provider"].lower()
139
+            if not self.index_provider.has_key(provider): self.index_provider[provider] = []
140
+            self.index_provider[provider].append(i)
141
+
142
+            i += 1
143
+
144
+        f_services.close()
145
+
146
+    def _load_bouquets(self):
147
+        f_bouquets = open(self.bouquets_fname,"r") if not self.bouquets_fname[0:4] == "ftp:" else urllib2.urlopen(self.bouquets_fname)
148
+        self.bouquets=Rec()
149
+        for line in f_bouquets.readlines():
150
+            if line[0:9] == "#SERVICE:":
151
+                if self.enigma==1:
152
+                    bn = line.strip().split("/")
153
+                else:
154
+                    bn = line.strip().split(":")
155
+                bfn = bn[-1]
156
+                bid = bfn.split(".")[1]
157
+
158
+                ### Process one bouquet file ###
159
+                bouq = Rec()
160
+                bouq.bid = bid
161
+                bouq.name = ""
162
+                bouq.services=[]
163
+                #fb = open(self.fname,"r") if not self.fname[0:4] == "ftp:" else urllib2.urlopen(self.fname)
164
+                fb = open(os.path.join(self.service_path,bfn))
165
+                i= 0
166
+                line = fb.readline()
167
+                while True:
168
+                    if line[0:5] == "#NAME":
169
+                        bouq.name = line.strip()[6:].decode("utf8")
170
+                    elif line[0:8] == "#SERVICE":
171
+                        sref = line.strip()[9:]
172
+                        s = sref_parse(sref)
173
+                        s["sref"] = sref
174
+                        if s["flags"] == 64:
175
+                            s["type"] = "marker"
176
+                            line = fb.readline()
177
+                            if line[0:12] =='#DESCRIPTION':
178
+                                s["name"] = line [13:].strip().decode("utf8")
179
+                            else: continue
180
+                        elif s["url"]: # IPTV stream
181
+                            s["type"] = "stream"
182
+                            s["sref"] = ":".join(s["sref"].split(":")[0:10])+":"
183
+                            line = fb.readline()
184
+                            if line[0:12] =='#DESCRIPTION':
185
+                                s["name"] = line [13:].strip().decode("utf8")
186
+                            else: continue
187
+                        bouq.services.append(s)
188
+                    line = fb.readline()
189
+                    if not line: break
190
+                fb.close()
191
+                self.bouquets[bid] = bouq
192
+
193
+        f_bouquets.close()
194
+
195
+    def _load_bouquets2(self):
196
+        f_bouquets = open(self.bouquets_fname,"r") if not self.bouquets_fname[0:4] == "ftp:" else urllib2.urlopen(self.bouquets_fname)
197
+        for line in f_bouquets.readlines():
198
+            if line[0:9] == "#SERVICE:":
199
+                if self.enigma==1:
200
+                    bn = line.strip().split("/")
201
+                else:
202
+                    bn = line.strip().split(":")
203
+                bfn = bn[-1]
204
+                bid = bfn.split(".")[1]
205
+                bouq = Bouquet(bid, self.service_path)
206
+                self.bouquets.append(bouq)
207
+        f_bouquets.close()
208
+
209
+    def create_bouqet(self,bid,name):
210
+        fname = "userbouquet.%s.tv"%bid
211
+        f_bouquets = open(self.bouquets_fname,"r") if not self.bouquets_fname[0:4] == "ftp:" else urllib2.urlopen(self.bouquets_fname)
212
+        lines = f_bouquets.read().strip()
213
+        f_bouquets.close()
214
+        if not fname in lines:
215
+            lines += "\n#SERVICE: 1:7:1:0:0:0:0:0:0:0:%s\n"%fname
216
+            f_bouquets = open(self.bouquets_fname,"w")
217
+            f_bouquets.write(lines)
218
+            f_bouquets.close()
219
+            with open(os.path.join(self.service_path,fname), 'w') as f:
220
+                f.write("#NAME %s"%name.encode("utf8"))
221
+        pass
222
+
223
+    def get_bouquets(self):
224
+        if not self.bouquets:
225
+            self._load_bouquets()
226
+        return self.bouquets
227
+
228
+    def get_bouquet_services(self,bn):
229
+        if not self.bouquets:
230
+            self._load_bouquets()
231
+        return self.bouquets[bn].services
232
+
233
+    #----------------------------------------------------------------------
234
+    def save_bouquet(self,bn):
235
+        """Save bouquet to file"""
236
+        fn = "userbouquet.%s.tv"%bn
237
+        fb = open(os.path.join(self.service_path,fn),"w")
238
+        fb.write("#NAME %s\n"%self.bouquets[bn].name.encode("utf8"))
239
+        for s in self.bouquets[bn].services:
240
+            if s.type=="marker":
241
+                fb.write("#SERVICE %s\n"%s.sref)
242
+                fb.write("#DESCRIPTION %s\n"%s.name.encode("utf8"))
243
+            elif s.type == "stream":
244
+                fb.write("#SERVICE %s%s:%s\n"%(s.sref,quote(s.url),s.name.encode("utf8")))
245
+                fb.write("#DESCRIPTION %s\n"%s.name.encode("utf8"))
246
+            else:
247
+                fb.write("#SERVICE %s\n"%s.sref)
248
+        fb.close()
249
+
250
+    def get_service_by_sref(self,sref):
251
+        return self.services[self.index_sref[sref]] if self.index_sref.has_key(sref) else None
252
+
253
+    def get_service_by_chid(self,chid):
254
+        return self.services[self.index_chid[chid]] if self.index_chid.has_key(chid) else None
255
+
256
+    def get_service_by_name(self,name):
257
+        return [self.services[i] for i in self.index_name[name]] if name in self.index_name else None
258
+
259
+    def get_service_by_provider(self,provider):
260
+        return [self.services[i] for i in self.index_provider[provider]] if provider in self.index_privider else Non
261
+
262
+    def get_transp_by_tpid(self,tpid):
263
+        return self.transp[self.tp_index_tpid[tpid]] if self.tp_index_tpid.has_key(tpid) else None
264
+
265
+    def get_transp_by_pic(self,picid):
266
+        return self.transp[self.tp_index_pic[picid]] if self.tp_index_pic.has_key(picid) else None
267
+
268
+    def find_sref(ns,freq,polar,sid):
269
+        "Find service reference according to given parameters"
270
+        tp = services.get_transp_by_pic((ns,freq,polar))
271
+        if tp:
272
+            serv = self.services.get_service_by_chid((sid,tp["tsid"],tp["onid"]))
273
+            if serv:
274
+                return serv["sref"]
275
+        return ""
276
+
277
+########################################################################
278
+class Bouquet(object):
279
+    """Bouquet file object"""
280
+
281
+    #----------------------------------------------------------------------
282
+    def __init__(self, bid, service_path=None):
283
+        """Constructor"""
284
+        if not service_path:
285
+            service_path = "enigma2" if sys.platform=="win32" else "/etc/enigma2"
286
+        self.service_path = service_path
287
+        self.bid = bid
288
+        if not "ftp:" in service_path:
289
+            self.fname = os.path.join(service_path, "userbouquet.%s.tv"%self.bid)
290
+        else:
291
+            sep = "" if service_path[-1]=="//" else "//"
292
+            self.fname = os.path.join(service_path, "userbouquet.%s.tv"%self.bid)
293
+        self.load()
294
+
295
+    #----------------------------------------------------------------------
296
+    def load(self):
297
+        """Load bouquet from file"""
298
+        self.services=[]
299
+        fb = open(self.fname,"r") if not self.fname[0:4] == "ftp:" else urllib2.urlopen(self.fname)
300
+        i= 0
301
+        line = fb.readline()
302
+        while True:
303
+            if line[0:5] == "#NAME":
304
+                self.name = line.strip()[6:].decode("utf8")
305
+            elif line[0:8] == "#SERVICE":
306
+                sref = line.strip()[9:]
307
+                s = sref_parse(sref)
308
+                s["sref"] = sref
309
+                if s["flags"] == 64:
310
+                    s["type"] = "marker"
311
+                    line = fb.readline()
312
+                    if line[0:12] =='#DESCRIPTION':
313
+                        s["name"] = line [13:].strip().decode("utf8")
314
+                    else: continue
315
+                elif s["url"]: # IPTV stream
316
+                    s["type"] = "stream"
317
+                    s["sref"] = ":".join(s["sref"].split(":")[0:10])
318
+                    line = fb.readline()
319
+                    if line[0:12] =='#DESCRIPTION':
320
+                        s["name"] = line [13:].strip().decode("utf8")
321
+                    else: continue
322
+                self.services.append(s)
323
+            line = fb.readline()
324
+            if not line: break
325
+        fb.close()
326
+
327
+
328
+
329
+def decode_charset(s):
330
+    u = None
331
+    if isinstance(s,unicode):
332
+        return s
333
+    charset_list=('iso-8859-4','iso-8859-1','iso-8859-2''iso-8859-15')
334
+
335
+    for charset in charset_list:
336
+        try:
337
+            u=unicode(s,charset,"strict")
338
+        except:
339
+            pass
340
+        else:
341
+            break
342
+
343
+    if u == None:
344
+        raise Error, "CHARSET ERROR while decoding lamedb. Aborting !"
345
+    else:
346
+        return(u)
347
+
348
+# lref - lamedb/services
349
+#	SID:NS:TSID:ONID:STYPE:UNUSED(channelnumber in enigma1)
350
+#	0   1  2    3    4     5
351
+#	X   X  X    X    D     D
352
+#
353
+# sref bouquets/picon
354
+#   REFTYPE:FLAGS:STYPE:SID:TSID:ONID:NS:PARENT_SID:PARENT_TSID:UNUSED
355
+#   0       1     2     3   4    5    6  7          8           9
356
+#   D       D     X     X   X    X    X  X          X           X
357
+
358
+def lref2sref(s):
359
+    "Converts service refefence from lamedb format to bouquets/picon (standard) format"
360
+    f = s.split(":")
361
+    sid = int(f[0],16)
362
+    ns = int(f[1], 16)
363
+    tsid = int(f[2],16)
364
+    onid = int(f[3],16)
365
+    stype = int(f[4])
366
+
367
+    s2 = "1:0:%X:%X:%X:%X:%X:0:0:0:" % (stype,sid,tsid,onid,ns)
368
+    return s2
369
+
370
+def lref_parse(s):
371
+    "Parse lamedb/service string, return dictionary of items"
372
+    f = s.split(":")
373
+    #r = {}
374
+    r = Rec()
375
+    r["sid"] = int(f[0],16)
376
+    r["ns"] = int(f[1], 16)
377
+    r["tsid"] = int(f[2],16)
378
+    r["onid"] = int(f[3],16)
379
+    r["stype"] = int(f[4])
380
+    return r
381
+
382
+def sref2lref(s):
383
+    "Converts service refefence from bouquets/picon (standard) format to lamedb format"
384
+    f = s.split(":")
385
+    reftype = int(f[0])
386
+    flags = int(f[1])
387
+    stype = int(f[2],16)
388
+    sid = int(f[3],16)
389
+    tsid = int(f[4],16)
390
+    onid = int(f[5],16)
391
+    ns = int(f[6],16)
392
+
393
+    s2 = "%04x:%08x:%04x:%04x:%i:0" % (sid,ns,tsid,onid,stype)
394
+    return s2
395
+
396
+def sref_parse(s):
397
+    "Parse service refefence string , return dictionary of items"
398
+    f = s.split(":")
399
+    #r = {}
400
+    r = Rec()
401
+    r["reftype"] = int(f[0])
402
+    r["flags"] = int(f[1])
403
+    r["stype"] = int(f[2],16)
404
+    r["sid"] = int(f[3],16)
405
+    r["tsid"] = int(f[4],16)
406
+    r["onid"] = int(f[5],16)
407
+    r["ns"] = int(f[6],16)
408
+    r["url"] = unquote(f[10]) if len(f)>10 else ""
409
+    return r
410
+
411
+pos_int2str = lambda pos_num: "%04x0000"%pos_num
412
+pos_str2int = lambda pos: int(pos[0:4],16)
413
+
414
+def decode_charset(s):
415
+    u = None
416
+    charset_list = ('utf-8','iso-8859-1','iso-8859-2','iso-8859-15')
417
+
418
+    for charset in charset_list:
419
+        try:
420
+            u = unicode(s,charset,"strict")
421
+        except:
422
+            pass
423
+        else:
424
+            break
425
+
426
+    if u == None:
427
+        print("CHARSET ERROR while decoding string")
428
+        sys.exit(1)
429
+    else:
430
+        return(u)
431
+
432
+import UserDict
433
+
434
+if __name__ == "__main__":
435
+    #print "** Starting..."
436
+    #sys.exit(main(sys.argv))
437
+    e2 = DBServices()
438
+    #e2.save_buquet("english")
439
+    e2.create_bouqet("test", "Tests")
440
+    pass

+ 29
- 0
locale/PlayStream.pot View File

@@ -0,0 +1,29 @@
1
+# SOME DESCRIPTIVE TITLE.
2
+# Copyright (C) YEAR ORGANIZATION
3
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4
+#
5
+msgid ""
6
+msgstr ""
7
+"Project-Id-Version: PACKAGE VERSION\n"
8
+"POT-Creation-Date: 2016-04-24 16:41+FLE Daylight Time\n"
9
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
10
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11
+"Language-Team: LANGUAGE <LL@li.org>\n"
12
+"MIME-Version: 1.0\n"
13
+"Content-Type: text/plain; charset=CHARSET\n"
14
+"Content-Transfer-Encoding: ENCODING\n"
15
+"Generated-By: pygettext.py 1.5\n"
16
+
17
+
18
+#: PlayStream.py:83
19
+msgid "Back"
20
+msgstr ""
21
+
22
+#: PlayStream.py:84
23
+msgid "Select"
24
+msgstr ""
25
+
26
+#: plugin.py:16 plugin.py:17
27
+msgid "Play online streams from various sources"
28
+msgstr ""
29
+

+ 30
- 0
locale/lv.po View File

@@ -0,0 +1,30 @@
1
+# SOME DESCRIPTIVE TITLE.
2
+# Copyright (C) YEAR ORGANIZATION
3
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4
+#
5
+msgid ""
6
+msgstr ""
7
+"Project-Id-Version: \n"
8
+"POT-Creation-Date: 2016-04-24 16:43+0300\n"
9
+"PO-Revision-Date: 2016-04-24 16:45+0300\n"
10
+"Language-Team: \n"
11
+"MIME-Version: 1.0\n"
12
+"Content-Type: text/plain; charset=UTF-8\n"
13
+"Content-Transfer-Encoding: 8bit\n"
14
+"Generated-By: pygettext.py 1.5\n"
15
+"X-Generator: Poedit 1.8.7\n"
16
+"Last-Translator: \n"
17
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n"
18
+"Language: lv\n"
19
+
20
+#: PlayStream.py:83
21
+msgid "Back"
22
+msgstr "Atpakaļ"
23
+
24
+#: PlayStream.py:84
25
+msgid "Select"
26
+msgstr "Izvēlies"
27
+
28
+#: plugin.py:16 plugin.py:17
29
+msgid "Play online streams from various sources"
30
+msgstr "Dažādu online plūsmu spēlēšana no dažādiem avotiem"

+ 30
- 0
locale/ru.po View File

@@ -0,0 +1,30 @@
1
+# SOME DESCRIPTIVE TITLE.
2
+# Copyright (C) YEAR ORGANIZATION
3
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4
+#
5
+msgid ""
6
+msgstr ""
7
+"Project-Id-Version: \n"
8
+"POT-Creation-Date: 2016-04-24 16:47+0300\n"
9
+"PO-Revision-Date: 2016-04-24 16:48+0300\n"
10
+"Language-Team: \n"
11
+"MIME-Version: 1.0\n"
12
+"Content-Type: text/plain; charset=UTF-8\n"
13
+"Content-Transfer-Encoding: 8bit\n"
14
+"Generated-By: pygettext.py 1.5\n"
15
+"X-Generator: Poedit 1.8.7\n"
16
+"Last-Translator: \n"
17
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
18
+"Language: ru\n"
19
+
20
+#: PlayStream.py:83
21
+msgid "Back"
22
+msgstr "Назад"
23
+
24
+#: PlayStream.py:84
25
+msgid "Select"
26
+msgstr "Выбрать"
27
+
28
+#: plugin.py:16 plugin.py:17
29
+msgid "Play online streams from various sources"
30
+msgstr "Играть онлайн потоки из различных источников"

BIN
offline.mp4 View File


BIN
picons/cinemalive.png View File


BIN
picons/latvia1.png View File


BIN
picons/latvia7.png View File


BIN
picons/lr_1_lv.png View File


BIN
picons/lr_2_lv.png View File


BIN
picons/lr_3_lv.png View File


BIN
picons/shortcut.png View File


BIN
release/enigma2-plugin-extensions-playstream_0.5f.ipk View File


+ 140
- 0
resolver.py View File

@@ -0,0 +1,140 @@
1
+# *      Copyright (C) 2011 Libor Zoubek
2
+# *      Modified by ivars777
3
+# *      Based in code from https://github.com/kodi-czsk/script.module.stream.resolver
4
+# *
5
+# *
6
+# *  This Program is free software; you can redistribute it and/or modify
7
+# *  it under the terms of the GNU General Public License as published by
8
+# *  the Free Software Foundation; either version 2, or (at your option)
9
+# *  any later version.
10
+# *
11
+# *  This Program is distributed in the hope that it will be useful,
12
+# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+# *  GNU General Public License for more details.
15
+# *
16
+# *  You should have received a copy of the GNU General Public License
17
+# *  along with this program; see the file COPYING.  If not, write to
18
+# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
+# *  http://www.gnu.org/copyleft/gpl.html
20
+# *
21
+# */
22
+# Based in code from https://github.com/kodi-czsk/script.module.stream.resolver
23
+
24
+import re
25
+import traceback
26
+
27
+import util
28
+import sys,os
29
+#sys.path.insert(0,os.path.dirname(os.path.abspath(__file__)))
30
+server_path = "resolvers"
31
+sys.path.append(os.path.join(os.path.dirname(__file__), server_path))
32
+
33
+RESOLVERS = []
34
+util.debug('%s searching for modules' % __name__)
35
+
36
+for module in os.listdir(os.path.join(os.path.dirname(__file__), server_path)):
37
+    if module == '__init__.py' or module[-3:] != '.py':
38
+        continue
39
+    module = module[:-3]
40
+    #exec 'import %s' % module
41
+    #resolver = eval(module)
42
+    resolver = __import__(module)
43
+    #reload(resolver)
44
+    
45
+    if not hasattr(resolver, 'resolve'):
46
+        continue
47
+    print 'found %s %s' % (resolver, dir(resolver))
48
+    #util.debug('found %s %s' % (resolver, dir(resolver)))
49
+    if not hasattr(resolver, '__priority__'):
50
+        resolver.__priority__ = 0
51
+    RESOLVERS.append(resolver)
52
+    del module
53
+RESOLVERS = sorted(RESOLVERS, key=lambda m: -m.__priority__)
54
+
55
+def item():
56
+    stream0 = {'name': '', 'url': '', 'quality': '???', 'surl': '', 'subs': '', 'headers': {},"desc":"","img":"","lang":"","type":"","order":0}            
57
+    return stream0
58
+
59
+def resolve(url):
60
+    """
61
+        resolves given url by asking all resolvers
62
+
63
+        returns None if no resolver advised to be able to resolve this url
64
+        returns False if resolver did his job, but did not return any value (thus failed)
65
+        returns Array of resolved objects in positive usecase
66
+    """
67
+    url = util.decode_html(url)
68
+    util.info('Resolving ' + url)
69
+    resolver = _get_resolver(url)
70
+    value = None
71
+    if resolver is None:
72
+        return None
73
+    util.info('Using resolver \'%s\'' % str(resolver.__name__));
74
+    value = resolver.resolve(url)
75
+    if value is None:
76
+        return False
77
+    default = item()
78
+    for i in value:
79
+        if 'name' not in i.keys():
80
+            i['name'] = resolver.__name__
81
+        if 'surl' not in i.keys():
82
+            i['surl'] = url
83
+        for key in default.keys():
84
+            if key not in i.keys():
85
+                i[key] = default[key]   
86
+        if "|" in i["url"]:
87
+            headers = i["url"].split("|")[1]
88
+            i["url"]=i["url"].split("|")[0]
89
+            for h in headers.split("&"):
90
+                if "=" in h:
91
+                    i["headers"][h.split("=")[0]] = h.split("=")[1]
92
+
93
+    return sorted(value, key=lambda i: i['quality'])
94
+
95
+
96
+def _get_resolver(url):
97
+    util.debug('Get resolver for ' + url)
98
+    for r in RESOLVERS:
99
+        util.debug('querying %s' % r)
100
+        if r.supports(url):
101
+            return r
102
+
103
+def can_resolve(url):
104
+    """ Returns true if we are able to resolve stream by given URL """
105
+    return _get_resolver(url) is not None
106
+
107
+
108
+if __name__ == "__main__":
109
+
110
+    from subprocess import call
111
+    #url = "http://hqq.tv/player/embed_player.php?vid=235238210241210222228241233208212245&autoplay=no"
112
+    #url = "http://hqq.tv/player/embed_player.php?vid=243221241234244238208213206212211231&autoplay=no"
113
+    url = "http://hqq.tv/player/embed_player.php?vid=208231211231207221227243206206221244&autoplay=no"
114
+    #url = "https://openload.co/embed/TMthIdpy4PI/"
115
+    #url = "https://www.youtube.com/watch?v=Tx1K51_F99o"
116
+    #url = "https://www.youtube.com/watch?v=8BkcX7O1890"
117
+    #url = "https://www.youtube.com/watch?v=Se07R8SYsg0"
118
+    #url = "https://kinostok.tv/embed/731f3437e3c53104dd56d04039a0b15a"
119
+    #url = "http://vk.com/video_ext.php?oid=246066565&id=169244575&hash=d430ab0e76c9f7a1&hd=3"
120
+    #url ="https://openload.co/embed/rPMXJYPTkw4/"
121
+    #url = "https://openload.co/embed/bE7WfZ-vz_A/" 
122
+    #url = "https://openload.co/embed/bE7WfZ/" 
123
+    #url = "https://openload.co/embed/OuskaKyC2GU/"
124
+    url = "http://hqq.tv/player/embed_player.php?vid=235238210241210222228241233208212245&autoplay=no"
125
+    url = "https://openload.co/embed/rmNcP-0QopE/"
126
+    url = "https://openload.co/embed/gpLF6Grzy80/"
127
+    url = "https://openload.co/embed/oQLXcU1ITAY/"
128
+    url = "http://hqq.tv/player/embed_player.php?vid=245208228234224222241224221239207212&autoplay=no"
129
+    streams = resolve(url)
130
+    if not streams:
131
+        print "No streams found"
132
+        sys.exit()
133
+
134
+    for s in streams:
135
+        print s
136
+
137
+    print streams[0]["url"] 
138
+    util.play_video(streams)
139
+    ##call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",streams[0]["url"]])
140
+    pass

+ 31
- 0
resolvers/__init__.py View File

@@ -0,0 +1,31 @@
1
+#/*
2
+# *      Copyright (C) 2011 Libor Zoubek
3
+# *
4
+# *
5
+# *  This Program is free software; you can redistribute it and/or modify
6
+# *  it under the terms of the GNU General Public License as published by
7
+# *  the Free Software Foundation; either version 2, or (at your option)
8
+# *  any later version.
9
+# *
10
+# *  This Program is distributed in the hope that it will be useful,
11
+# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+# *  GNU General Public License for more details.
14
+# *
15
+# *  You should have received a copy of the GNU General Public License
16
+# *  along with this program; see the file COPYING.  If not, write to
17
+# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18
+# *  http://www.gnu.org/copyleft/gpl.html
19
+# *
20
+# */
21
+
22
+
23
+##########################################################3
24
+# all resolvers modules in this directory must have following methods:
25
+
26
+# __name__ - name of the resolver module - can override module filename
27
+# def supports(url) - returns true iff resolver is able to resolve url to stream otherwise false
28
+# def resolve(url) - returns array of all hashmaps that were resolved
29
+#   - if resolving fails, nothing is returned
30
+#   - a hash MUST contain key 'url' - it's value is stream URL
31
+#   - optional keys are 'subs' (link to subtitle), 'quality' (quality string like '240p' or just 'HD'

+ 209
- 0
resolvers/aadecode.py View File

@@ -0,0 +1,209 @@
1
+#-*- coding: utf-8 -*-
2
+#
3
+# author : Djeman
4
+# Updated by Shani-08 (https://github.com/Shani-08/ShaniXBMCWork2)
5
+
6
+import re
7
+
8
+class AADecoder(object):
9
+    def __init__(self, aa_encoded_data):
10
+        self.encoded_str = aa_encoded_data.replace('/*´∇`*/','')
11
+
12
+        self.b = ["(c^_^o)", "(゚Θ゚)", "((o^_^o) - (゚Θ゚))", "(o^_^o)",
13
+                  "(゚ー゚)", "((゚ー゚) + (゚Θ゚))", "((o^_^o) +(o^_^o))", "((゚ー゚) + (o^_^o))",
14
+                  "((゚ー゚) + (゚ー゚))", "((゚ー゚) + (゚ー゚) + (゚Θ゚))", "(゚Д゚) .゚ω゚ノ", "(゚Д゚) .゚Θ゚ノ",
15
+                  "(゚Д゚) ['c']", "(゚Д゚) .゚ー゚ノ", "(゚Д゚) .゚Д゚ノ", "(゚Д゚) [゚Θ゚]"]
16
+
17
+    def is_aaencoded(self):
18
+        idx = self.encoded_str.find("゚ω゚ノ= /`m´)ノ ~┻━┻   //*´∇`*/ ['_']; o=(゚ー゚)  =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); ")
19
+        if idx == -1:
20
+            return False
21
+
22
+        if self.encoded_str.find("(゚Д゚)[゚o゚]) (゚Θ゚)) ('_');", idx) == -1:
23
+            return False
24
+
25
+        return True
26
+
27
+    def base_repr(self, number, base=2, padding=0):
28
+        digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
29
+        if base > len(digits):
30
+            base = len(digits)
31
+
32
+        num = abs(number)
33
+        res = []
34
+        while num:
35
+            res.append(digits[num % base])
36
+            num //= base
37
+        if padding:
38
+            res.append('0' * padding)
39
+        if number < 0:
40
+            res.append('-')
41
+        return ''.join(reversed(res or '0'))
42
+
43
+    def decode_char(self, enc_char, radix):
44
+        end_char = "+ "
45
+        str_char = ""
46
+        while enc_char != '':
47
+            found = False
48
+
49
+            if not found:
50
+                for i in range(len(self.b)):             
51
+                    enc_char=enc_char.replace(self.b[i], str(i))
52
+                
53
+                startpos=0
54
+                findClose=True
55
+                balance=1
56
+                result=[]
57
+                if enc_char.startswith('('):
58
+                    l=0
59
+                    
60
+                    for t in enc_char[1:]:
61
+                        l+=1
62
+                        if findClose and t==')':
63
+                            balance-=1;
64
+                            if balance==0:
65
+                                result+=[enc_char[startpos:l+1]]
66
+                                findClose=False
67
+                                continue
68
+                        elif not findClose and t=='(':
69
+                            startpos=l
70
+                            findClose=True
71
+                            balance=1
72
+                            continue
73
+                        elif t=='(':
74
+                            balance+=1
75
+                 
76
+
77
+                if result is None or len(result)==0:
78
+                    return ""
79
+                else:
80
+                    
81
+                    for r in result:
82
+                        value = self.decode_digit(r, radix)
83
+                        if value == "":
84
+                            return ""
85
+                        else:
86
+                            str_char += value
87
+                            
88
+                    return str_char
89
+
90
+            enc_char = enc_char[len(end_char):]
91
+
92
+        return str_char
93
+
94
+        
95
+              
96
+    def decode_digit(self, enc_int, radix):
97
+
98
+        rr = '(\(.+?\)\))\+'
99
+        rerr=enc_int.split('))+')
100
+        v = ''
101
+        
102
+        #new mode
103
+
104
+        for c in rerr:
105
+            
106
+            if len(c)>0:
107
+                if c.strip().endswith('+'):
108
+                    c=c.strip()[:-1]
109
+
110
+                startbrackets=len(c)-len(c.replace('(',''))
111
+                endbrackets=len(c)-len(c.replace(')',''))
112
+                    
113
+                if startbrackets>endbrackets:
114
+                    c+=')'*startbrackets-endbrackets
115
+                    
116
+                c = c.replace('!+[]','1')
117
+                c = c.replace('-~','1+')
118
+                c = c.replace('[]','0')
119
+                    
120
+                v+=str(eval(c))
121
+                    
122
+        return v
123
+         
124
+        mode = 0
125
+        value = 0
126
+
127
+        while enc_int != '':
128
+            found = False
129
+            for i in range(len(self.b)):
130
+                if enc_int.find(self.b[i]) == 0:
131
+                    if mode == 0:
132
+                        value += i
133
+                    else:
134
+                        value -= i
135
+                    enc_int = enc_int[len(self.b[i]):]
136
+                    found = True
137
+                    break
138
+
139
+            if not found:
140
+                return ""
141
+
142
+            enc_int = re.sub('^\s+|\s+$', '', enc_int)
143
+            if enc_int.find("+") == 0:
144
+                mode = 0
145
+            else:
146
+                mode = 1
147
+
148
+            enc_int = enc_int[1:]
149
+            enc_int = re.sub('^\s+|\s+$', '', enc_int)
150
+
151
+        return self.base_repr(value, radix)
152
+
153
+    def decode(self):
154
+
155
+        self.encoded_str = re.sub('^\s+|\s+$', '', self.encoded_str)
156
+
157
+        # get data
158
+        pattern = (r"\(゚Д゚\)\[゚o゚\]\+ (.+?)\(゚Д゚\)\[゚o゚\]\)")
159
+        result = re.search(pattern, self.encoded_str, re.DOTALL)
160
+        if result is None:
161
+            print "AADecoder: data not found"
162
+            return False
163
+
164
+        data = result.group(1)
165
+
166
+        # hex decode string
167
+        begin_char = "(゚Д゚)[゚ε゚]+"
168
+        alt_char = "(o゚ー゚o)+ "
169
+
170
+        out = ''
171
+
172
+        while data != '':
173
+            # Check new char
174
+            if data.find(begin_char) != 0:
175
+                print "AADecoder: data not found"
176
+                return False
177
+
178
+            data = data[len(begin_char):]
179
+
180
+            # Find encoded char
181
+            enc_char = ""
182
+            if data.find(begin_char) == -1:
183
+                enc_char = data
184
+                data = ""
185
+            else:
186
+                enc_char = data[:data.find(begin_char)]
187
+                data = data[len(enc_char):]
188
+
189
+            
190
+            radix = 8
191
+            # Detect radix 16 for utf8 char
192
+            if enc_char.find(alt_char) == 0:
193
+                enc_char = enc_char[len(alt_char):]
194
+                radix = 16
195
+
196
+            str_char = self.decode_char(enc_char, radix)
197
+            
198
+            if str_char == "":
199
+                print "no match :  "
200
+                print  data + "\nout = " + out + "\n"
201
+                return False
202
+            
203
+            out += chr(int(str_char, radix))
204
+
205
+        if out == "":
206
+            print "no match : " + data
207
+            return False
208
+
209
+        return out

+ 95
- 0
resolvers/hdgo.py View File

@@ -0,0 +1,95 @@
1
+# -*- coding: UTF-8 -*-
2
+# /*
3
+# *      Copyright (C) 2016 ivars777
4
+# *
5
+# *
6
+# *  This Program is free software; you can redistribute it and/or modify
7
+# *  it under the terms of the GNU General Public License as published by
8
+# *  the Free Software Foundation; either version 2, or (at your option)
9
+# *  any later version.
10
+# *
11
+# *  This Program is distributed in the hope that it will be useful,
12
+# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+# *  GNU General Public License for more details.
15
+# *
16
+# *  You should have received a copy of the GNU General Public License
17
+# *  along with this program; see the file COPYING.  If not, write to
18
+# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
+# *  http://www.gnu.org/copyleft/gpl.html
20
+# *
21
+# */
22
+
23
+import re,os,sys
24
+import json
25
+try:
26
+    import util
27
+except:
28
+    pp = os.path.dirname(os.path.abspath(__file__))
29
+    sys.path.insert(0,os.sep.join(pp.split(os.sep)[:-1]))
30
+    import util
31
+import urllib2
32
+import requests
33
+#from aadecode import AADecoder
34
+
35
+if __name__ <> "__main__":
36
+    __name__ = 'hqq'
37
+
38
+def supports(url):
39
+    m = re.search(r"https?://hdgo\.\w+/(.+?)$", url, re.DOTALL)
40
+    if m:
41
+        return True
42
+    else:
43
+        return False
44
+
45
+def resolve(url):
46
+    HTTP_HEADER = {
47
+        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0',
48
+        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
49
+        'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
50
+        'Accept-Encoding': 'none',
51
+        'Accept-Language': 'en-US,en;q=0.8',
52
+        'Referer': url}  # 'Connection': 'keep-alive'
53
+    streams = []
54
+    m = re.search(r"https?://hdgo\.\w+/(.+?)$", url, re.DOTALL)
55
+    vid=m.group(1)
56
+    url2 = "http://couber.be/"+vid
57
+    r = requests.get(url2,headers=HTTP_HEADER)
58
+    if r.status_code <> 200:
59
+        return streams
60
+    m = re.search('<iframe src="([^"]+)"', r.content, re.DOTALL)
61
+    if not m: return streams
62
+    url3 = m.group(1)
63
+    HTTP_HEADER["Rererer"] = url2
64
+    r = requests.get(url3,headers=HTTP_HEADER)
65
+    m = re.search(r"else{\s+setFlash\('([^']+)'\);", r.content, re.DOTALL)
66
+    if not m: return streams
67
+    q = ["1080p","720p","480p","360p"]
68
+    for i,ss in enumerate(m.group(1).split(",")):
69
+        s = ss.split(" or ")
70
+        if not s[0]: continue
71
+        stream = util.item()
72
+        stream["url"] = s[0]
73
+        stream["name"] = s[0]
74
+        stream["quality"] = q[i]
75
+        streams.append(stream)
76
+    return streams
77
+
78
+
79
+if __name__ == "__main__":
80
+
81
+    from subprocess import call
82
+    url = "http://hdgo.cc/video/t/Qrz0riUvA65GtkTpDvmlD9TBOn56HSm2/127280/"
83
+    url = "http://hdgo.cc/video/t/Qrz0riUvA65GtkTpDvmlD9TBOn56HSm2/34879/"
84
+    streams = resolve(url)
85
+    if not streams:
86
+        print "No streams found"
87
+        sys.exit()
88
+    for s in streams:
89
+        print s
90
+    util.play_video(streams)
91
+
92
+
93
+    #print streams[0]["url"]    
94
+    #call([r"gst-launch-1.0.exe",'uri="%s""'%streams[0]["url"]])
95
+    pass

+ 186
- 0
resolvers/hqqresolver.py View File

@@ -0,0 +1,186 @@
1
+# -*- coding: UTF-8 -*-
2
+# *  GNU General Public License for more details.
3
+# *
4
+# *
5
+# *  You should have received a copy of the GNU General Public License
6
+# *  along with this program; see the file COPYING.  If not, write to
7
+# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
8
+# *  http://www.gnu.org/copyleft/gpl.html
9
+# *
10
+# *
11
+# *  based on https://gitorious.org/iptv-pl-dla-openpli/ urlresolver
12
+# */
13
+from StringIO import StringIO
14
+import json
15
+import re
16
+import util
17
+import base64
18
+import urllib
19
+
20
+__name__ = 'hqq'
21
+
22
+
23
+def supports(url):
24
+    #return False
25
+    return _regex(url) is not None
26
+
27
+
28
+def _decode(data):
29
+    def O1l(string):
30
+        ret = ""
31
+        i = len(string) - 1
32
+        while i >= 0:
33
+            ret += string[i]
34
+            i -= 1
35
+        return ret
36
+
37
+    def l0I(string):
38
+        enc = ""
39
+        dec = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
40
+        i = 0
41
+        while True:
42
+            h1 = dec.find(string[i])
43
+            i += 1
44
+            h2 = dec.find(string[i])
45
+            i += 1
46
+            h3 = dec.find(string[i])
47
+            i += 1
48
+            h4 = dec.find(string[i])
49
+            i += 1
50
+            bits = h1 << 18 | h2 << 12 | h3 << 6 | h4
51
+            o1 = bits >> 16 & 0xff
52
+            o2 = bits >> 8 & 0xff
53
+            o3 = bits & 0xff
54
+            if h3 == 64:
55
+                enc += unichr(o1)
56
+            else:
57
+                if h4 == 64:
58
+                    enc += unichr(o1) + unichr(o2)
59
+                else:
60
+                    enc += unichr(o1) + unichr(o2) + unichr(o3)
61
+            if i >= len(string):
62
+                break
63
+        return enc
64
+
65
+    escape = re.search("var _escape=\'([^\']+)", l0I(O1l(data))).group(1)
66
+    return escape.replace('%', '\\').decode('unicode-escape')
67
+
68
+
69
+def _decode2(file_url):
70
+    def K12K(a, typ='b'):
71
+        codec_a = ["G", "L", "M", "N", "Z", "o", "I", "t", "V", "y", "x", "p", "R", "m", "z", "u",
72
+                   "D", "7", "W", "v", "Q", "n", "e", "0", "b", "="]
73
+        codec_b = ["2", "6", "i", "k", "8", "X", "J", "B", "a", "s", "d", "H", "w", "f", "T", "3",
74
+                   "l", "c", "5", "Y", "g", "1", "4", "9", "U", "A"]
75
+        if 'd' == typ:
76
+            tmp = codec_a
77
+            codec_a = codec_b
78
+            codec_b = tmp
79
+        idx = 0
80
+        while idx < len(codec_a):
81
+            a = a.replace(codec_a[idx], "___")
82
+            a = a.replace(codec_b[idx], codec_a[idx])
83
+            a = a.replace("___", codec_b[idx])
84
+            idx += 1
85
+        return a
86
+
87
+    def _xc13(_arg1):
88
+        _lg27 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
89
+        _local2 = ""
90
+        _local3 = [0, 0, 0, 0]
91
+        _local4 = [0, 0, 0]
92
+        _local5 = 0
93
+        while _local5 < len(_arg1):
94
+            _local6 = 0
95
+            while _local6 < 4 and (_local5 + _local6) < len(_arg1):
96
+                _local3[_local6] = _lg27.find(_arg1[_local5 + _local6])
97
+                _local6 += 1
98
+            _local4[0] = ((_local3[0] << 2) + ((_local3[1] & 48) >> 4))
99
+            _local4[1] = (((_local3[1] & 15) << 4) + ((_local3[2] & 60) >> 2))
100
+            _local4[2] = (((_local3[2] & 3) << 6) + _local3[3])
101
+
102
+            _local7 = 0
103
+            while _local7 < len(_local4):
104
+                if _local3[_local7 + 1] == 64:
105
+                    break
106
+                _local2 += chr(_local4[_local7])
107
+                _local7 += 1
108
+            _local5 += 4
109
+        return _local2
110
+
111
+    return _xc13(K12K(file_url, 'e'))
112
+
113
+
114
+def resolve(url):
115
+    m = _regex(url)
116
+    if m:
117
+        vid = m.group('vid')
118
+        headers = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
119
+                   'Content-Type': 'text/html; charset=utf-8'}
120
+        player_url = "http://hqq.tv/player/embed_player.php?vid=%s&autoplay=no" % vid
121
+        data = util.request(player_url, headers)
122
+        b64enc = re.search('base64([^\"]+)', data, re.DOTALL)
123
+        b64dec = b64enc and base64.decodestring(b64enc.group(1))
124
+        enc = b64dec and re.search("\'([^']+)\'", b64dec).group(1)
125
+        if enc:
126
+            data = re.findall('<input name="([^"]+?)" [^>]+? value="([^"]+?)">', _decode(enc))
127
+            post_data = {}
128
+            for idx in range(len(data)):
129
+                post_data[data[idx][0]] = data[idx][1]
130
+            data = util.post(player_url, post_data, headers)
131
+            b64enc = re.search('base64([^\"]+)', data, re.DOTALL)
132
+            b64dec = b64enc and base64.decodestring(b64enc.group(1))
133
+            enc = b64dec and re.search("\'([^']+)\'", b64dec).group(1)
134
+            if enc:
135
+                data = re.findall('<input name="([^"]+?)" [^>]+? value="([^"]*)">', _decode(enc))
136
+                post_data = {}
137
+                for idx in range(len(data)):
138
+                    post_data[data[idx][0]] = data[idx][1]
139
+                data = urllib.unquote(util.request("http://hqq.tv/sec/player/embed_player.php?" +
140
+                                                   urllib.urlencode(post_data), headers))
141
+                server_1 = re.search("server_1: (\w+)",data).group(1)
142
+                link_1 = re.search("link_1: (\w+)",data).group(1)
143
+                vid_server = re.search(r'var\s*%s\s*=\s*"([^"]*?)"'%server_1, data)
144
+                vid_link = re.search(r'var\s*%s\s*=\s*"([^"]*?)"'%link_1, data)
145
+                at = re.search(r'var\s*at\s*=\s*"([^"]*?)"', data)
146
+                vid = re.search('vid: "([^"]+)"',data)
147
+                if vid_server and vid_link and at:
148
+                    get_data = {'server_1': vid_server.group(1),
149
+                                'link_1': vid_link.group(1),
150
+                                'at': at.group(1),
151
+                                'adb': '0/',
152
+                                'b':'1',
153
+                                'vid': vid.group(1)}
154
+                    # X-Requested-With: XMLHttpRequest
155
+                    headers["X-Requested-With"] = "XMLHttpRequest"
156
+                    html = util.request("http://hqq.tv/player/get_md5.php?"+urllib.urlencode(get_data), headers)
157
+                    data = json.load(StringIO(html))
158
+                    if 'file' in data:
159
+                        file_url = _decode2(data['file'])
160
+                        file_url = re.sub(r'\?socket=?$', '.mp4.m3u8',file_url)
161
+                        return [{'url': file_url, 'quality': '360p',"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"}}]
162
+    return None
163
+
164
+
165
+def _regex(url):
166
+    match = re.search("(hqq|netu)\.tv/watch_video\.php\?v=(?P<vid>[0-9A-Z]+)", url)
167
+    if match:
168
+        return match
169
+    match = re.search(r'(hqq|netu)\.tv/player/embed_player\.php\?vid=(?P<vid>[0-9A-Z]+)', url)
170
+    if match:
171
+        return match
172
+    match = re.search(r'(hqq|netu)\.tv/player/hash\.php\?hash=\d+', url)
173
+    if match:
174
+        match = re.search(r'var\s+vid\s*=\s*\'(?P<vid>[^\']+)\'', urllib.unquote(util.request(url)))
175
+        if match:
176
+            return match
177
+    b64enc = re.search(r'data:text/javascript\;charset\=utf\-8\;base64([^\"]+)', url)
178
+    b64dec = b64enc and base64.decodestring(b64enc.group(1))
179
+    enc = b64dec and re.search(r"\'([^']+)\'", b64dec).group(1)
180
+    if enc:
181
+        decoded = _decode(enc)
182
+        match = re.search(r'<input name="vid"[^>]+? value="(?P<vid>[^"]+?)">', decoded)
183
+        if re.search(r'<form(.+?)action="[^"]*(hqq|netu)\.tv/player/embed_player\.php"[^>]*>',
184
+                     decoded) and match:
185
+            return match
186
+    return None

+ 109
- 0
resolvers/openload3.py View File

@@ -0,0 +1,109 @@
1
+# -*- coding: UTF-8 -*-
2
+# /*
3
+# *      Copyright (C) 2015 Lubomir Kucera
4
+# *
5
+# *
6
+# *  This Program is free software; you can redistribute it and/or modify
7
+# *  it under the terms of the GNU General Public License as published by
8
+# *  the Free Software Foundation; either version 2, or (at your option)
9
+# *  any later version.
10
+# *
11
+# *  This Program is distributed in the hope that it will be useful,
12
+# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+# *  GNU General Public License for more details.
15
+# *
16
+# *  You should have received a copy of the GNU General Public License
17
+# *  along with this program; see the file COPYING.  If not, write to
18
+# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
+# *  http://www.gnu.org/copyleft/gpl.html
20
+# *
21
+# */
22
+
23
+import re,os,sys
24
+import json
25
+try:
26
+    import util
27
+except:
28
+    pp = os.path.dirname(os.path.abspath(__file__))
29
+    sys.path.insert(0,os.sep.join(pp.split(os.sep)[:-1]))
30
+    import util
31
+import urllib2
32
+import requests
33
+#from aadecode import AADecoder
34
+
35
+__author__ = 'Jose Riha/Lubomir Kucera'
36
+__name__ = 'openload3'
37
+
38
+
39
+def supports(url):
40
+    return re.search(r'openload\.\w+/embed/.+', url) is not None
41
+
42
+
43
+#INFO_URL = API_BASE_URL + '/streaming/info'
44
+
45
+def resolve(url):
46
+    HTTP_HEADER = {
47
+        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0',
48
+        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
49
+        'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
50
+        'Accept-Encoding': 'none',
51
+        'Accept-Language': 'en-US,en;q=0.8',
52
+        'Referer': url}  # 'Connection': 'keep-alive'    
53
+
54
+    stream = util.item()
55
+    m = re.search('https*://openload\.\w+/embed/([^/]+)', url)
56
+    if not m:
57
+        return stream
58
+    vid=m.group(1)
59
+    url2 = "https://api.openload.co/1/streaming/get?file="+vid
60
+    r = requests.get(url2,headers=HTTP_HEADER)
61
+    try:
62
+        js = json.loads(r.content)
63
+    except:
64
+        return stream
65
+    if js["status"] <>200:
66
+        raise Exception(js["msg"])
67
+    res = js["result"]
68
+    stream["url"] = res["url"]
69
+    stream["name"]= res["url"]
70
+    ### Retrieve subtitles ####
71
+    html = requests.get(url, headers=HTTP_HEADER).content 
72
+    m = re.search('<track kind="captions" src="([^"]+)" srclang="([^"]+)" label="([^"]+)"', html)
73
+    if m:
74
+        stream["subs"] = m.group(1)
75
+        stream["lang"] = m.group(2)   
76
+
77
+    return [stream]
78
+
79
+
80
+if __name__ == "__main__":
81
+
82
+    from subprocess import call
83
+    #url = "http://hqq.tv/player/embed_player.php?vid=235238210241210222228241233208212245&autoplay=no"
84
+    #url = "http://hqq.tv/player/embed_player.php?vid=243221241234244238208213206212211231&autoplay=no"
85
+    url = "http://hqq.tv/player/embed_player.php?vid=208231211231207221227243206206221244&autoplay=no"
86
+    #url = "https://openload.co/embed/TMthIdpy4PI/"
87
+    #url = "https://www.youtube.com/watch?v=Tx1K51_F99o"
88
+    #url = "https://www.youtube.com/watch?v=8BkcX7O1890"
89
+    #url = "https://www.youtube.com/watch?v=Se07R8SYsg0"
90
+    #url = "https://kinostok.tv/embed/731f3437e3c53104dd56d04039a0b15a"
91
+    #url = "http://vk.com/video_ext.php?oid=246066565&id=169244575&hash=d430ab0e76c9f7a1&hd=3"
92
+    #url ="https://openload.co/embed/rPMXJYPTkw4/"
93
+    #url = "https://openload.co/embed/bE7WfZ-vz_A/" 
94
+    #url = "https://openload.co/embed/bE7WfZ/" 
95
+    #url = "https://openload.co/embed/OuskaKyC2GU/"
96
+    url = "http://hqq.tv/player/embed_player.php?vid=235238210241210222228241233208212245&autoplay=no"
97
+    url = "https://openload.co/embed/rmNcP-0QopE/"
98
+    url = "https://openload.co/embed/oQLXcU1ITAY/"
99
+    streams = resolve(url)
100
+    if not streams:
101
+        print "No streams found"
102
+        sys.exit()
103
+
104
+    for s in streams:
105
+        print s
106
+
107
+    print streams[0]["url"]    
108
+    call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",streams[0]["url"]])
109
+    pass

+ 347
- 0
resolvers/youtuberesolver.py View File

@@ -0,0 +1,347 @@
1
+# -*- coding: UTF-8 -*-
2
+
3
+import urllib2
4
+# source from https://github.com/rg3/youtube-dl/issues/1208
5
+# removed some unnecessary debug messages..
6
+class CVevoSignAlgoExtractor:
7
+    # MAX RECURSION Depth for security
8
+    MAX_REC_DEPTH = 5
9
+
10
+    def __init__(self):
11
+        self.algoCache = {}
12
+        self._cleanTmpVariables()
13
+
14
+    def _cleanTmpVariables(self):
15
+        self.fullAlgoCode = ''
16
+        self.allLocalFunNamesTab = []
17
+        self.playerData = ''
18
+
19
+    def _jsToPy(self, jsFunBody):
20
+        pythonFunBody = jsFunBody.replace('function', 'def').replace('{', ':\n\t').replace('}', '').replace(';', '\n\t').replace('var ', '')
21
+        pythonFunBody = pythonFunBody.replace('.reverse()', '[::-1]')
22
+
23
+        lines = pythonFunBody.split('\n')
24
+        for i in range(len(lines)):
25
+            # a.split("") -> list(a)
26
+            match = re.search('(\w+?)\.split\(""\)', lines[i])
27
+            if match:
28
+                lines[i] = lines[i].replace(match.group(0), 'list(' + match.group(1) + ')')
29
+            # a.length -> len(a)
30
+            match = re.search('(\w+?)\.length', lines[i])
31
+            if match:
32
+                lines[i] = lines[i].replace(match.group(0), 'len(' + match.group(1) + ')')
33
+            # a.slice(3) -> a[3:]
34
+            match = re.search('(\w+?)\.slice\(([0-9]+?)\)', lines[i])
35
+            if match:
36
+                lines[i] = lines[i].replace(match.group(0), match.group(1) + ('[%s:]' % match.group(2)))
37
+            # a.join("") -> "".join(a)
38
+            match = re.search('(\w+?)\.join\(("[^"]*?")\)', lines[i])
39
+            if match:
40
+                lines[i] = lines[i].replace(match.group(0), match.group(2) + '.join(' + match.group(1) + ')')
41
+        return "\n".join(lines)
42
+
43
+    def _getLocalFunBody(self, funName):
44
+        # get function body
45
+        match = re.search('(function %s\([^)]+?\){[^}]+?})' % funName, self.playerData)
46
+        if match:
47
+            # return jsFunBody
48
+            return match.group(1)
49
+        return ''
50
+
51
+    def _getAllLocalSubFunNames(self, mainFunBody):
52
+        match = re.compile('[ =(,](\w+?)\([^)]*?\)').findall(mainFunBody)
53
+        if len(match):
54
+            # first item is name of main function, so omit it
55
+            funNameTab = set(match[1:])
56
+            return funNameTab
57
+        return set()
58
+
59
+    def decryptSignature(self, s, playerUrl):
60
+        playerUrl = playerUrl[:4] != 'http' and 'http:' + playerUrl or playerUrl
61
+        util.debug("decrypt_signature sign_len[%d] playerUrl[%s]" % (len(s), playerUrl))
62
+
63
+        # clear local data
64
+        self._cleanTmpVariables()
65
+
66
+        # use algoCache
67
+        if playerUrl not in self.algoCache:
68
+            # get player HTML 5 sript
69
+            request = urllib2.Request(playerUrl)
70
+            try:
71
+                self.playerData = urllib2.urlopen(request).read()
72
+                self.playerData = self.playerData.decode('utf-8', 'ignore')
73
+            except:
74
+                util.debug('Unable to download playerUrl webpage')
75
+                return ''
76
+
77
+            # get main function name
78
+            match = re.search("signature=(\w+?)\([^)]\)", self.playerData)
79
+            if match:
80
+                mainFunName = match.group(1)
81
+                util.debug('Main signature function name = "%s"' % mainFunName)
82
+            else:
83
+                util.debug('Can not get main signature function name')
84
+                return ''
85
+
86
+            self._getfullAlgoCode(mainFunName)
87
+
88
+            # wrap all local algo function into one function extractedSignatureAlgo()
89
+            algoLines = self.fullAlgoCode.split('\n')
90
+            for i in range(len(algoLines)):
91
+                algoLines[i] = '\t' + algoLines[i]
92
+            self.fullAlgoCode = 'def extractedSignatureAlgo(param):'
93
+            self.fullAlgoCode += '\n'.join(algoLines)
94
+            self.fullAlgoCode += '\n\treturn %s(param)' % mainFunName
95
+            self.fullAlgoCode += '\noutSignature = extractedSignatureAlgo( inSignature )\n'
96
+
97
+            # after this function we should have all needed code in self.fullAlgoCode
98
+            try:
99
+                algoCodeObj = compile(self.fullAlgoCode, '', 'exec')
100
+            except:
101
+                util.debug('decryptSignature compile algo code EXCEPTION')
102
+                return ''
103
+        else:
104
+            # get algoCodeObj from algoCache
105
+            util.debug('Algo taken from cache')
106
+            algoCodeObj = self.algoCache[playerUrl]
107
+
108
+        # for security alow only flew python global function in algo code
109
+        vGlobals = {"__builtins__": None, 'len': len, 'list': list}
110
+
111
+        # local variable to pass encrypted sign and get decrypted sign
112
+        vLocals = { 'inSignature': s, 'outSignature': '' }
113
+
114
+        # execute prepared code
115
+        try:
116
+            exec(algoCodeObj, vGlobals, vLocals)
117
+        except:
118
+            util.debug('decryptSignature exec code EXCEPTION')
119
+            return ''
120
+
121
+        util.debug('Decrypted signature = [%s]' % vLocals['outSignature'])
122
+        # if algo seems ok and not in cache, add it to cache
123
+        if playerUrl not in self.algoCache and '' != vLocals['outSignature']:
124
+            util.debug('Algo from player [%s] added to cache' % playerUrl)
125
+            self.algoCache[playerUrl] = algoCodeObj
126
+
127
+        # free not needed data
128
+        self._cleanTmpVariables()
129
+
130
+        return vLocals['outSignature']
131
+
132
+    # Note, this method is using a recursion
133
+    def _getfullAlgoCode(self, mainFunName, recDepth=0):
134
+        if self.MAX_REC_DEPTH <= recDepth:
135
+            util.debug('_getfullAlgoCode: Maximum recursion depth exceeded')
136
+            return
137
+
138
+        funBody = self._getLocalFunBody(mainFunName)
139
+        if '' != funBody:
140
+            funNames = self._getAllLocalSubFunNames(funBody)
141
+            if len(funNames):
142
+                for funName in funNames:
143
+                    if funName not in self.allLocalFunNamesTab:
144
+                        self.allLocalFunNamesTab.append(funName)
145
+                        util.debug("Add local function %s to known functions" % mainFunName)
146
+                        self._getfullAlgoCode(funName, recDepth + 1)
147
+
148
+            # conver code from javascript to python
149
+            funBody = self._jsToPy(funBody)
150
+            self.fullAlgoCode += '\n' + funBody + '\n'
151
+        return
152
+
153
+decryptor = CVevoSignAlgoExtractor()
154
+
155
+'''
156
+   YouTube plugin for XBMC
157
+    Copyright (C) 2010-2012 Tobias Ussing And Henrik Mosgaard Jensen
158
+
159
+    This program is free software: you can redistribute it and/or modify
160
+    it under the terms of the GNU General Public License as published by
161
+    the Free Software Foundation, either version 3 of the License, or
162
+    (at your option) any later version.
163
+
164
+    This program is distributed in the hope that it will be useful,
165
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
166
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
167
+    GNU General Public License for more details.
168
+
169
+    You should have received a copy of the GNU General Public License
170
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
171
+'''
172
+
173
+import sys
174
+import urllib
175
+import cgi
176
+import simplejson as json
177
+
178
+
179
+class YoutubePlayer(object):
180
+    fmt_value = {
181
+            5: "240p",
182
+            18: "360p",
183
+            22: "720p",
184
+            26: "???",
185
+            33: "???",
186
+            34: "360p",
187
+            35: "480p",
188
+            37: "1080p",
189
+            38: "720p",
190
+            43: "360p",
191
+            44: "480p",
192
+            45: "720p",
193
+            46: "520p",
194
+            59: "480",
195
+            78: "400",
196
+            82: "360p",
197
+            83: "240p",
198
+            84: "720p",
199
+            85: "520p",
200
+            100: "360p",
201
+            101: "480p",
202
+            102: "720p",
203
+            120: "hd720",
204
+            121: "hd1080"
205
+            }
206
+
207
+    # YouTube Playback Feeds
208
+    urls = {}
209
+    urls['video_stream'] = "http://www.youtube.com/watch?v=%s&safeSearch=none"
210
+    urls['embed_stream'] = "http://www.youtube.com/get_video_info?video_id=%s"
211
+    urls['video_info'] = "http://gdata.youtube.com/feeds/api/videos/%s"
212
+
213
+    def __init__(self):
214
+        pass
215
+
216
+    def removeAdditionalEndingDelimiter(self, data):
217
+        pos = data.find("};")
218
+        if pos != -1:
219
+            data = data[:pos + 1]
220
+        return data
221
+
222
+    def extractFlashVars(self, data, assets):
223
+        flashvars = {}
224
+        found = False
225
+
226
+        for line in data.split("\n"):
227
+            if line.strip().find(";ytplayer.config = ") > 0:
228
+                found = True
229
+                p1 = line.find(";ytplayer.config = ") + len(";ytplayer.config = ") - 1
230
+                p2 = line.rfind(";")
231
+                if p1 <= 0 or p2 <= 0:
232
+                    continue
233
+                data = line[p1 + 1:p2]
234
+                break
235
+        data = self.removeAdditionalEndingDelimiter(data)
236
+
237
+        if found:
238
+            data = json.loads(data)
239
+            if assets:
240
+                flashvars = data["assets"]
241
+            else:
242
+                flashvars = data["args"]
243
+        return flashvars
244
+
245
+    def scrapeWebPageForVideoLinks(self, result, video):
246
+        links = {}
247
+        flashvars = self.extractFlashVars(result, 0)
248
+        if not flashvars.has_key(u"url_encoded_fmt_stream_map"):
249
+            return links
250
+
251
+        if flashvars.has_key(u"ttsurl"):
252
+            video[u"ttsurl"] = flashvars[u"ttsurl"]
253
+        if flashvars.has_key("title"):
254
+            video["title"] = flashvars["title"]
255
+
256
+        for url_desc in flashvars[u"url_encoded_fmt_stream_map"].split(u","):
257
+            url_desc_map = cgi.parse_qs(url_desc)
258
+            if not (url_desc_map.has_key(u"url") or url_desc_map.has_key(u"stream")):
259
+                continue
260
+
261
+            key = int(url_desc_map[u"itag"][0])
262
+            url = u""
263
+            if url_desc_map.has_key(u"url"):
264
+                url = urllib.unquote(url_desc_map[u"url"][0])
265
+            elif url_desc_map.has_key(u"conn") and url_desc_map.has_key(u"stream"):
266
+                url = urllib.unquote(url_desc_map[u"conn"][0])
267
+                if url.rfind("/") < len(url) - 1:
268
+                    url = url + "/"
269
+                url = url + urllib.unquote(url_desc_map[u"stream"][0])
270
+            elif url_desc_map.has_key(u"stream") and not url_desc_map.has_key(u"conn"):
271
+                url = urllib.unquote(url_desc_map[u"stream"][0])
272
+
273
+            if url_desc_map.has_key(u"sig"):
274
+                url = url + u"&signature=" + url_desc_map[u"sig"][0]
275
+            elif url_desc_map.has_key(u"s"):
276
+                sig = url_desc_map[u"s"][0]
277
+                flashvars = self.extractFlashVars(result, 1)
278
+                js = flashvars[u"js"]
279
+                url = url + u"&signature=" + self.decrypt_signature(sig, js)
280
+
281
+            links[key] = url
282
+
283
+        return links
284
+
285
+    def decrypt_signature(self, s, js):
286
+        return decryptor.decryptSignature(s, js)
287
+
288
+
289
+    def extractVideoLinksFromYoutube(self, url, videoid, video):
290
+        result = util.request(self.urls[u"video_stream"] % videoid)
291
+        links = self.scrapeWebPageForVideoLinks(result, video)
292
+        if len(links) == 0:
293
+            util.error(u"Couldn't find video url- or stream-map.")
294
+        return links
295
+# /*
296
+# *      Copyright (C) 2011 Libor Zoubek
297
+# *
298
+# *
299
+# *  This Program is free software; you can redistribute it and/or modify
300
+# *  it under the terms of the GNU General Public License as published by
301
+# *  the Free Software Foundation; either version 2, or (at your option)
302
+# *  any later version.
303
+# *
304
+# *  This Program is distributed in the hope that it will be useful,
305
+# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
306
+# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
307
+# *  GNU General Public License for more details.
308
+# *
309
+# *  You should have received a copy of the GNU General Public License
310
+# *  along with this program; see the file COPYING.  If not, write to
311
+# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
312
+# *  http://www.gnu.org/copyleft/gpl.html
313
+# *
314
+# */
315
+import re, util, urllib
316
+__name__ = 'youtube'
317
+
318
+
319
+def supports(url):
320
+    return not _regex(url) == None
321
+
322
+def resolve(url):
323
+    m = _regex(url)
324
+    if not m == None:
325
+        player = YoutubePlayer()
326
+        video = {'title':'žádný název'}
327
+        index = url.find('&')  # strip out everytihing after &
328
+        if index > 0:
329
+            url = url[:index]
330
+        links = player.extractVideoLinksFromYoutube(url, m.group('id'), video)
331
+        resolved = []
332
+        for q in links:
333
+            if q in player.fmt_value.keys():
334
+                quality = player.fmt_value[q]
335
+                item = {}
336
+                item['name'] = __name__
337
+                item['url'] = links[q]
338
+                item['quality'] = quality
339
+                item['surl'] = url
340
+                item['subs'] = ''
341
+                item['title'] = video['title']
342
+                item['fmt'] = q
343
+                resolved.append(item)
344
+        return resolved
345
+
346
+def _regex(url):
347
+    return re.search('www\.youtube\.com/(watch\?v=|v/|embed/)(?P<id>.+?)(\?|$|&)', url, re.IGNORECASE | re.DOTALL)

+ 148
- 0
sources/SourceBase.py View File

@@ -0,0 +1,148 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+
9
+import urllib2, urllib
10
+import datetime, re, sys,os
11
+import requests
12
+from collections import OrderedDict
13
+import ConfigParser
14
+
15
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
16
+stream0 = {'name': '', 'url': '', 'quality': '???', 'surl': '', 'subs': '', 'headers': {},"desc":"","img":"","lang":"","type":"","order":0}        
17
+
18
+
19
+class SourceBase(object):
20
+    """Stream source base class"""
21
+
22
+    def __init__(self,country="lv"):
23
+        self.name = "name"
24
+        self.title = "Title"
25
+        self.img = ""
26
+        self.desc = ""
27
+        self.options = OrderedDict()
28
+        self.config_file = ""
29
+        self.url = "http://www.bbb.com/"
30
+        self.headers = headers2dict("""
31
+User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
32
+""")
33
+
34
+    def login(self,user="",password=""):
35
+        return False
36
+
37
+    def logout(self):
38
+        return True
39
+
40
+    def get_content(self,data):
41
+        ### To be overriden in child class
42
+        return [("..atpakaļ","back",None,"Kļūda, atgriezties atpakaļ")]
43
+
44
+    def is_video(self,data):
45
+        ### To be overriden in child class
46
+        return False
47
+
48
+    def get_streams(self,data):
49
+        ### Normally to be overrided in child class
50
+
51
+        if not self.is_video(data):
52
+            return []
53
+        content = self.get_content(data)
54
+        stream = stream0
55
+        stream["name"] = content[0].encode("utf8") if isinstance(content[0],unicode) else content[0]
56
+        stream["url"] = content[1].encode("utf8") if isinstance(content[1],unicode) else content[1]
57
+        stream["img"] = content[2].encode("utf8") if isinstance(content[2],unicode) else content[2]
58
+        stream["desc"] = content[3].encode("utf8") if isinstance(content[3],unicode) else content[3]
59
+        stream["type"] = stream_type(content[1]).encode("utf8") 
60
+        return[stream]
61
+
62
+    def options_read(self):
63
+        if not ("options" in dir(self) and self.options): # process options only if self.options defined, self.config_file should be defined too
64
+            return None
65
+        config = ConfigParser.ConfigParser()        
66
+        if os.path.exists(self.config_file):
67
+            config.read(self.config_file)
68
+            self.options = OrderedDict(config.items(self.name))             
69
+        else:
70
+            self.options_write(self.options)
71
+        return self.options
72
+
73
+    def options_write(self,options):        
74
+        config = ConfigParser.ConfigParser() 
75
+        config.add_section(self.name)
76
+        for k in options.keys():
77
+            config.set(self.name, k,options[k])
78
+        with open(self.config_file,"w") as f:
79
+            config.write(f)
80
+        self.options = OrderedDict(config.items(self.name))        
81
+
82
+    def call(self, data,params=None,headers=None,lang=""):
83
+        if not headers: headers = self.headers
84
+        url = self.url+data
85
+        result = self._http_request(url,params,headers=headers)
86
+        return result
87
+
88
+    def call_json(self, data,params=None,headers=None,lang=""):
89
+        result = self.call(url,params,headers=headers)
90
+        if result:
91
+            result = json.loads(content)
92
+            return result
93
+        else:
94
+            raise "No data returned"
95
+
96
+    def _http_request(self, url,params = None, headers=None):
97
+        if not headers: 
98
+            headers = self.headers if "headers" in dir(self) else headers2dict("User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0")
99
+        try:
100
+            if params:
101
+                r = requests.post(url, data=params, headers=headers)
102
+            else:
103
+                r = requests.get(url, headers=headers)
104
+            return r.content 
105
+        except Exception as ex:
106
+            if "read" in ex:
107
+                content = ex.read()
108
+            else:
109
+                content = None
110
+            return content
111
+
112
+    @staticmethod
113
+    def stream_type(data):
114
+        return stream_type(data)
115
+
116
+    @staticmethod
117
+    def get_stream0():
118
+        return stream0.copy()
119
+
120
+    @staticmethod
121
+    def parse_data(data):
122
+        if "::" in data:
123
+            source = data.split("::")[0]
124
+            data = data.split("::")[1]
125
+        else:
126
+            source = ""
127
+        path = data.split("?")[0]
128
+        plist = path.split("/")
129
+        clist = plist[0]
130
+        params = data[data.find("?"):] if "?" in data else ""
131
+        qs = dict(map(lambda x:x.split("="),re.findall("\w+=\w+",params)))
132
+        return source,data,path,plist,clist,params,qs
133
+
134
+def stream_type(data):
135
+    if "::" in data:
136
+        data = data.split("::")[1]
137
+    data = data.lower()
138
+    m = re.search(r"^(\w+)://", data)
139
+    prefix = m.group(1) if m else ""
140
+    if prefix in ("http","https") and "m3u8" in data:
141
+        return "hls"
142
+    elif prefix == "http":
143
+        return "http"
144
+    else:
145
+        return prefix
146
+
147
+if __name__ == "__main__":
148
+    pass

+ 404
- 0
sources/YouTubeVideoUrl.py View File

@@ -0,0 +1,404 @@
1
+# -*- coding: UTF-8 -*-
2
+# This video extraction code based on youtube-dl: https://github.com/rg3/youtube-dl
3
+
4
+import codecs
5
+import json
6
+import re
7
+
8
+from urllib import urlencode
9
+from urllib2 import urlopen, URLError
10
+import sys
11
+
12
+#from Components.config import config
13
+
14
+#from . import sslContext
15
+sslContext = None
16
+if sys.version_info >= (2, 7, 9):
17
+	try:
18
+		import ssl
19
+		sslContext = ssl._create_unverified_context()
20
+	except:
21
+		pass 
22
+from jsinterp import JSInterpreter
23
+from swfinterp import SWFInterpreter
24
+
25
+
26
+PRIORITY_VIDEO_FORMAT = []
27
+maxResolution =  '22'
28
+
29
+
30
+def createPriorityFormats():
31
+	global PRIORITY_VIDEO_FORMAT,maxResolution
32
+	PRIORITY_VIDEO_FORMAT = []
33
+	use_format = False
34
+	for itag_value in ['38', '37', '96', '22', '95', '120',
35
+		'35', '94', '18', '93', '5', '92', '132', '17']:
36
+		if itag_value == maxResolution: #config.plugins.YouTube.maxResolution.value:
37
+			use_format = True
38
+		if use_format:
39
+			PRIORITY_VIDEO_FORMAT.append(itag_value)
40
+
41
+createPriorityFormats()
42
+
43
+IGNORE_VIDEO_FORMAT = [
44
+		'43',  # webm
45
+		'44',  # webm
46
+		'45',  # webm
47
+		'46',  # webm
48
+		'100',  # webm
49
+		'101',  # webm
50
+		'102'  # webm
51
+	]
52
+
53
+
54
+def uppercase_escape(s):
55
+	unicode_escape = codecs.getdecoder('unicode_escape')
56
+	return re.sub(
57
+		r'\\U[0-9a-fA-F]{8}',
58
+		lambda m: unicode_escape(m.group(0))[0],
59
+		s)
60
+
61
+
62
+def compat_urllib_parse_unquote(string, encoding='utf-8', errors='replace'):
63
+	if string == '':
64
+		return string
65
+	res = string.split('%')
66
+	if len(res) == 1:
67
+		return string
68
+	if encoding is None:
69
+		encoding = 'utf-8'
70
+	if errors is None:
71
+		errors = 'replace'
72
+	# pct_sequence: contiguous sequence of percent-encoded bytes, decoded
73
+	pct_sequence = b''
74
+	string = res[0]
75
+	for item in res[1:]:
76
+		try:
77
+			if not item:
78
+				raise ValueError
79
+			pct_sequence += item[:2].decode('hex')
80
+			rest = item[2:]
81
+			if not rest:
82
+				# This segment was just a single percent-encoded character.
83
+				# May be part of a sequence of code units, so delay decoding.
84
+				# (Stored in pct_sequence).
85
+				continue
86
+		except ValueError:
87
+			rest = '%' + item
88
+		# Encountered non-percent-encoded characters. Flush the current
89
+		# pct_sequence.
90
+		string += pct_sequence.decode(encoding, errors) + rest
91
+		pct_sequence = b''
92
+	if pct_sequence:
93
+		# Flush the final pct_sequence
94
+		string += pct_sequence.decode(encoding, errors)
95
+	return string
96
+
97
+
98
+def _parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
99
+			encoding='utf-8', errors='replace'):
100
+	qs, _coerce_result = qs, unicode
101
+	pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
102
+	r = []
103
+	for name_value in pairs:
104
+		if not name_value and not strict_parsing:
105
+			continue
106
+		nv = name_value.split('=', 1)
107
+		if len(nv) != 2:
108
+			if strict_parsing:
109
+				raise ValueError("bad query field: %r" % (name_value,))
110
+			# Handle case of a control-name with no equal sign
111
+			if keep_blank_values:
112
+				nv.append('')
113
+			else:
114
+				continue
115
+		if len(nv[1]) or keep_blank_values:
116
+			name = nv[0].replace('+', ' ')
117
+			name = compat_urllib_parse_unquote(
118
+				name, encoding=encoding, errors=errors)
119
+			name = _coerce_result(name)
120
+			value = nv[1].replace('+', ' ')
121
+			value = compat_urllib_parse_unquote(
122
+				value, encoding=encoding, errors=errors)
123
+			value = _coerce_result(value)
124
+			r.append((name, value))
125
+	return r
126
+
127
+
128
+def compat_parse_qs(qs, keep_blank_values=False, strict_parsing=False,
129
+					encoding='utf-8', errors='replace'):
130
+	parsed_result = {}
131
+	pairs = _parse_qsl(qs, keep_blank_values, strict_parsing,
132
+					encoding=encoding, errors=errors)
133
+	for name, value in pairs:
134
+		if name in parsed_result:
135
+			parsed_result[name].append(value)
136
+		else:
137
+			parsed_result[name] = [value]
138
+	return parsed_result
139
+
140
+
141
+class YouTubeVideoUrl():
142
+
143
+	def _download_webpage(self, url):
144
+		""" Returns a tuple (page content as string, URL handle) """
145
+		try:
146
+			if sslContext:
147
+				urlh = urlopen(url, context = sslContext)
148
+			else:
149
+				urlh = urlopen(url)
150
+		except URLError, e:
151
+			#raise Exception(e.reason)
152
+			return ""
153
+		return urlh.read()
154
+
155
+	def _search_regex(self, pattern, string):
156
+		"""
157
+		Perform a regex search on the given string, using a single or a list of
158
+		patterns returning the first matching group.
159
+		"""
160
+		mobj = re.search(pattern, string, 0)
161
+		if mobj:
162
+			# return the first matching group
163
+			return next(g for g in mobj.groups() if g is not None)
164
+		else:
165
+			raise Exception('Unable extract pattern from string!')
166
+
167
+	def _decrypt_signature(self, s, player_url):
168
+		"""Turn the encrypted s field into a working signature"""
169
+
170
+		if player_url is None:
171
+			raise Exception('Cannot decrypt signature without player_url!')
172
+
173
+		if player_url[:2] == '//':
174
+			player_url = 'https:' + player_url
175
+		try:
176
+			func = self._extract_signature_function(player_url)
177
+			return func(s)
178
+		except:
179
+			raise Exception('Signature extraction failed!')
180
+
181
+	def _extract_signature_function(self, player_url):
182
+		id_m = re.match(
183
+			r'.*?-(?P<id>[a-zA-Z0-9_-]+)(?:/watch_as3|/html5player(?:-new)?|/base)?\.(?P<ext>[a-z]+)$',
184
+			player_url)
185
+		if not id_m:
186
+			raise Exception('Cannot identify player %r!' % player_url)
187
+		player_type = id_m.group('ext')
188
+		code = self._download_webpage(player_url)
189
+		if player_type == 'js':
190
+			return self._parse_sig_js(code)
191
+		elif player_type == 'swf':
192
+			return self._parse_sig_swf(code)
193
+		else:
194
+			raise Exception('Invalid player type %r!' % player_type)
195
+
196
+	def _parse_sig_js(self, jscode):
197
+		funcname = self._search_regex(r'\.sig\|\|([a-zA-Z0-9$]+)\(', jscode)
198
+		jsi = JSInterpreter(jscode)
199
+		initial_function = jsi.extract_function(funcname)
200
+		return lambda s: initial_function([s])
201
+
202
+	def _parse_sig_swf(self, file_contents):
203
+		swfi = SWFInterpreter(file_contents)
204
+		TARGET_CLASSNAME = 'SignatureDecipher'
205
+		searched_class = swfi.extract_class(TARGET_CLASSNAME)
206
+		initial_function = swfi.extract_function(searched_class, 'decipher')
207
+		return lambda s: initial_function([s])
208
+
209
+	def _extract_from_m3u8(self, manifest_url):
210
+		url_map = {}
211
+
212
+		def _get_urls(_manifest):
213
+			lines = _manifest.split('\n')
214
+			urls = filter(lambda l: l and not l.startswith('#'), lines)
215
+			return urls
216
+
217
+		manifest = self._download_webpage(manifest_url)
218
+		formats_urls = _get_urls(manifest)
219
+		for format_url in formats_urls:
220
+			itag = self._search_regex(r'itag/(\d+?)/', format_url)
221
+			url_map[itag] = format_url
222
+		return url_map
223
+
224
+	def _get_ytplayer_config(self, webpage):
225
+		# User data may contain arbitrary character sequences that may affect
226
+		# JSON extraction with regex, e.g. when '};' is contained the second
227
+		# regex won't capture the whole JSON. Yet working around by trying more
228
+		# concrete regex first keeping in mind proper quoted string handling
229
+		# to be implemented in future that will replace this workaround (see
230
+		# https://github.com/rg3/youtube-dl/issues/7468,
231
+		# https://github.com/rg3/youtube-dl/pull/7599)
232
+		patterns = [
233
+			r';ytplayer\.config\s*=\s*({.+?});ytplayer',
234
+			r';ytplayer\.config\s*=\s*({.+?});',
235
+		]
236
+		for pattern in patterns:
237
+			config = self._search_regex(pattern, webpage)
238
+			if config:
239
+				return json.loads(uppercase_escape(config))
240
+
241
+	def extract(self, video_id):
242
+		url = 'https://www.youtube.com/watch?v=%s&gl=US&hl=en&has_verified=1&bpctr=9999999999' % video_id
243
+
244
+		# Get video webpage
245
+		video_webpage = self._download_webpage(url)
246
+		if not video_webpage:
247
+			#raise Exception('Video webpage not found!')
248
+			return ""
249
+
250
+		# Attempt to extract SWF player URL
251
+		mobj = re.search(r'swfConfig.*?"(https?:\\/\\/.*?watch.*?-.*?\.swf)"', video_webpage)
252
+		if mobj is not None:
253
+			player_url = re.sub(r'\\(.)', r'\1', mobj.group(1))
254
+		else:
255
+			player_url = None
256
+
257
+		# Get video info
258
+		embed_webpage = None
259
+		if re.search(r'player-age-gate-content">', video_webpage) is not None:
260
+			age_gate = True
261
+			# We simulate the access to the video from www.youtube.com/v/{video_id}
262
+			# this can be viewed without login into Youtube
263
+			url = 'https://www.youtube.com/embed/%s' % video_id
264
+			embed_webpage = self._download_webpage(url)
265
+			data = urlencode({
266
+				'video_id': video_id,
267
+				'eurl': 'https://youtube.googleapis.com/v/' + video_id,
268
+				'sts': self._search_regex(r'"sts"\s*:\s*(\d+)', embed_webpage),
269
+			})
270
+			video_info_url = 'https://www.youtube.com/get_video_info?' + data
271
+			video_info_webpage = self._download_webpage(video_info_url)
272
+			video_info = compat_parse_qs(video_info_webpage)
273
+		else:
274
+			age_gate = False
275
+			video_info = None
276
+			# Try looking directly into the video webpage
277
+			ytplayer_config = self._get_ytplayer_config(video_webpage)
278
+			if ytplayer_config:
279
+				args = ytplayer_config['args']
280
+				if args.get('url_encoded_fmt_stream_map'):
281
+					# Convert to the same format returned by compat_parse_qs
282
+					video_info = dict((k, [v]) for k, v in args.items())
283
+
284
+			if not video_info:
285
+				# We also try looking in get_video_info since it may contain different dashmpd
286
+				# URL that points to a DASH manifest with possibly different itag set (some itags
287
+				# are missing from DASH manifest pointed by webpage's dashmpd, some - from DASH
288
+				# manifest pointed by get_video_info's dashmpd).
289
+				# The general idea is to take a union of itags of both DASH manifests (for example
290
+				# video with such 'manifest behavior' see https://github.com/rg3/youtube-dl/issues/6093)
291
+				for el_type in ['&el=info', '&el=embedded', '&el=detailpage', '&el=vevo', '']:
292
+					video_info_url = (
293
+						'https://www.youtube.com/get_video_info?&video_id=%s%s&ps=default&eurl=&gl=US&hl=en'
294
+						% (video_id, el_type))
295
+					video_info_webpage = self._download_webpage(video_info_url)
296
+					video_info = compat_parse_qs(video_info_webpage)
297
+					if 'token' in video_info:
298
+						break
299
+		if 'token' not in video_info:
300
+			if 'reason' in video_info:
301
+				print '[YouTubeVideoUrl] %s' % video_info['reason'][0]
302
+			else:
303
+				print '[YouTubeVideoUrl] "token" parameter not in video info for unknown reason'
304
+
305
+		# Start extracting information
306
+		if 'conn' in video_info and video_info['conn'][0][:4] == 'rtmp':
307
+			url = video_info['conn'][0]
308
+		elif len(video_info.get('url_encoded_fmt_stream_map', [''])[0]) >= 1 or \
309
+			len(video_info.get('adaptive_fmts', [''])[0]) >= 1:
310
+			encoded_url_map = video_info.get('url_encoded_fmt_stream_map', [''])[0] + \
311
+				',' + video_info.get('adaptive_fmts', [''])[0]
312
+			if 'rtmpe%3Dyes' in encoded_url_map:
313
+				raise Exception('rtmpe downloads are not supported, see https://github.com/rg3/youtube-dl/issues/343')
314
+
315
+			# Find the best format from our format priority map
316
+			encoded_url_map = encoded_url_map.split(',')
317
+			url_map_str = None
318
+			# If format changed in config, recreate priority list
319
+			if PRIORITY_VIDEO_FORMAT[0] != maxResolution: #config.plugins.YouTube.maxResolution.value:
320
+				createPriorityFormats()
321
+			for our_format in PRIORITY_VIDEO_FORMAT:
322
+				our_format = 'itag=' + our_format
323
+				for encoded_url in encoded_url_map:
324
+					if our_format in encoded_url and 'url=' in encoded_url:
325
+						url_map_str = encoded_url
326
+						break
327
+				if url_map_str:
328
+					break
329
+			# If anything not found, used first in the list if it not in ignore map
330
+			if not url_map_str:
331
+				for encoded_url in encoded_url_map:
332
+					if 'url=' in encoded_url:
333
+						url_map_str = encoded_url
334
+						for ignore_format in IGNORE_VIDEO_FORMAT:
335
+							ignore_format = 'itag=' + ignore_format
336
+							if ignore_format in encoded_url:
337
+								url_map_str = None
338
+								break
339
+					if url_map_str:
340
+						break
341
+			if not url_map_str:
342
+				url_map_str = encoded_url_map[0]
343
+
344
+			url_data = compat_parse_qs(url_map_str)
345
+			url = url_data['url'][0]
346
+			if 'sig' in url_data:
347
+				url += '&signature=' + url_data['sig'][0]
348
+			elif 's' in url_data:
349
+				encrypted_sig = url_data['s'][0]
350
+				ASSETS_RE = r'"assets":.+?"js":\s*("[^"]+")'
351
+
352
+				jsplayer_url_json = self._search_regex(ASSETS_RE,
353
+					embed_webpage if age_gate else video_webpage)
354
+				if not jsplayer_url_json and not age_gate:
355
+					# We need the embed website after all
356
+					if embed_webpage is None:
357
+						embed_url = 'https://www.youtube.com/embed/%s' % video_id
358
+						embed_webpage = self._download_webpage(embed_url)
359
+					jsplayer_url_json = self._search_regex(ASSETS_RE, embed_webpage)
360
+
361
+				player_url = json.loads(jsplayer_url_json)
362
+				if player_url is None:
363
+					player_url_json = self._search_regex(
364
+						r'ytplayer\.config.*?"url"\s*:\s*("[^"]+")',
365
+						video_webpage)
366
+					player_url = json.loads(player_url_json)
367
+
368
+				signature = self._decrypt_signature(encrypted_sig, player_url)
369
+				url += '&signature=' + signature
370
+			if 'ratebypass' not in url:
371
+				url += '&ratebypass=yes'
372
+		elif video_info.get('hlsvp'):
373
+			url = None
374
+			manifest_url = video_info['hlsvp'][0]
375
+			url_map = self._extract_from_m3u8(manifest_url)
376
+
377
+			# Find the best format from our format priority map
378
+			for our_format in PRIORITY_VIDEO_FORMAT:
379
+				if url_map.get(our_format):
380
+					url = url_map[our_format]
381
+					break
382
+			# If anything not found, used first in the list if it not in ignore map
383
+			if not url:
384
+				for url_map_key in url_map.keys():
385
+					if url_map_key not in IGNORE_VIDEO_FORMAT:
386
+						url = url_map[url_map_key]
387
+						break
388
+			if not url:
389
+				url = url_map.values()[0]
390
+		else:
391
+			#raise Exception('No supported formats found in video info!')
392
+			return ""
393
+
394
+		return str(url)
395
+
396
+if __name__ == "__main__":
397
+
398
+	#yt = YouTubeVideoUrl()
399
+	if len(sys.argv)>1:
400
+		video_id= sys.argv[1]
401
+	else:
402
+		video_id = "2rlTF6HiMGg"
403
+	e = YouTubeVideoUrl().extract(video_id)
404
+	print e

+ 0
- 0
sources/__init__.py View File


+ 209
- 0
sources/cinemalive.py View File

@@ -0,0 +1,209 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+import urllib2, urllib
13
+import datetime, re, sys,os
14
+import ConfigParser
15
+from SourceBase import SourceBase
16
+#from collections import OrderedDict
17
+import os
18
+
19
+#sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 
20
+from resolver import resolve
21
+import util
22
+
23
+
24
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
25
+import HTMLParser
26
+h = HTMLParser.HTMLParser()
27
+    
28
+class Source(SourceBase):
29
+    
30
+    def __init__(self,country=""):
31
+        self.name = "cinemalive"
32
+        self.title = "cinemalive.tv"
33
+        self.img = "picons/cinemalive.png" #"https://cinemalive.tv/assets/img/logo.png"
34
+        self.desc = "cinemalive.tv satura skatīšanās"
35
+        self.country=country
36
+        self.headers = headers2dict("""
37
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
38
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
39
+""")
40
+        self.headers2 = headers2dict("""
41
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36
42
+Content-Type: application/x-www-form-urlencoded; charset=UTF-8
43
+Accept-Language: en-US,en;q=0.8
44
+""")
45
+        self.url = "https://cinemalive.tv/"
46
+        #self.login()
47
+        
48
+            
49
+    ######### Entry point ########
50
+    def get_content(self, data):
51
+        print "[cinemalive] get_content:", data
52
+        source,data,path,plist,clist,params,qs = self.parse_data(data)     
53
+        content=[]
54
+        content.append(("..return", "back","","Return back"))
55
+        
56
+        if clist=="home":
57
+            content.extend([
58
+                ("Search", "cinemalive::scripts/search.php?search={0}","","Search"),            
59
+                ("Filmas latviski - visas", "cinemalive::filmaslatviski/visas/lapa/1","","Filmas latviski - visas"),
60
+                ("Filmas angliski", "cinemalive::home_en","","Filmas angliski"),
61
+                ("Filmas latviski - jaunākās", "cinemalive::filmaslatviski/jaunakas/lapa/1","","Filmas latviski - jaunākās"),
62
+                ("Filmas latviski - vertētākās", "cinemalive::filmaslatviski/vertetakas/lapa/1","","Filmas latviski - vērtētākās"),
63
+                ("Filmas latviski - skatitakās", "cinemalive::filmaslatviski/skatitakas/lapa/1","","Filmas latviski - skatītākās"),
64
+            ])
65
+            r = self.call("filmaslatviski")
66
+            for item in re.findall(r'<li class="nav-submenu-item"><a href="/([\w/]+)">(.+?)</a></li>', r):
67
+                title = "Filmas latviski - "+item[1]
68
+                data2 = item[0]+"/lapa/1"
69
+                img = self.img
70
+                desc = title
71
+                content.append((title,self.name+"::"+data2,img,desc))      
72
+            return content
73
+
74
+        elif clist=="home_en":
75
+            content.extend([
76
+                ("Search", "cinemalive::scripts/search.php?search={0}","","Search"),            
77
+                ("Movies English - all", "cinemalive::moviesenglish/all/page/1","","Movies English - all"),
78
+                ("Movies Latvian", "cinemalive::home","","Filmas latviski"),
79
+                ("Movies English - newest", "cinemalive::moviesenglish/newestmovies/page/1","","Movies English - newest"),
80
+                ("Movies English - top rated", "cinemalive::moviesenglish/toprated/page/1","","Movies English - top rated"),
81
+                ("Movies English - most watched", "cinemalive::moviesenglish/mostwatched/page/1","","Movies English - most watched"),
82
+            ])
83
+            r = self.call("moviesenglish")
84
+            for item in re.findall(r'<li class="nav-submenu-item"><a href="/([\w/]+)">(.+?)</a></li>', r):
85
+                title = "Movies English - "+item[1]
86
+                data2 = item[0]+"/page/1"
87
+                img = self.img
88
+                desc = title
89
+                content.append((title,self.name+"::"+data2,img,desc))      
90
+            return content
91
+
92
+    
93
+        elif "search.php" in data:
94
+            
95
+            r=self.call(path,params=params[1:],headers=self.headers2)
96
+            result = re.findall(r'<div class="results.+?<a href="https://cinemalive\.tv/(.+?)">.+?<img src="(.+?)".+?<span style="color:#bcbcbc">([^<]+)</span> <span style="color:#5a606d;font-size:12px;">([^<]+)</span><br/>.+?<p class="dec" style="font-size:12px; color:#777;line-height:14px;">([^<]+)</p>', r, re.DOTALL)            
97
+            for item in result:
98
+                title = item[2]
99
+                title0 = re.sub(" \(\d+\)","",title)
100
+                if title0 == item[3]:
101
+                    title = title+" [EN]"
102
+                else:
103
+                    title = title + "/"+ item[3]+" [LV]"
104
+                title = util.unescape(title)
105
+                data2 = item[0]
106
+                img = item[1].replace("xs.","sm.")
107
+                desc = util.unescape(item[4])
108
+                content.append((title,self.name+"::"+data2,img,desc))            
109
+            return content
110
+
111
+        elif clist in ("filmaslatviski","moviesenglish"):
112
+            r = self.call(data)
113
+            if not r:
114
+                return content
115
+            result = re.findall(r'<div class="base-used">.+?<a href="https://cinemalive.tv/([^"]+)">.+?<img class="img-thumbnail" src="/([^"]+)" alt="([^"]+)"/>.+?<p class="year">(\d+)</p>', r, re.DOTALL)
116
+            for item in result:
117
+                title = item[2] + " (%s)"%item[3]
118
+                data2 = item[0]
119
+                img = "https://cinemalive.tv/"+item[1]
120
+                title = util.unescape(title)
121
+                desc = title
122
+                content.append((title,self.name+"::"+data2,img,desc)) 
123
+            m = re.search(r"""<a href='https://cinemalive\.tv/([^']+)' style="border-right:none;">»</a>""", r, re.DOTALL)
124
+            if m:
125
+                data2 = m.group(1)
126
+                content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))                                  
127
+            return content      
128
+         
129
+        else:
130
+            return content                            
131
+              
132
+    def is_video(self,data):
133
+        source,data,path,plist,clist,params,qs = self.parse_data(data)        
134
+        if clist=="movie":
135
+            return True
136
+        else:
137
+            return False
138
+                        
139
+    def get_streams(self, data):
140
+        print "[cinemalive] get_streams:", data
141
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
142
+        r = self.call(path)
143
+        if not r:
144
+            return []
145
+        streams = []
146
+        title0 = re.search("<title>([^<]+)</title>", r).group(1)
147
+        lang = "LV" if "Filma Online Latviski" in title0 else "EN"
148
+        title = title0.replace(" - Filma Online Latviski","").replace(" - Movie Online English HD","")
149
+        desc = re.search('<p class="plot">(.+?)</p>', r).group(1)
150
+        img = "http://cinemalive.tv"+re.search('<img src="(.+?)" class="img-thumbnail"', r).group(1)
151
+        
152
+        m = re.search(r'<video id=.+?<source src="([^"]+\.mp4)"', r, re.DOTALL)
153
+        if m:
154
+            s = util.item()
155
+            s["url"] = m.group(1)
156
+            s["name"] = util.unescape(title)
157
+            s["desc"] = util.unescape(desc)
158
+            s["img"] = img
159
+            s["type"] = self.stream_type(s["url"])
160
+            s["lang"] = lang 
161
+            return [s]
162
+        
163
+        #m = re.search('<div class="viboom-overroll"><iframe src="([^"]+)"', r)
164
+        #if m:
165
+        result = re.findall('<div id="video_container"><iframe src="(.+?)"', r)
166
+        if result:
167
+            streams = resolve(result[0])
168
+            for s in streams:
169
+                s["name"] = util.unescape(title)
170
+                s["desc"] = util.unescape(desc)
171
+                s["img"] = img
172
+                s["type"] = self.stream_type(s["url"])
173
+                s["lang"] = lang
174
+            if len(result)>1:
175
+                lang2 = "EN" if lang=="LV" else "LV"
176
+                streams2 = resolve(result[1])
177
+                for s in streams2:
178
+                    s["name"] = util.unescape(title)
179
+                    s["desc"] = util.unescape(desc)
180
+                    s["img"] = img
181
+                    s["type"]= self.stream_type(s["url"])
182
+                    s["lang"] = lang2
183
+                    streams.append(s)  
184
+            return streams
185
+        else:
186
+            return []
187
+
188
+                    
189
+if __name__ == "__main__":
190
+    country= "lv"
191
+    c = Source(country)
192
+    if len(sys.argv)>1:
193
+        data= sys.argv[1]
194
+    else:
195
+        data = "home"
196
+    content = c.get_content(data)
197
+    for item in content:
198
+        print item
199
+    #cat = api.get_categories(country)
200
+    #chan = api.get_channels("lv")
201
+    #prog = api.get_programs(channel=6400)
202
+    #prog = api.get_programs(category=55)
203
+    #seas = api.get_seasons(program=6453)
204
+    #str = api.get_streams(660243)
205
+    #res = api.get_videos(802)
206
+    #formats = api.getAllFormats()
207
+    #det = api.detailed("1516")
208
+    #vid = api.getVideos("13170")
209
+    pass

+ 99
- 0
sources/config.py View File

@@ -0,0 +1,99 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+import os.path,re
9
+import collections
10
+from SourceBase import SourceBase
11
+
12
+os.path.dirname(os.path.abspath(__file__))
13
+class Source(SourceBase):
14
+    
15
+    def __init__(self,country="lv"):
16
+        self.name = "config"
17
+        self.country=country
18
+        cur_directory = os.path.dirname(os.path.abspath(__file__))
19
+        self.streams_file = os.path.join(cur_directory,"streams.cfg")
20
+        self.lists = collections.OrderedDict()
21
+        self.read_streams()
22
+         
23
+    def get_content(self, data):
24
+        self.read_streams()
25
+        if "::" in data:
26
+            data = data.split("::")[1]
27
+        if not data in self.lists:
28
+            return []
29
+        return self.lists[data]
30
+
31
+    def is_video(self,data):
32
+        return False
33
+    
34
+    def read_streams(self):
35
+        for line in open(self.streams_file,"r"):
36
+            r = re.search("^\[(\w+)\]", line)
37
+            if r:
38
+                name = r.group(1)
39
+                self.lists[name] = []
40
+            else:
41
+                if len(line)<10 or line[0] in ("#"): continue
42
+                items = tuple(line.strip().split("|"))
43
+                if len(items)<2:
44
+                    continue
45
+                self.lists[name].append(items)
46
+                
47
+    def write_streams(self):
48
+        f = open(self.streams_file,"w")
49
+        for l in self.lists.keys():
50
+            f.write("[%s]\n"%l)
51
+            for item in self.lists[l]:
52
+                f.write("%s|%s|%s|%s\n"%(item[0],item[1],item[2],item[3]))
53
+            f.write("\n")
54
+        f.close()
55
+    
56
+    def get_lists(self):
57
+        return self.lists.keys()
58
+    
59
+    def get_list_items(self,name):
60
+        return self.lists[name]
61
+    
62
+    def add_list(self,name):
63
+        if not name in self.lists.keys():
64
+            self.lists[name] = []
65
+            
66
+    def del_list(self,name):
67
+        if name in self.lists.keys():
68
+            del self.lists[name]
69
+            
70
+    def add_item(self,name,item,pos=None):
71
+        if name in self.lists.keys():
72
+            if pos==None:
73
+                self.lists[name].append(item)
74
+            else:
75
+                self.lists[name].insert(pos,item)
76
+                
77
+    def del_item(self,name,pos):
78
+        self.lists[name].pop(pos)
79
+    
80
+    def replace_item(self,name,item,pos):
81
+        self.lists[name][pos]=item
82
+            
83
+                           
84
+if __name__ == "__main__":
85
+    c = Source()
86
+    content = c.get_content("home")
87
+    for item in content: print item
88
+    #c.del_item("home",0)
89
+    #c.del_list("favorites")
90
+    
91
+    #c.add_item("home",("My Streams","config::favorites","","Mani saglabātie TV kanāli un video"),0)
92
+    c.replace_item("home",("My Streams","config::my_streams","default","Mani saglabātie TV kanāli un video"),0)
93
+    #c.add_list("favorites")
94
+    #c.add_item("favorites",("..return","back","","Atgriezties atpakaļ"))    
95
+    #c.add_item("favorites",("LTV1","http://streamltv.cloudy.services/ltv/LTV02.smil/playlist.m3u8","picons/latvia1.png", "Latvijas televīzijas 1.kanāls"))
96
+    
97
+    c.write_streams()
98
+    for item in content: print item
99
+    

+ 286
- 0
sources/euronews.py View File

@@ -0,0 +1,286 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, time,re, sys,os
15
+from collections import OrderedDict
16
+from SourceBase import SourceBase
17
+
18
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
19
+import HTMLParser
20
+h = HTMLParser.HTMLParser()
21
+    
22
+class Source(SourceBase):
23
+    
24
+    def __init__(self,language="en"):
25
+        self.name = "euronews"
26
+        self.title = "Euronews"
27
+        self.img = "http://pbs.twimg.com/profile_images/732665354242150400/tZsCnjuh_400x400.jpg"
28
+        self.desc = "Euronews live and archive"
29
+        self.headers = headers2dict("""
30
+User-Agent: Euronews/4.0.126
31
+Content-Type: application/json
32
+Connection: keep-alive
33
+        """)
34
+        #self.language=language
35
+        cur_directory = os.path.dirname(os.path.abspath(__file__))
36
+        self.config_file = os.path.join(cur_directory,self.name+".cfg")
37
+        self.options = OrderedDict([("language","en")])
38
+        self.options_read()
39
+        self.vid={"1": "News", "2": "European Affairs", "3": "Lifestyle", "4": "Knowledge"}
40
+        self.languages = []
41
+        try:
42
+            self.get_languages()
43
+        except:
44
+            pass
45
+        
46
+    def login(self,user="",password=""):
47
+        return True
48
+            
49
+    def get_content(self, data):
50
+        print "[%s] get_content:"%self.name, data
51
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
52
+        #lang = qs["lang"] if "lang" in qs else self.language
53
+        lang = self.options["language"]
54
+        if not lang in self.get_languages():
55
+            raise Exception("Not valid default language - '%s'"%lang)
56
+    
57
+        content=[]
58
+        content.append(("..return", "back","","Return back"))
59
+        
60
+        if clist=="home":
61
+            content.extend([
62
+                ("Search", "euronews::content/getSearch?lang=%s&byPage=40&page=1&text={0}"%lang,self.img,"Top stories timeline"),                
63
+                ("Live stream", "euronews::live?lang=%s"%lang,self.img,"Euronews live stream"),
64
+                ("Just in", "euronews::content/getTimeline?lang=%s&byPage=40&page=1"%lang,self.img,"News timeline"),
65
+                ("Top stories", "euronews::content/getTopStories?lang=%s"%lang,self.img,"Top stories timeline"),
66
+                ("Category - News", "euronews::content/getVertical?lang=%s&byPage=40&page=1&vId=1"%lang,self.img,"Category - News"),
67
+                ("Category - European Affairs", "euronews::content/getVertical?lang=%s&byPage=40&page=1&vId=2"%lang,self.img,"Category - European Affairs"),
68
+                ("Category - Lifestyle", "euronews::content/getVertical?lang=%s&byPage=40&page=1&vId=3"%lang,self.img,"Category - Lifestyle"),
69
+                ("Category - Knowledge", "euronews::content/getVertical?lang=%s&byPage=40&page=1&vId=4"%lang,self.img,"Category - Knowledge"),
70
+                ("Latest programs", "euronews::content/getLatestPrograms?lang=%s&byPage=40&page=1"%lang,self.img,"Latest programs"),
71
+                ("Programs list", "euronews::content/getPrograms?lang=%s"%lang,self.img,"Programs list"),
72
+             ])
73
+            return content
74
+        
75
+
76
+        ### Video arhīvs ###  
77
+        elif clist=="content":
78
+            if "lang" in qs:
79
+                del qs["lang"]
80
+            params = json.dumps(qs)
81
+            
82
+            req = '{"methodName":"content.%s","apiKey":"androidPhoneEuronews-1.0","params":%s,"language":"%s"}'%(plist[1],params,lang)
83
+            r = self.call(req)    
84
+            if not r:
85
+                return content
86
+            lst = r["timeline"] if "timeline" in r else\
87
+                r["topstorieslist"] if "topstorieslist" in r else\
88
+                r["programs"] if "programs" in r else\
89
+                r["programDetailsList"] if "programDetailsList" in r else\
90
+                r["programlist"] if "programlist" in r else\
91
+                r["articlelist"] if "articlelist" in r else\
92
+                r["verticals"] if "verticals" in r else\
93
+                []
94
+            if not lst:
95
+                return content
96
+            
97
+            for item in lst:
98
+                if plist[1] in ("getTimeline"):
99
+                    article = item["article"]
100
+                    atype = item["type"]
101
+                    #if item["type"] == "wire":
102
+                        #continue # TODO
103
+                else:
104
+                    article = item
105
+                    atype = "article"
106
+                if plist[1]=="getPrograms":
107
+                    title = article["title"] 
108
+                    id = article["pId"]
109
+                    desc = title
110
+                    img = "http://static.euronews.com/articles/programs/533x360_%s"%article["img"]
111
+                    data2 = "content/getProgramDetails?lang=%s&byPage=40&page=1&pId=%s"%(lang,id)
112
+                    content.append((title,self.name+"::"+data2,img,desc))
113
+                else:
114
+                    title = article["title"] if "title" in article else article["text"] if "text" in article else "No title"
115
+                    if atype <> "article":
116
+                        title = "[%s] %s"%(atype,title)
117
+                    atime = datetime.datetime.fromtimestamp(int(article["uts"]))
118
+                    #atime = datetime.datetime.fromtimestamp(int(article["uts"])-time.altzone) 
119
+                    atime = atime.strftime("%Y-%m-%d %H:%M")
120
+                    vert = self.vid[article["vId"]] if "vId" in article else ""
121
+                    ptitle = article["pTitle"] if "pTitle" in article else ""
122
+                    id = article["id"]
123
+                    desc = "%s\n%s\n%s %s"%(title,atime,vert,ptitle)
124
+                    img = "http://static.euronews.com/articles/%s/399x225_%s.jpg"%(id,id)
125
+                    if not atype in ("breakingnews","wire"):
126
+                        data2 = "content/getArticle?lang=%s&id=%s"%(lang,id)
127
+                    else:
128
+                        data2 = ""
129
+                    content.append((title,self.name+"::"+data2,img,desc))
130
+            if "page=" in data:
131
+                data2 = re.sub("page=\d+","page=%s"%(int(qs["page"])+1),data)
132
+                content.append(("Next page",self.name+"::"+data2,self.img,"Next page")) 
133
+            return content            
134
+
135
+    
136
+    def is_video(self,data):
137
+        source,data,path,plist,clist,params,qs = self.parse_data(data)        
138
+        if path == "live":
139
+            return True
140
+        elif clist=="content" and plist[1]=="getArticle":
141
+            return True
142
+        else:
143
+            return False
144
+        
145
+    def get_streams(self, data):
146
+        print "[euronews] get_streams:", data
147
+        if not self.is_video(data):
148
+            return []
149
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
150
+        #lang = qs["lang"] if "lang" in qs else self.language
151
+        lang = self.options["language"]
152
+        if not lang in self.get_languages():
153
+            raise Exception("Not valid default language - '%s'"%lang)        
154
+        
155
+        streams = []
156
+        
157
+        if path == "live":
158
+            url = "http://www.euronews.com/api/watchlive.json"
159
+            r = self._http_request(url)
160
+            try:
161
+                js = json.loads(r)
162
+                url = js["url"]
163
+            except:
164
+                raise Exception("No live stream found")
165
+            r = self._http_request(url)
166
+            try:
167
+                js = json.loads(r)
168
+                if not js["status"]=="ok":
169
+                    raise Exception("No live stream found")
170
+            except:
171
+                raise Exception("No live stream found")
172
+            
173
+            slist = js["primary"]
174
+            
175
+            for l in slist:
176
+                stream = self.get_stream0()
177
+                stream["url"]=slist[l]["hls"]
178
+                stream["lang"]=l
179
+                stream["quality"]="variant"
180
+                stream["name"]="Euronews live [%s]"%l
181
+                stream["desc"]=stream["name"]
182
+                stream["type"]="hls" #stream_type(url)
183
+                streams.append(stream)
184
+                
185
+        elif clist=="content" and plist[1] == "getArticle":
186
+            if "lang" in qs:
187
+                del qs["lang"]
188
+            languages = self.get_languages()
189
+            for lang in languages:
190
+                id = qs["id"]
191
+                req = '{"methodName":"content.getArticle","apiKey":"androidPhoneEuronews-1.0","params":{"id":"%s"},"language":"%s"}'%(id,lang)
192
+                r = self.call(req)    
193
+                if not r:
194
+                    raise Exception("No live stream found") 
195
+                if not "articlelist" in r:
196
+                    msg = r["label"] if "label" in r else "No article finde"
197
+                    raise Exception(msg)
198
+                article = r["articlelist"]
199
+                stream = self.get_stream0()
200
+                stream["url"]=article["videoUri"] if "videoUri" in article else ""
201
+                if not stream["url"]:
202
+                    return []
203
+                stream["lang"]=lang
204
+                stream["quality"]="?"
205
+                stream["name"]= article["title"]
206
+                stream["desc"]=article["text"] if "text" in article else article["title"]
207
+                stream["type"]="http" #stream_type(url)
208
+                streams.append(stream)
209
+            
210
+        else:
211
+            raise Exception("No live stream found")
212
+           
213
+        ### TODO - sakārtot sarakstu, lai pirmais ir labakais video
214
+        qlist = ["???","lq","mq","hq","hd","variant"]
215
+        llist = ["fr","en","ru","lv"]        
216
+        for s in streams:
217
+            if s["lang"]==self.options["language"]:
218
+                s["order"] = 10000
219
+                continue
220
+            lv = llist.index(s["lang"])*10 if s["lang"] in llist else 0
221
+            qv=qlist.index(s["quality"]) if s["quality"] in qlist else 0
222
+            s["order"] = lv+qv
223
+        streams = sorted(streams,key=lambda item: item["order"],reverse=True)
224
+        return streams
225
+    def get_languages(self):
226
+        if self.languages: return self.languages
227
+        url = "http://www.euronews.com/api/watchlive.json"
228
+        r = self._http_request(url)
229
+        try:
230
+            js = json.loads(r)
231
+            url = js["url"]
232
+        except:
233
+            raise Exception("Can not get languages list")
234
+        r = self._http_request(url)
235
+        try:
236
+            js = json.loads(r)
237
+            if not js["status"]=="ok":
238
+                raise Exception("Can not get languages list")
239
+        except:
240
+            raise Exception("Can not get languages list")
241
+        
242
+        slist = js["primary"]
243
+        self.languages=slist.keys()
244
+        return self.languages
245
+        
246
+    def call(self, data,params = None, headers=None):
247
+        if not headers: headers = self.headers
248
+        #if not lang: lang = self.country
249
+        url = "http://api.euronews.com/ipad/"
250
+        headers = headers2dict("""
251
+User-Agent: Euronews/4.0.126
252
+Content-Type: multipart/form-data, boundary=AaB03xBounDaRy; charset=UTF-8
253
+Host: api.euronews.com
254
+Connection: Keep-Alive
255
+        """)
256
+        params = """
257
+--AaB03xBounDaRy
258
+content-disposition: form-data; name=request
259
+
260
+%s
261
+--AaB03xBounDaRy--
262
+"""%data
263
+        content = self._http_request(url, params, headers)
264
+        if content:
265
+            try:
266
+                result = json.loads(content)
267
+                return result
268
+            except Exception, ex:
269
+                return None
270
+        else:
271
+            return None
272
+        
273
+
274
+if __name__ == "__main__":
275
+    language= "en"
276
+    c = Source(language)
277
+    data = '{"methodName":"content.getTimeline","apiKey":"androidPhoneEuronews-1.0","params":{"page":"1","byPage":"30"},"language":"en"}'
278
+    r = c.call(data)
279
+    if len(sys.argv)>1:
280
+        data= sys.argv[1]
281
+    else:
282
+        data = "home"
283
+    content = c.get_content(data)
284
+    for item in content:
285
+        print item
286
+    pass

+ 284
- 0
sources/filmix.py View File

@@ -0,0 +1,284 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, re, sys,os
15
+import ConfigParser
16
+from SourceBase import SourceBase
17
+import base64
18
+from collections import OrderedDict
19
+import sys
20
+
21
+
22
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
23
+import util
24
+    
25
+class Source(SourceBase):
26
+    
27
+    def __init__(self,country=""):
28
+        self.name = "filmix"
29
+        self.title = "Filmix.net"
30
+        self.img = "http://cs5324.vk.me/g33668783/a_903fcc63.jpg"
31
+        self.desc = "Filmix.net satura skatīšanās"
32
+        self.country=country
33
+        self.headers = headers2dict("""
34
+Host: filmix.net
35
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
37
+Accept-Language: en-US,en;q=0.5
38
+""")
39
+        self.headers2 = headers2dict("""
40
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
41
+X-Requested-With: XMLHttpRequest
42
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
43
+""")
44
+        self.url = "http://filmix.net/"
45
+        #self.login()
46
+        
47
+    def login(self,user="",password=""):
48
+        return True
49
+            
50
+    def get_content(self, data):
51
+        print "[filmix] get_content:", data
52
+        if "::" in data:
53
+            data = data.split("::")[1] 
54
+        path = data.split("?")[0]
55
+        clist = path.split("/")[0]
56
+        params = data[data.find("?"):] if "?" in data else ""
57
+        qs = dict(map(lambda x:x.split("="),re.findall("[%\w]+=\w+",params)))
58
+        lang = qs["lang"] if "lang" in qs else self.country
59
+    
60
+        content=[]
61
+        content.append(("..return", "back","","Return back"))
62
+        
63
+        if clist=="home":
64
+            content.extend([
65
+                ("Search", "filmix::search/{0}","","Search"),                
66
+                ("Movies", "filmix::movies","","Movies"),
67
+                ("Series", "filmix::series","","TV Series"),
68
+                ("Cartoons", "filmix::cartoons","","Cartoons"),
69
+            ])
70
+            return content
71
+    
72
+        #elif clist=="search":
73
+            # TODO
74
+            #return content
75
+
76
+        elif data in ("movies","series","cartoons"):
77
+            r = self.call("")
78
+            r = r.decode("cp1251").encode("utf8")
79
+            if data == "movies":
80
+                sname = "Фильмы"
81
+            elif data=="series":
82
+                sname = "Сериалы"
83
+            else:
84
+                sname = "Мультфильмы"
85
+            m = re.search('<li><span class="menu-title">%s</span>(.+?)<li><span'%sname, r, re.DOTALL|re.UNICODE)
86
+            if not m: return content
87
+            r2 = m.group(1)
88
+            result = re.findall(r'<a href="http://filmix\.net/([^"]+)".*?>([^<]+)</a>', r2, re.DOTALL)
89
+            for item in result:
90
+                if "catalog" in item[0]: continue
91
+                title = item[1]
92
+                data2 = item[0]
93
+                img = self.img
94
+                desc = title
95
+                content.append((title,self.name+"::"+data2,img,desc))      
96
+            return content      
97
+         
98
+        ## Video/seriāla spēlēšana
99
+        elif clist=="play":
100
+            r = self.call(path)
101
+            r = r.decode("cp1251").encode("utf8")
102
+            title = title0 = util.unescape(re.search("titlePlayer = '([^']+)'", r, re.DOTALL).group(1))
103
+            m = re.search('<meta itemprop="thumbnailUrl" content="([^"]+)',r,re.DOTALL)
104
+            img = m.group(1) if m else self.img
105
+            m = re.search('<meta itemprop="duration" content="([^"]+)" />', r, re.DOTALL)
106
+            duration = "(%s)"%m.group(1) if m else ""
107
+            m = re.search('<p itemprop="description"[^>]+>([^<]+)<', r, re.DOTALL)
108
+            desc = desc0 =  util.unescape(m.group(1).strip()) if m else ""
109
+            
110
+            #url2 = re.search('<div id="play-descr" class="title-page">.+?<a href="([^"]+)"', r, re.DOTALL).group(1)
111
+            #r2 = self._http_request(url2)
112
+             
113
+            m = re.search("videoLink = '([^']+)'", r, re.DOTALL)
114
+            if not m:
115
+                raise Exception("Can not find video link")
116
+            video_link = m.group(1)
117
+            if video_link<>'{video-link}':
118
+                video_link = self.decode_uppod_text(video_link)
119
+                streams = self.get_streams2(video_link)
120
+                data2 = streams[0][1]
121
+                content = (title,data2,img,desc)
122
+                return content
123
+            else:
124
+                m = re.search("plLink = '([^']+)'", r, re.DOTALL)
125
+                if not m:
126
+                    raise Exception("Can not find video link")
127
+                pl_link = m.group(1)
128
+                pl_link = self.decode_uppod_text(pl_link)
129
+                js = self._http_request(pl_link)
130
+                js = self.decode_uppod_text(js)
131
+                js = json.loads(js)
132
+                if "s" in qs and "e" in qs:
133
+                    s = int(qs["s"])
134
+                    e = int(qs["e"])
135
+                    serie = js["playlist"][s-1]["playlist"][e-1]["comment"].encode("utf8")
136
+                    title = title0+" - "+ serie
137
+                    data2 = js["playlist"][s-1]["playlist"][e-1]["file"].encode("utf8")
138
+                    streams = self.get_streams2(data2)
139
+                    data2=streams[0][1]
140
+                    desc = serie +"\n"+desc0
141
+                    content = (title,data2,img,desc)
142
+                    return content
143
+                
144
+                elif "s" in qs:
145
+                    s = int(qs["s"])
146
+                    for i,ep in enumerate(js["playlist"][s-1]["playlist"]):
147
+                        title = title0+" - "+js["playlist"][s-1]["playlist"][i]["comment"].encode("utf8")
148
+                        serie = js["playlist"][s-1]["playlist"][i]["comment"].encode("utf8")
149
+                        data2 = data+"&e=%s"%(i+1)
150
+                        desc = serie +"\n"+desc0
151
+                        content.append((title,self.name+"::"+data2,img,desc)) 
152
+                else:
153
+                    for i,ep in enumerate(js["playlist"]):
154
+                        title = title0 +" - "+js["playlist"][i]["comment"].encode("utf8")
155
+                        serie = js["playlist"][i]["comment"].encode("utf8")
156
+                        data2 = data+"?s=%s"%(i+1)
157
+                        desc = serie +"\n"+desc0
158
+                        content.append((title,self.name+"::"+data2,img,desc)) 
159
+                return content                    
160
+                #r = self._http_request(url)
161
+            
162
+        
163
+        ### saraksts ### 
164
+        else:
165
+            r = self.call(data)
166
+            r = r.decode("cp1251").encode("utf8")
167
+            for r2 in re.findall('<article class="shortstory line".+?</article>', r, re.DOTALL):
168
+                m = re.search(r'<a href="http://filmix\.net/play/(\d+)" class="watch icon-play">', r2, re.DOTALL)
169
+                if not m: continue
170
+                vid = m.group(1)
171
+                data2 = "play/%s"%vid
172
+                #title = re.search('itemprop="name">([^<]+)</div>', r2, re.DOTALL).group(1)
173
+                title = re.search('itemprop="name" content="([^"]+)"', r2, re.DOTALL).group(1)
174
+                m = re.search('itemprop="alternativeHeadline" content="([^"]+)"', r2, re.DOTALL)
175
+                if m:
176
+                    title = title + "/"+m.group(1)
177
+                m = re.search('<img src="([^"]+.jpg)"', r2, re.DOTALL)
178
+                img = "http://filmix.net"+m.group(1) if m else self.img
179
+                m = re.search(r'<a itemprop="copyrightYear".+?>(\d+)<', r2, re.DOTALL)
180
+                if m:
181
+                    year = m.group(1) if m else ""
182
+                    title = "%s (%s)"%(title,year)
183
+                title = util.unescape(title)
184
+                genre = re.findall('<a itemprop="genre"[^>]+?">([^<]+)</a>', r2, re.DOTALL)
185
+                genre = ",".join(genre)
186
+                m = re.search('<p itemprop="description">([^<]+)</p>', r2, re.DOTALL)
187
+                desc0 = util.unescape(m.group(1)) if m else ""
188
+                m = re.search('<div class="quality">([^<]+)</div>', r2, re.DOTALL)
189
+                quality = m.group(1) if m else ""
190
+                actors = re.findall('itemprop="actor">([^<]+)<', r2, re.DOTALL)
191
+                actors = ",".join(actors)
192
+                desc="%s\n%s\n%s\n%s\n%s"%(title,genre,desc0,actors,quality)
193
+                content.append((title,self.name+"::"+data2,img,desc))
194
+            if '<div class="navigation">' in r:
195
+                m = re.search(r'href="http://filmix\.net/([^"]+)" class="next icon-arowRight btn-tooltip"', r, re.DOTALL)
196
+                if m:
197
+                    data2 = m.group(1)
198
+                else:
199
+                    m = re.search("/page/(\d)+",data)
200
+                    if m:
201
+                        page = int(m.group(1))+1
202
+                        data2 = re.sub("/page/(\d)+", "/page/%s"%page, data)
203
+                    else:
204
+                        data2 = data + "/page/2"
205
+                content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))
206
+                        
207
+            return content                            
208
+              
209
+    def is_video(self,data):
210
+        if "::" in data:
211
+            data = data.split("::")[1] 
212
+        path = data.split("?")[0]
213
+        clist = path.split("/")[0]
214
+        params = data[data.find("?"):] if "?" in data else ""
215
+        
216
+        if clist == "play" and "s=" in data and "e=" in data:
217
+            return True
218
+        elif clist=="play" and not params:
219
+            r = self.call(path)
220
+            #r = r.decode("cp1251").encode("utf8")
221
+            title = title0 = util.unescape(re.search("titlePlayer = '([^']+)'", r, re.DOTALL).group(1))
222
+            m = re.search("videoLink = '([^']+)'", r, re.DOTALL)
223
+            if not m:
224
+                raise Exception("Can not find video link")
225
+            video_link = m.group(1)
226
+            if video_link=='{video-link}':
227
+                return False
228
+            else:
229
+                return True
230
+        else:
231
+            return False
232
+        
233
+    def call(self, data,params=None,headers=None,lang=""):
234
+        if not headers: headers = self.headers
235
+        url = self.url+data
236
+        result = self._http_request(url,params,headers=headers)
237
+        return result
238
+
239
+
240
+        
241
+    def decode_uppod_text(self, text): 
242
+        Client_codec_a = ["l", "u", "T", "D", "Q", "H", "0", "3", "G", "1", "f", "M", "p", "U", "a", "I", "6", "k", "d", "s", "b", "W", "5", "e", "y", "="]
243
+        Client_codec_b = ["w", "g", "i", "Z", "c", "R", "z", "v", "x", "n", "N", "2", "8", "J", "X", "t", "9", "V", "7", "4", "B", "m", "Y", "o", "L", "h"]    
244
+        text = text.replace("\n", "").strip()
245
+        for i in range(len(Client_codec_a)):
246
+            char1 = Client_codec_b[i]
247
+            char2 = Client_codec_a[i]
248
+            text = text.replace(char1, "___")
249
+            text = text.replace(char2, char1)
250
+            text = text.replace("___", char2)
251
+        result = base64.b64decode(text)
252
+        print result
253
+        return result
254
+    
255
+    def get_streams2(self,url0):
256
+        res = re.search("\[([\d,]+)]",url0).group(1)
257
+        streams=[]
258
+        for res in res.split(","):
259
+            if not res: continue
260
+            url=re.sub("\[[\d,]+]",res,url0)
261
+            streams.append((res,url))
262
+        return streams
263
+        
264
+if __name__ == "__main__":
265
+    country= "lv"
266
+    c = Source(country)
267
+    if len(sys.argv)>1:
268
+        data= sys.argv[1]
269
+    else:
270
+        data = "home"
271
+    content = c.get_content(data)
272
+    for item in content:
273
+        print item
274
+    #cat = api.get_categories(country)
275
+    #chan = api.get_channels("lv")
276
+    #prog = api.get_programs(channel=6400)
277
+    #prog = api.get_programs(category=55)
278
+    #seas = api.get_seasons(program=6453)
279
+    #str = api.get_streams(660243)
280
+    #res = api.get_videos(802)
281
+    #formats = api.getAllFormats()
282
+    #det = api.detailed("1516")
283
+    #vid = api.getVideos("13170")
284
+    pass

+ 272
- 0
sources/filmon.py View File

@@ -0,0 +1,272 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, re, sys
15
+from SourceBase import SourceBase
16
+
17
+API_URL = 'http://www.filmon.com/'
18
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
19
+#User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
20
+headers0 = headers2dict("""
21
+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
22
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
23
+Accept-Language: en-US,en;q=0.5
24
+Accept-Encoding: deflate
25
+Connection: keep-alive
26
+""")
27
+import HTMLParser
28
+h = HTMLParser.HTMLParser()
29
+
30
+class Source(SourceBase):
31
+
32
+    def __init__(self,country="lv"):
33
+        self.name = "filmon"
34
+        self.title = "FilmOn"
35
+        self.img = "http://behindthegloves.com/wp-content/uploads/2016/01/FilmOn-logo1.jpg"
36
+        self.desc = "FilmOn portāla satura skatīšanās"
37
+        self.headers = headers0
38
+
39
+        self.country=country
40
+        self.jstv = None
41
+        self.session_key = None
42
+        self.cookie = None
43
+
44
+    def get_content(self, data):
45
+        print "[filmon] get_content:", data
46
+        if "::" in data:
47
+            data = data.split("::")[1] 
48
+        path = data.split("?")[0]
49
+        clist = path.split("/")[0]
50
+        params = data[data.find("?"):] if "?" in data else ""
51
+        qs = dict(map(lambda x:x.split("="),re.findall("\w+=\w+",params)))
52
+        lang = qs["lang"] if "lang" in qs else self.country
53
+
54
+        if not self.jstv:
55
+            self.jstv = self.get_tv_channels()
56
+        #if not self.session_key: # TODO izskatās, ka strādā bez, vismaz ja nelogojas iekšā,  jānočeko
57
+        #    html = self._http_request("http://www.filmon.com/api/init")
58
+        #    js = json.loads(html)
59
+        #    self.session_key = js["session_key"]
60
+
61
+        content=[]
62
+        content.append(("..return", "back","","Return back"))
63
+
64
+        if clist=="home":
65
+            content.extend([
66
+                ("Live streams", "filmon::tv","","TV live streams"),
67
+                ("Video on demand", "filmon::vod","","Last videos"),
68
+            ])
69
+            return content
70
+
71
+        ### TV Groups ###
72
+        elif clist in ("tv","home"):
73
+            for gr in self.jstv:
74
+                title = gr["name"].encode("utf8")
75
+                data2 = "group?id=%s"%gr["id"]
76
+                img = gr["logo_148x148_uri"].encode("utf8")
77
+                desc = gr["description"].encode("utf8")
78
+                content.append((title,self.name+"::"+data2,img,desc))
79
+            return content
80
+
81
+        ### TV group channels ###
82
+        elif clist=="group":
83
+            if "id" in qs:
84
+                group_id = qs["id"] 
85
+            else:
86
+                return content
87
+            group = None
88
+            for gr in self.jstv:
89
+                if gr["id"]==group_id:
90
+                    group = gr
91
+                    break
92
+            if not group:
93
+                return content
94
+            for ch in group["channels"]:
95
+                title = ch["title"].encode("utf8")
96
+                data2 = "channel?id=%s"%ch["id"]
97
+                img = ch["big_logo"].encode("utf8")
98
+                desc = ch["description"].encode("utf8") if ch["description"] else title
99
+                content.append((title,self.name+"::"+data2,img,desc))
100
+            return content
101
+
102
+        ### TV Channel ###
103
+        elif clist == "channel" or clist == "video":
104
+            if "id" in qs:
105
+                ch_id = qs["id"] 
106
+            else:
107
+                return ("No stream found %s"%data,"","","No stream found")
108
+            ch = self.get_tv_channel_info(ch_id)
109
+            if ch["now_playing"]:
110
+                current_event = ch["now_playing"]["programme_name"] if "programme_name" in ch["now_playing"] else ""
111
+            else:
112
+                current_event = ""
113
+            title = u"%s - %s"%(ch["title"],current_event)
114
+            title = title.encode("utf8")
115
+            if current_event:
116
+                desc = ch["now_playing"]["programme_description"].encode("utf8")
117
+            else:
118
+                desc = title
119
+            data2 = ""
120
+            for t in ("SD","HD"):
121
+                for s in ch["streams"]:
122
+                    if s["name"]==t:
123
+                        data2 = s["url"].encode("utf8")
124
+                        break
125
+                if data2: break
126
+            return (title,data2,"",desc)
127
+
128
+        ### VOD genres ###
129
+        elif path in ("vod","vod/genres"):
130
+            data = "vod/genres"
131
+            js = self.call(data)
132
+            for gr in js["response"]:
133
+                title = gr["name"].encode("utf8")
134
+                data2 = "vod/search?genre=%s&max_results=30&no_episode=true&start_index=0"%(gr["slug"].encode("utf8"))
135
+                img = gr["images"][0]["url"].encode("utf8")
136
+                desc = gr["description"].encode("utf8") if gr["description"] else title
137
+                content.append((title,self.name+"::"+data2,img,desc))
138
+            return content           
139
+
140
+        ### VOD genre videos ###
141
+        elif path == "vod/search":
142
+            js = self.call(data)
143
+            for vid in js["response"]:
144
+                title = vid["title"].encode("utf8")
145
+                if vid["type"]=="series":
146
+                    title = "[Series] "+title
147
+                data2 = "vod/movie?id=%s&type=%s"%(vid["id"],vid["type"].encode("utf8"))
148
+                img = "http://static.filmon.com/assets/"+vid["poster"]["couchdb_url"].encode("utf8")
149
+                desc = vid["description"].encode("utf8") if vid["description"] else title
150
+                content.append((title,self.name+"::"+data2,img,desc))
151
+            start_index = int(qs["start_index"]) if "start_index" in qs else 0
152
+            if start_index+js["total"]<js["total_found"]:
153
+                start_index += 30
154
+                data2 = re.sub("start_index=\d+","start_index=%s"%start_index,data) if "start_index" in qs else data +"&start_index=30"
155
+                content.append(("Next page",self.name+"::"+data2,"","Next page"))                                            
156
+            return content
157
+
158
+        ### VOD video sigle/series ###
159
+        elif path == "vod/movie":
160
+            js = self.call(data)
161
+            if js["response"]["type"] == "series":
162
+                ids = ",".join(js["response"]["episodes"])
163
+                data2 = "vod/movies?ids=%s"%ids
164
+                js2 = self.call(data2)
165
+                for vid in js2["response"]:
166
+                    title = vid["title"].encode("utf8")
167
+                    if vid["type"]=="series":
168
+                        title = "[Series] "+title
169
+                    data2 = "vod/movie?id=%s&type=%s"%(vid["id"],vid["type"].encode("utf8"))
170
+                    img = "http://static.filmon.com/assets/"+vid["poster"]["couchdb_url"].encode("utf8")
171
+                    desc = vid["description"].encode("utf8") if vid["description"] else title
172
+                    content.append((title,self.name+"::"+data2,img,desc))
173
+                return content
174
+            else:
175
+                title = js["response"]["title"].encode("utf8")
176
+                desc = js["response"]["description"].encode("utf8") if js["response"]["description"] else title
177
+                data2 = js["response"]["streams"]["low"]["url"].encode("utf8")
178
+                return (title,data2,"",desc)
179
+
180
+    def is_video(self,data):
181
+        if "::" in data:
182
+            data = data.split("::")[1]
183
+        cmd = data.split("?")
184
+        if cmd[0] in ("video","channel"):
185
+            return True
186
+        elif cmd[0] == "vod/movie" and "type=movie" in data:
187
+            return True
188
+        else:
189
+            return False
190
+
191
+    def call(self, data,headers=headers0,lang=""):
192
+        if not lang: lang = self.country
193
+        url = "http://www.filmon.com/api/" + data
194
+        #if not "?" in url: url += "?session_key=%s"%self.session_key
195
+        #if not "session_key=" in url: url += "&session_key=%s"%self.session_key
196
+        #print "[TVPlay Api] url: ",url
197
+        result = []
198
+        content = self._http_request(url)
199
+        if content:
200
+            try:
201
+                result = json.loads(content)
202
+            except Exception, ex:
203
+                return None
204
+        return result
205
+
206
+    #----------------------------------------------------------------------
207
+    def get_tv_channel_info(self,id):
208
+        url = "http://www.filmon.com/ajax/getChannelInfo"
209
+        headers = headers2dict("""
210
+Host: www.filmon.com
211
+User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:41.0) Gecko/20100101 Firefox/41.0
212
+Accept: application/json, text/javascript, */*; q=0.01
213
+Accept-Language: en-US,en;q=0.5
214
+Accept-Encoding: deflate
215
+DNT: 1
216
+Content-Type: application/x-www-form-urlencoded; charset=UTF-8
217
+X-Requested-With: XMLHttpRequest
218
+Referer: http://www.filmon.com/tv/live
219
+Connection: keep-alive
220
+Pragma: no-cache
221
+Cache-Control: no-cache
222
+""")
223
+        headers["Cookie"] = self.cookie
224
+        data = "channel_id=%s&quality=low"%id
225
+        response = urllib2.urlopen(urllib2.Request(url, headers=headers,data=data))
226
+        html =  response.read()
227
+        js = json.loads(html)
228
+        return js
229
+
230
+    #----------------------------------------------------------------------
231
+    def get_tv_channels(self):
232
+        """Get tv channels list"""
233
+        headers = headers2dict("""
234
+Host: www.filmon.com
235
+User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:41.0) Gecko/20100101 Firefox/41.0
236
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
237
+Accept-Language: en-US,en;q=0.5
238
+Accept-Encoding: deflate
239
+DNT: 1
240
+Connection: keep-alive
241
+    """)
242
+
243
+        url = "http://www.filmon.com/tv"
244
+        response = urllib2.urlopen(urllib2.Request(url, headers=headers))
245
+        if "set-cookie" in response.headers:
246
+            self.cookie = response.headers["set-cookie"]
247
+        html =  response.read()
248
+        s = re.search("(?i)var groups = (.*);", html).groups(1)[0]
249
+        js = json.loads(s)
250
+        return js
251
+
252
+if __name__ == "__main__":
253
+    country= "lv"
254
+    c = Source(country)
255
+    if len(sys.argv)>1:
256
+        data= sys.argv[1]
257
+    else:
258
+        data = "home"
259
+    content = c.get_content(data)
260
+    for item in content:
261
+        print item
262
+    #cat = api.get_categories(country)
263
+    #chan = api.get_channels("lv")
264
+    #prog = api.get_programs(channel=6400)
265
+    #prog = api.get_programs(category=55)
266
+    #seas = api.get_seasons(program=6453)
267
+    #str = api.get_streams(660243)
268
+    #res = api.get_videos(802)
269
+    #formats = api.getAllFormats()
270
+    #det = api.detailed("1516")
271
+    #vid = api.getVideos("13170")
272
+    pass

+ 519
- 0
sources/iplayer.py View File

@@ -0,0 +1,519 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+import sys, os, os.path, re, sys
9
+import urllib,urllib2
10
+from xml.sax.saxutils import unescape,escape
11
+from urllib import quote, unquote
12
+import datetime
13
+import HTMLParser
14
+import json
15
+import datetime,time
16
+from SourceBase import SourceBase, stream_type, stream0
17
+from collections import OrderedDict
18
+
19
+API_URL = 'https://m.lattelecom.tv/'
20
+user_agent = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; da-dk) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3"
21
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
22
+h = HTMLParser.HTMLParser()
23
+    
24
+class Source(SourceBase):
25
+    
26
+    def __init__(self):
27
+        self.name = "iplayer"
28
+        self.title = "BBC iPlayer"
29
+        self.img = "http://www.userlogos.org/files/logos/inductiveload/BBC_iPlayer_logo.png"
30
+        self.desc = "BBC iPlayer portal content"
31
+        
32
+        self.api_url = "http://ibl.api.bbci.co.uk/ibl/v1/"
33
+        self.headers = headers2dict("""
34
+User-Agent: BBCiPlayer/4.19.0.3021 (SM-G900FD; Android 4.4.2)
35
+Connection: Keep-Alive
36
+        """)
37
+        self.headers2 = headers2dict("""
38
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
39
+Connection: Keep-Alive
40
+        """)
41
+        
42
+        self.ch = []
43
+        self.ch_id={}
44
+        self.ch_id2={}
45
+        self.ch_name={}
46
+        self.logos ={
47
+            "bbc_one_london":"http://www.lyngsat-logo.com/hires/bb/bbc_one.png",
48
+            "bbc_two_england":"http://www.lyngsat-logo.com/hires/bb/bbc_two_uk.png",
49
+            "bbc_three":"http://www.lyngsat-logo.com/hires/bb/bbc_three_uk.png",
50
+            "bbc_four":"http://www.lyngsat-logo.com/hires/bb/bbc_four_uk.png",
51
+            "bbc_radio_one":"http://www.lyngsat-logo.com/hires/bb/bbc_radio1.png",
52
+            "cbbc":"http://www.lyngsat-logo.com/hires/bb/bbc_cbbc.png",
53
+            "cbeebies":"http://www.lyngsat-logo.com/hires/bb/bbc_cbeebies_uk.png",
54
+            "bbc_news24":"http://www.lyngsat-logo.com/hires/bb/bbc_news.png",
55
+            "bbc_parliament":"http://www.lyngsat-logo.com/hires/bb/bbc_parliament.png",
56
+            "bbc_alba":"http://www.lyngsat-logo.com/hires/bb/bbc_alba.png",
57
+            "s4cpbs":"http://www.lyngsat-logo.com/hires/ss/s4c_uk.png"          
58
+        }
59
+                       
60
+    def get_content(self, data):
61
+        print "[iplayer] get_content:", data
62
+        if "::" in data:
63
+            data = data.split("::")[1] 
64
+        path = data.split("?")[0]
65
+        clist = path.split("/")[0]
66
+        params = data[data.find("?"):] if "?" in data else ""
67
+        qs = dict(map(lambda x:x.split("="),re.findall("\w+=[\w-]+",params)))
68
+        #lang = qs["lang"] if "lang" in qs else self.country
69
+    
70
+        content=[]
71
+        content.append(("..return", "back","","Return back"))
72
+        
73
+        ### Home ###
74
+        if data=="home":
75
+            content.extend([
76
+                ("Search TV", "iplayer::search/{0}","","Search in iPlayer"),
77
+                ("Live streams", "iplayer::live","","TV live streams"),
78
+                ("Channels", "iplayer::channels","","Programmes by channel/date"),
79
+                ("Categories", "iplayer::categories","","Programmes by categories"),
80
+                ("A-Z", "iplayer::a-z","","All programmes by name"),
81
+                ("Highlights", "iplayer::home/highlights","","Current highlights"),             
82
+                ("Most popular", "iplayer::groups/popular/episodes?per_page=40&page=1","","Most popular programmes")            
83
+            ])
84
+            return content
85
+        
86
+        ### Search ###
87
+        elif clist=="search":
88
+            data_ = "search-suggest/?q=%s&rights=mobile&initial_child_count=1"%data.split("/")[1]
89
+            r = self.call(data_)
90
+            for item in r["search_suggest"]["results"]:
91
+                title,data2,img,desc = self.get_data_element(item)
92
+                content.append((title,self.name+"::"+data2,img,desc))
93
+            return content
94
+                        
95
+        
96
+        ### Live main ###
97
+        elif data=="live":
98
+            for ch in self.get_channels():
99
+                title = ch["title"]
100
+                img = self.logos[ch["id"]] if ch["id"] in self.logos else  "http://static.bbci.co.uk/mobileiplayerappbranding/1.9/android/images/channels/tv-guide-wide-logo/layout_normal/xxhdpi/%s_tv-guide-wide-logo.png"%ch["id"]
101
+                desc = title
102
+                data2 = "live/%s"%ch["id"]
103
+                ee = self.get_epg_live(ch["id"])
104
+                desc = ee[2]
105
+                content.append((title,self.name+"::"+data2,img,desc))
106
+            return content
107
+        
108
+        ### Categories ###
109
+        elif data == "categories":
110
+            r = self.call(data)
111
+            if not "categories":
112
+                raise Exception("Error reading categories")
113
+            for item in r["categories"]:
114
+                data2 = "categories/%s"%(item["id"])
115
+                title = item["title"]
116
+                desc = title
117
+                img = self.img
118
+                content.append((title,self.name+"::"+data2,img,desc))
119
+            return content
120
+        
121
+        ### Catetory root ###
122
+        elif clist == "categories" and len(data.split("/"))==2:
123
+            r = self.call(data)
124
+            title = "%s - highlights"%r["category"]["title"]
125
+            content.append((title,self.name+"::"+data+"/highlights?lang=en&rights=mobile&availability=available",self.img,title))
126
+            title = "%s - recent (%s programmes, %s episodes)"%(r["category"]["title"],r["category"]["child_programme_count"],r["category"]["child_episode_count"])
127
+            content.append((title,self.name+"::"+data+"/programmes?rights=mobile&page=1&per_page=40&sort=recent&sort_direction=asc&initial_child_count=1&availability=available",self.img,title))
128
+            title = "%s - a-z (%s programmes, %s episodes)"%(r["category"]["title"],r["category"]["child_programme_count"],r["category"]["child_episode_count"])
129
+            content.append((title,self.name+"::"+data+"/programmes?rights=mobile&page=1&per_page=40&sort=title&sort_direction=asc&initial_child_count=1&availability=available",self.img,title))
130
+            return content
131
+        
132
+        ### Program/episodes list ###
133
+        elif   re.search("categories/([\w\-]+)/(highlights|programmes).+",data) or\
134
+               re.search("programmes/(\w+)/episodes.+",data) or\
135
+               re.search("groups/(\w+)/episodes.+",data) or\
136
+               re.search("atoz/([\w]+)/programmes.+",data) or\
137
+               re.search("channels/(\w+)/schedule/[\d\-].+",data) or\
138
+               re.search("channels/(\w+)/programmes.+",data) or\
139
+               re.search("channels/(\w+)/highlights.+",data) or\
140
+               data == "home/highlights":
141
+            r = self.call(data)
142
+            lst = r["category_highlights"] if "category_highlights" in r else\
143
+                  r["category_programmes"] if "category_programmes" in r else\
144
+                  r["programme_episodes"] if "programme_episodes" in r else\
145
+                  r["atoz_programmes"] if "atoz_programmes" in r else\
146
+                  r["group_episodes"] if "group_episodes" in r else\
147
+                  r["schedule"] if "schedule" in r else\
148
+                  r["channel_highlights"] if "channel_highlights" in r else\
149
+                  r["channel_programmes"] if "channel_programmes" in r else\
150
+                  r["home_highlights"] if "home_highlights" in r else\
151
+                  []
152
+            if not lst:
153
+                return content
154
+            for el in lst["elements"]:
155
+                if el["type"] == "broadcast":
156
+                    if not len(el["episode"]["versions"]):continue
157
+                    title,data2,img,desc = self.get_data_element(el["episode"])
158
+                    t1 = gt(el['scheduled_start'])
159
+                    t2 = gt(el['scheduled_end'])
160
+                    title = "[%s-%s]%s"%(t1.strftime("%d.%m.%Y %H:%M"),t2.strftime("%H:%M"),title)
161
+                else:
162
+                    title,data2,img,desc = self.get_data_element(el)
163
+                content.append((title,self.name+"::"+data2,img,desc))
164
+            
165
+            if "&page=" in data and lst["page"]*lst["per_page"]<lst["count"]:
166
+                data2 = re.sub("&page=\d+","&page=%s"%(lst["page"]+1),data)
167
+                content.append(("Next page",self.name+"::"+data2,self.img,"Next page")) 
168
+            return content
169
+        
170
+        ### A-z root ###
171
+        elif data=="a-z":
172
+            url = "http://www.bbc.co.uk/programmes/a-z/by/x/all.json?page=1"
173
+            r = self._http_request(url)
174
+            if not r:
175
+                raise Exception("Can not read %s"%s)
176
+            js = json.loads(r)
177
+            for ch in js["atoz"]["letters"]:
178
+                title = ch.upper()
179
+                desc = "Programmes beginning with %s"%title
180
+                img = self.img
181
+                data2 = "atoz/%s/programmes?rights=mobile&page=1&per_page=40&initial_child_count=1&sort=title&sort_direction=asc&availability=available"%ch
182
+                content.append((title,self.name+"::"+data2,img,desc))
183
+            return content
184
+        
185
+        ###  Channels home ###
186
+        elif data=="channels":
187
+            for ch in self.get_channels():
188
+                title = ch["title"]
189
+                img = self.logos[ch["id"]] if ch["id"] in self.logos else  "http://static.bbci.co.uk/mobileiplayerappbranding/1.9/android/images/channels/tv-guide-wide-logo/layout_normal/xxhdpi/%s_tv-guide-wide-logo.png"%ch["id"]
190
+                desc = title
191
+                data2 = "channels/%s"%ch["id"]
192
+                #ee = self.get_epg_live(ch["id"])
193
+                desc = title
194
+                content.append((title,self.name+"::"+data2,img,desc))
195
+            return content
196
+        
197
+        ### Channel higlihts/progrmmes/days ###
198
+        elif clist=="channels" and len(data.split("/"))==2:
199
+            r = self.call(data)
200
+            chid = data.split("/")[1]
201
+            ch = self.get_channel_by_id(chid)
202
+            
203
+            # Highlights
204
+            title = ch["title"] + " - highlights"
205
+            img = "http://static.bbci.co.uk/mobileiplayerappbranding/1.9/android/images/channels/tv-guide-wide-logo/layout_normal/xxhdpi/%s_tv-guide-wide-logo.png"%ch["id"]
206
+            data2 = "channels/%s/highlights?lang=en&rights=mobile&availability=available"%ch["id"]
207
+            desc = title
208
+            content.append((title,self.name+"::"+data2,img,desc))
209
+            
210
+            #AtoZ
211
+            title = ch["title"] + " - programmes AtoZ"
212
+            data2 = "channels/%s/programmes?rights=mobile&page=1&per_page=40&sort=recent&sort_direction=asc&initial_child_count=1&availability=available"%ch["id"]
213
+            desc = title
214
+            content.append((title,self.name+"::"+data2,img,desc))
215
+            
216
+            day0 = datetime.date.today()
217
+            for i in range(10):
218
+                day = day0-datetime.timedelta(days=i)
219
+                days = day.strftime("%Y-%m-%d")
220
+                title = ch["title"] + " - " + days
221
+                img = "http://static.bbci.co.uk/mobileiplayerappbranding/1.9/android/images/channels/tv-guide-wide-logo/layout_normal/xxhdpi/%s_tv-guide-wide-logo.png"%ch["id"]
222
+                data2 = "channels/%s/schedule/%s?availability=available"%(ch["id"],days)
223
+                #ee = self.get_epg_live(ch["id"])
224
+                desc = title
225
+                content.append((title,self.name+"::"+data2,img,desc))
226
+            return content
227
+        
228
+            
229
+    def get_streams(self, data):
230
+        print "[iplayer] get_streams:", data
231
+        if "::" in data: data = data.split("::")[1]         
232
+        if not self.is_video(data):
233
+            return []
234
+        cmd = data.split("/")
235
+        vid = cmd[1].split("?")[0]
236
+        if cmd[0] == "live":
237
+            title,img,desc = self.get_epg_live(vid)
238
+        else:
239
+            data_ = "episodes/%s"%vid
240
+            r = self.call(data_)
241
+            title,img,desc,vid = self.get_epg_video(vid)
242
+        url = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/format/json/mediaset/iptv-all/vpid/%s"%vid
243
+        print "vid=%s"%vid
244
+        print url
245
+        r = self._http_request(url) #,headers=self.headers2
246
+        if not r:
247
+            raise Exception("No streams found")
248
+        js = json.loads(r)
249
+        if "result" in js and js["result"]=="geolocation":
250
+            raise Exception("BBC iPlayer service available only from UK")
251
+        if not "media" in js:
252
+            raise Exception("No streams found")
253
+        streams = []
254
+        captions = []
255
+        for s in js["media"]:
256
+            if s["kind"] == "captions":
257
+                if s["connection"][0]["href"]:
258
+                    sub = {}
259
+                    sub["url"] = s["connection"][0]["href"].encode('utf8')
260
+                    sub["type"] = s["type"]
261
+                    sub["name"] = s["service"] if "service" in s else "captions (taff)"
262
+                    sub["lang"] = "en"
263
+                    captions.append(sub)
264
+                    
265
+            if s["kind"] <> "video":
266
+                continue
267
+            for c in s["connection"]:
268
+                if c["transferFormat"] <> "hls": continue
269
+                #if not (c["supplier"].startswith("mf_") or c["supplier"].startswith("ll_")) : continue # TODO ir kaut kādas VPN problēmas ar akamaihd
270
+                #if c["priority"] <> "1": continue
271
+                url=c["href"].encode("utf8")
272
+                r2 = self._http_request(url)
273
+                if not r2: continue
274
+                slist = re.findall("#EXT-X-STREAM-INF:([^\n]+)\n([^\n]+)", r2, re.DOTALL)
275
+                if not slist:
276
+                    stream = stream0.copy()
277
+                    stream["url"]=url
278
+                    stream["name"]=title 
279
+                    stream["desc"]=desc
280
+                    stream["img"]=img
281
+                    stream["type"]="hls"
282
+                    stream["quality"]=("%s %sx%s %s,%s"%(s["bitrate"],s["width"],s["height"],c["supplier"],c["priority"])).encode("utf8")
283
+                    stream["lang"]="en"
284
+                    stream["subs"]=captions
285
+                    stream["order"]=int(s["bitrate"])
286
+                    streams.append(stream)
287
+                else:
288
+                    for cc in slist:
289
+                        m = re.search("RESOLUTION=([\dx]+)",cc[0])
290
+                        resolution = m.group(1) if m else "%sx%s"%(s["width"],s["height"])
291
+                        m = re.search("BANDWIDTH=([\d]+)",cc[0])
292
+                        bitrate = m.group(1) if m else s["bitrate"]
293
+                        url2 = cc[1].encode("utf8")
294
+                        if not url2.startswith("http"):
295
+                            uu = url.split("/")[:-1]
296
+                            uu.append(url2)
297
+                            url2 = "/".join(uu)
298
+                        stream = stream0.copy()
299
+                        stream["url"]=url2
300
+                        stream["name"]=title 
301
+                        stream["desc"]=desc
302
+                        stream["img"]=img
303
+                        stream["type"]="hls"
304
+                        stream["quality"]=("%s %s %s,%s"%(bitrate,resolution,c["supplier"],c["priority"])).encode("utf8")
305
+                        stream["lang"]="en"
306
+                        stream["subs"]=captions
307
+                        stream["order"]=int(bitrate)
308
+                        streams.append(stream)
309
+        if captions:
310
+            for s in streams:
311
+                s["subs"]=captions
312
+        streams = sorted(streams,key=lambda item: item["order"],reverse=True)
313
+        return streams
314
+        
315
+    def is_video(self,data):
316
+        if "::" in data:
317
+            data = data.split("::")[1]
318
+        cmd = data.split("/")
319
+        if cmd[0]=="live" and  len(cmd)==2:
320
+            return True
321
+        elif cmd[0]=="episodes" and len(cmd)==2:
322
+            return True
323
+        else:
324
+            return False
325
+        
326
+    def get_data_element(self,item):
327
+        if ("programme" in item["type"] or "group" in item["type"]) and item["count"]>1:
328
+            ep = item.copy()
329
+        elif ("programme" in item["type"] or "group" in item["type"]) and item["count"]==1:
330
+            ep = item["initial_children"][0].copy()
331
+        elif item["type"] == "episode":
332
+            ep = item.copy()
333
+        elif item["type"] == "broadcast":
334
+            ep = item["episode"].copy()
335
+        else:
336
+            ep = item.copy()
337
+        title = ep["title"]
338
+        if "subtitle" in ep and ep["subtitle"]:
339
+            title = title+". "+ ep["subtitle"]
340
+        desc = ep["synopses"]["large"] if "large" in ep["synopses"] else ep["synopses"]["medium"] if "medium" in ep["synopses"] else ep["synopses"]["small"]
341
+        #TODO papildus info pie apraksta
342
+        img = ep["images"]["standard"].replace("{recipe}","512x288") if "images" in ep else self.img
343
+        if ep["type"] == "episode":
344
+            data2 = "episodes/%s"%ep["id"]
345
+        elif "programme" in ep["type"]:
346
+            data2 = "programmes/%s/episodes?per_page=40&page=1"%ep["id"]
347
+            title = "%s [%s episodes]"%(title,ep["count"])
348
+        elif "group" in ep["type"]:
349
+            data2 = "groups/%s/episodes?per_page=40&page=1"%ep["id"]
350
+            title = "%s [%s episodes]"%(title,ep["count"])
351
+        else:
352
+            data2 = "programmes/%s/episodes?per_page=40&page=1"%ep["id"]
353
+            title = "%s [%s episodes]"%(title,ep["count"])            
354
+        return title,data2,img,desc
355
+    
356
+    def get_epg_video(self,vid):
357
+        data = "episodes/%s"%vid
358
+        r = self.call(data)
359
+        if "episodes" in r :
360
+            ep = r["episodes"][0]
361
+            title = ep["title"]
362
+            if "subtitle" in ep:
363
+                title = title +". "+ ep["subtitle"]
364
+            title = title
365
+            desc = ep["synopses"]["medium"] if "medium" in ep["synopses"] else p["synopses"]["small"] if "small" in ep["synopses"] else title
366
+            desc = desc
367
+            ver = ep["versions"][0]
368
+            vid = ver["id"]
369
+            remaining = ver["availability"]["remaining"]["text"]
370
+            duration = ver["duration"]
371
+            first_broadcast = ver["first_broadcast"]
372
+            desc =u"%s\n%s\%s\n%s\n%s"%(title,duration,remaining,first_broadcast,desc)
373
+            img = ep["images"]["standard"].replace("{recipe}","512x288")
374
+            return title.encode("utf8"),img.encode("utf8"),desc.encode("utf8"),vid.encode("utf8")
375
+        else:
376
+            raise Exception("No video info")
377
+    
378
+    def get_epg_live(self,channelid):
379
+        data = "channels/%s/highlights?live=true"%channelid
380
+        r = self.call(data)
381
+        if "channel_highlights" in r and r["channel_highlights"]["elements"][0]["id"] == "live":
382
+            epg = r["channel_highlights"]["elements"][0]["initial_children"][0].copy()
383
+            t1 = gt(epg['scheduled_start'])
384
+            t2 = gt(epg['scheduled_end'])
385
+            ep = epg["episode"]
386
+            title = ep["title"]
387
+            if "subtitle" in ep:
388
+                title = title +". "+ ep["subtitle"]
389
+            title = "%s (%s-%s)"%(title,t1.strftime("%H:%M"),t2.strftime("%H:%M"))
390
+            title = title
391
+            desc = ep["synopses"]["medium"] if "medium" in ep["synopses"] else p["synopses"]["small"] if "small" in ep["synopses"] else title
392
+            desc = desc
393
+            desc ="%s\n%s"%(title,desc)
394
+            img = ep["images"]["standard"].replace("{recipe}","512x288")
395
+            #return title,img,desc
396
+        else:
397
+            title = r["channel_highlights"]["channel"]["title"]
398
+            img = ""
399
+            desc = title
400
+            
401
+        return title.encode("utf8"),img.encode("utf8"),desc.encode("utf8")
402
+        
403
+    def get_channels(self):
404
+        if self.ch:
405
+            return self.ch
406
+        r= self.call("channels")
407
+        self.ch=[]
408
+        for i,item in enumerate(r["channels"]):
409
+            self.ch.append(item)
410
+            self.ch_id[item["id"]]=i
411
+            self.ch_id2[item["master_brand_id"]]=i
412
+            self.ch_name[item["title"]]=i
413
+        return self.ch
414
+                
415
+    def get_channel_by_id(self,chid):
416
+        if not self.ch:
417
+            self.get_channels()
418
+        if not self.ch:
419
+            return None
420
+        return self.ch[self.ch_id[chid]] if self.ch_id.has_key(chid) else None
421
+    
422
+    def get_channel_by_id2(self,chid):
423
+        if not self.ch:
424
+            self.get_channels()
425
+        if not self.ch:
426
+            return None
427
+        return self.ch[self.ch_id2[chid]] if self.ch_id2.has_key(chid) else None
428
+     
429
+    def get_channel_by_name(self,name):
430
+        if not self.ch:
431
+            self.get_channels()
432
+        ch2 = self.get_channel_by_name2(name)
433
+        if not ch2:
434
+            return None
435
+        ch = self.get_channel_by_id2(ch2["id2"]) 
436
+        return ch
437
+                
438
+        
439
+    def call(self, data,params = None, headers=None):
440
+        if not headers: headers = self.headers
441
+        #if not lang: lang = self.country
442
+        url = self.api_url + data
443
+        content = self._http_request(url,params, headers)
444
+        if content:
445
+            try:
446
+                result = json.loads(content)
447
+                return result
448
+            except Exception, ex:
449
+                return None
450
+        else:
451
+            return None
452
+        
453
+    def call2(self, data,params = None, headers=None):
454
+        if not headers: headers = self.headers2
455
+        #if not lang: lang = self.country
456
+        url = self.api_url2 + data
457
+        content = self._http_request(url,params, headers)
458
+        return content
459
+
460
+    def _http_request(self, url,params = None, headers=None):
461
+        if not headers: headers = self.headers
462
+        import requests
463
+        try:
464
+            r = requests.get(url, headers=headers)
465
+            return r.content
466
+        
467
+        except Exception as ex:
468
+            if ex.code==403:
469
+                return ex.read()
470
+            else:
471
+                return None
472
+
473
+def gt(dt_str):
474
+    dt, _, us= dt_str.partition(".")
475
+    dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
476
+    dt = dt - datetime.timedelta(seconds=time.altzone)
477
+    #us= int(us.rstrip("Z"), 10)
478
+    #r = dt + datetime.timedelta(microseconds=us)a
479
+    return dt
480
+   
481
+if __name__ == "__main__":
482
+    c = Source()
483
+    from subprocess import call
484
+    #ch = c.get_channels()
485
+    #c.get_epg_live("bbc_two_england")
486
+    
487
+    if len(sys.argv)>1 and  not "iplayer::" in sys.argv[1]:
488
+        
489
+        vid = sys.argv[1]
490
+        print "login - %s"%c.login("ivars777","xxx")        
491
+        vid = "1069"
492
+        vid = "1462566072086"
493
+        channelid="101"
494
+        vid = "1350462656767"
495
+        #data = c.get_stream_url(vid,"vod")
496
+        #call([r"c:\Program Files\VideoLAN\VLC\vlc.exe",data["stream"]])
497
+        pass
498
+ 
499
+
500
+        
501
+    else:
502
+        if len(sys.argv)>1:
503
+            data= sys.argv[1]
504
+        else:
505
+            data = "iplayer::home"
506
+        content = c.get_content(data)
507
+        for item in content:
508
+            print item
509
+        #cat = api.get_categories(country)
510
+        #chan = api.get_channels("lv")
511
+        #prog = api.get_programs(channel=6400)
512
+        #prog = api.get_programs(category=55)
513
+        #seas = api.get_seasons(program=6453)
514
+        #str = api.get_streams(660243)
515
+        #res = api.get_videos(802)
516
+        #formats = api.getAllFormats()
517
+        #det = api.detailed("1516")
518
+        #vid = api.getVideos("13170")
519
+        pass

+ 261
- 0
sources/jsinterp.py View File

@@ -0,0 +1,261 @@
1
+# This code comes from youtube-dl: https://github.com/rg3/youtube-dl/blob/master/youtube_dl/jsinterp.py
2
+
3
+from __future__ import unicode_literals
4
+
5
+import json
6
+import operator
7
+import re
8
+
9
+
10
+_OPERATORS = [
11
+    ('|', operator.or_),
12
+    ('^', operator.xor),
13
+    ('&', operator.and_),
14
+    ('>>', operator.rshift),
15
+    ('<<', operator.lshift),
16
+    ('-', operator.sub),
17
+    ('+', operator.add),
18
+    ('%', operator.mod),
19
+    ('/', operator.truediv),
20
+    ('*', operator.mul),
21
+]
22
+_ASSIGN_OPERATORS = [(op + '=', opfunc) for op, opfunc in _OPERATORS]
23
+_ASSIGN_OPERATORS.append(('=', lambda cur, right: right))
24
+
25
+_NAME_RE = r'[a-zA-Z_$][a-zA-Z_$0-9]*'
26
+
27
+
28
+class JSInterpreter(object):
29
+    def __init__(self, code, objects=None):
30
+        if objects is None:
31
+            objects = {}
32
+        self.code = code
33
+        self._functions = {}
34
+        self._objects = objects
35
+
36
+    def interpret_statement(self, stmt, local_vars, allow_recursion=100):
37
+        if allow_recursion < 0:
38
+            print '[JSInterpreter] Recursion limit reached'
39
+            return None
40
+
41
+        should_abort = False
42
+        stmt = stmt.lstrip()
43
+        stmt_m = re.match(r'var\s', stmt)
44
+        if stmt_m:
45
+            expr = stmt[len(stmt_m.group(0)):]
46
+        else:
47
+            return_m = re.match(r'return(?:\s+|$)', stmt)
48
+            if return_m:
49
+                expr = stmt[len(return_m.group(0)):]
50
+                should_abort = True
51
+            else:
52
+                # Try interpreting it as an expression
53
+                expr = stmt
54
+
55
+        v = self.interpret_expression(expr, local_vars, allow_recursion)
56
+        return v, should_abort
57
+
58
+    def interpret_expression(self, expr, local_vars, allow_recursion):
59
+        expr = expr.strip()
60
+
61
+        if expr == '':  # Empty expression
62
+            return None
63
+
64
+        if expr.startswith('('):
65
+            parens_count = 0
66
+            for m in re.finditer(r'[()]', expr):
67
+                if m.group(0) == '(':
68
+                    parens_count += 1
69
+                else:
70
+                    parens_count -= 1
71
+                    if parens_count == 0:
72
+                        sub_expr = expr[1:m.start()]
73
+                        sub_result = self.interpret_expression(
74
+                            sub_expr, local_vars, allow_recursion)
75
+                        remaining_expr = expr[m.end():].strip()
76
+                        if not remaining_expr:
77
+                            return sub_result
78
+                        else:
79
+                            expr = json.dumps(sub_result) + remaining_expr
80
+                        break
81
+            else:
82
+                print '[JSInterpreter] Premature end of parens in %r' % expr
83
+                return None
84
+
85
+        for op, opfunc in _ASSIGN_OPERATORS:
86
+            m = re.match(r'''(?x)
87
+                (?P<out>%s)(?:\[(?P<index>[^\]]+?)\])?
88
+                \s*%s
89
+                (?P<expr>.*)$''' % (_NAME_RE, re.escape(op)), expr)
90
+            if not m:
91
+                continue
92
+            right_val = self.interpret_expression(
93
+                m.group('expr'), local_vars, allow_recursion - 1)
94
+
95
+            if m.groupdict().get('index'):
96
+                lvar = local_vars[m.group('out')]
97
+                idx = self.interpret_expression(
98
+                    m.group('index'), local_vars, allow_recursion)
99
+                assert isinstance(idx, int)
100
+                cur = lvar[idx]
101
+                val = opfunc(cur, right_val)
102
+                lvar[idx] = val
103
+                return val
104
+            else:
105
+                cur = local_vars.get(m.group('out'))
106
+                val = opfunc(cur, right_val)
107
+                local_vars[m.group('out')] = val
108
+                return val
109
+
110
+        if expr.isdigit():
111
+            return int(expr)
112
+
113
+        var_m = re.match(
114
+            r'(?!if|return|true|false)(?P<name>%s)$' % _NAME_RE,
115
+            expr)
116
+        if var_m:
117
+            return local_vars[var_m.group('name')]
118
+
119
+        try:
120
+            return json.loads(expr)
121
+        except ValueError:
122
+            pass
123
+
124
+        m = re.match(
125
+            r'(?P<var>%s)\.(?P<member>[^(]+)(?:\(+(?P<args>[^()]*)\))?$' % _NAME_RE,
126
+            expr)
127
+        if m:
128
+            variable = m.group('var')
129
+            member = m.group('member')
130
+            arg_str = m.group('args')
131
+
132
+            if variable in local_vars:
133
+                obj = local_vars[variable]
134
+            else:
135
+                if variable not in self._objects:
136
+                    self._objects[variable] = self.extract_object(variable)
137
+                obj = self._objects[variable]
138
+
139
+            if arg_str is None:
140
+                # Member access
141
+                if member == 'length':
142
+                    return len(obj)
143
+                return obj[member]
144
+
145
+            assert expr.endswith(')')
146
+            # Function call
147
+            if arg_str == '':
148
+                argvals = tuple()
149
+            else:
150
+                argvals = tuple([
151
+                    self.interpret_expression(v, local_vars, allow_recursion)
152
+                    for v in arg_str.split(',')])
153
+
154
+            if member == 'split':
155
+                assert argvals == ('',)
156
+                return list(obj)
157
+            if member == 'join':
158
+                assert len(argvals) == 1
159
+                return argvals[0].join(obj)
160
+            if member == 'reverse':
161
+                assert len(argvals) == 0
162
+                obj.reverse()
163
+                return obj
164
+            if member == 'slice':
165
+                assert len(argvals) == 1
166
+                return obj[argvals[0]:]
167
+            if member == 'splice':
168
+                assert isinstance(obj, list)
169
+                index, howMany = argvals
170
+                res = []
171
+                for i in range(index, min(index + howMany, len(obj))):
172
+                    res.append(obj.pop(index))
173
+                return res
174
+
175
+            return obj[member](argvals)
176
+
177
+        m = re.match(
178
+            r'(?P<in>%s)\[(?P<idx>.+)\]$' % _NAME_RE, expr)
179
+        if m:
180
+            val = local_vars[m.group('in')]
181
+            idx = self.interpret_expression(
182
+                m.group('idx'), local_vars, allow_recursion - 1)
183
+            return val[idx]
184
+
185
+        for op, opfunc in _OPERATORS:
186
+            m = re.match(r'(?P<x>.+?)%s(?P<y>.+)' % re.escape(op), expr)
187
+            if not m:
188
+                continue
189
+            x, abort = self.interpret_statement(
190
+                m.group('x'), local_vars, allow_recursion - 1)
191
+            if abort:
192
+                print '[JSInterpreter] Premature left-side return of %s in %r' % (op, expr)
193
+                return None
194
+            y, abort = self.interpret_statement(
195
+                m.group('y'), local_vars, allow_recursion - 1)
196
+            if abort:
197
+                print '[JSInterpreter] Premature right-side return of %s in %r' % (op, expr)
198
+                return None
199
+            return opfunc(x, y)
200
+
201
+        m = re.match(
202
+            r'^(?P<func>%s)\((?P<args>[a-zA-Z0-9_$,]+)\)$' % _NAME_RE, expr)
203
+        if m:
204
+            fname = m.group('func')
205
+            argvals = tuple([
206
+                int(v) if v.isdigit() else local_vars[v]
207
+                for v in m.group('args').split(',')])
208
+            if fname not in self._functions:
209
+                self._functions[fname] = self.extract_function(fname)
210
+            return self._functions[fname](argvals)
211
+
212
+        print '[JSInterpreter] Unsupported JS expression %r' % expr
213
+        return None
214
+
215
+    def extract_object(self, objname):
216
+        obj = {}
217
+        obj_m = re.search(
218
+            (r'(?:var\s+)?%s\s*=\s*\{' % re.escape(objname)) +
219
+            r'\s*(?P<fields>([a-zA-Z$0-9]+\s*:\s*function\(.*?\)\s*\{.*?\}(?:,\s*)?)*)' +
220
+            r'\}\s*;',
221
+            self.code)
222
+        fields = obj_m.group('fields')
223
+        # Currently, it only supports function definitions
224
+        fields_m = re.finditer(
225
+            r'(?P<key>[a-zA-Z$0-9]+)\s*:\s*function'
226
+            r'\((?P<args>[a-z,]+)\){(?P<code>[^}]+)}',
227
+            fields)
228
+        for f in fields_m:
229
+            argnames = f.group('args').split(',')
230
+            obj[f.group('key')] = self.build_function(argnames, f.group('code'))
231
+
232
+        return obj
233
+
234
+    def extract_function(self, funcname):
235
+        func_m = re.search(
236
+            r'''(?x)
237
+                (?:function\s+%s|[{;,]%s\s*=\s*function|var\s+%s\s*=\s*function)\s*
238
+                \((?P<args>[^)]*)\)\s*
239
+                \{(?P<code>[^}]+)\}''' % (
240
+                re.escape(funcname), re.escape(funcname), re.escape(funcname)),
241
+            self.code)
242
+        if func_m is None:
243
+            print '[JSInterpreter] Could not find JS function %r' % funcname
244
+            return None
245
+        argnames = func_m.group('args').split(',')
246
+
247
+        return self.build_function(argnames, func_m.group('code'))
248
+
249
+    def call_function(self, funcname, *args):
250
+        f = self.extract_function(funcname)
251
+        return f(args)
252
+
253
+    def build_function(self, argnames, code):
254
+        def resf(args):
255
+            local_vars = dict(zip(argnames, args))
256
+            for stmt in code.split(';'):
257
+                res, abort = self.interpret_statement(stmt, local_vars)
258
+                if abort:
259
+                    break
260
+            return res
261
+        return resf

+ 202
- 0
sources/kinofilmnet.py View File

@@ -0,0 +1,202 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+import urllib2, urllib
13
+import datetime, re, sys,os
14
+import ConfigParser
15
+from collections import OrderedDict
16
+from SourceBase import SourceBase
17
+import resolver
18
+import util
19
+
20
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
21
+import HTMLParser
22
+h = HTMLParser.HTMLParser()
23
+    
24
+class Source(SourceBase):
25
+    
26
+    def __init__(self,country=""):
27
+        self.name = "kinofilmnet"
28
+        self.title = "KinoFilmNet.lv"
29
+        self.img = "http://kinofilmnet.lv/MusuBaneri/1268.png"
30
+        self.desc = "Kinofilmnet.lv satura skatīšanās"
31
+        self.country=country
32
+        self.headers = headers2dict("""
33
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
34
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
35
+Accept-Language: en-US,en;q=0.5
36
+""")
37
+        self.headers2 = headers2dict("""
38
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
39
+X-Requested-With: XMLHttpRequest
40
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
41
+""")
42
+        self.url = "http://kinofilmnet.lv/"
43
+
44
+
45
+    ######### Entry point ########        
46
+    def get_content(self, data):
47
+        print "[kinofilmnet] get_content:", data
48
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
49
+        content=[]
50
+        content.append(("..return", "back","","Return back"))
51
+        
52
+        if clist=="home":
53
+            content.extend([
54
+                ("Meklēt", "kinofilmnet::search/?q={0}","","Meklēt"),                
55
+                ("Jaunākās", "kinofilmnet::jaunakas","","Visu žanru jaunākās filmas"),
56
+                ("Top50", "kinofilmnet::top50","","Top 50 filmas"),
57
+            ])
58
+            r = self.call("")
59
+            i = r.find('<div class="cat-title">Meklēt pēc žanriem</div>')
60
+            if i<=0:
61
+                return content
62
+            for item in re.findall('<li><a href=".*load/([^"]+)">([^<]+)</a></li>', r[i:]):
63
+                title = item[1]
64
+                data2 = "filmas/" + item[0]
65
+                img = self.img
66
+                desc = title
67
+                content.append((title,self.name+"::"+data2,img,desc))      
68
+            return content
69
+    
70
+        elif clist=="search":
71
+            r=self.call(data)
72
+            result = re.findall('<a href="([^"]+)"> (.+?) </a></div>.+?> (.+?)</div>', r, re.DOTALL)            
73
+            for item in result:
74
+                title = item[1].replace("<b>","").replace("</b>","")
75
+                data2 = item[0].replace("http://kinofilmnet.lv/","")
76
+                img = self.img
77
+                desc = item[2].replace("<b>","").replace("</b>","")
78
+                content.append((title,self.name+"::"+data2,img,desc))
79
+            if '<span>&raquo;</span>' in r:
80
+                m = re.search("p=(\d+)",data)
81
+                if m:
82
+                    page = int(m.group(1))+1
83
+                    data2 = re.sub(r"p=\d+", r"p=%s"%page, data)
84
+                    content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))               
85
+            return content
86
+            
87
+        # Žanru saraksti ##
88
+        elif clist in ("jaunakas","top50") or clist=="filmas" and len(plist)==3:
89
+            if clist == "jaunakas":
90
+                r = self.call("")
91
+            else:
92
+                r = self.call(data)
93
+            #r = r.decode("cp1251").encode("utf8")
94
+            result = re.findall(r'<div id="entryID\w+">.+?<img src="([^"]+)" alt="([^"]+)".+?data-link="([^"]+)".+?<span>([^<]*)</span>.+?<div class="movie-date">([^<]+)</div>.+?<div class="movie-director"><b>Žanrs:</b> ([^<]+)</div>.+?<div class="movie-text">([^<]+)</div>', r, re.DOTALL)
95
+            for item in result:
96
+                title = item[1]+"[%s]"%item[3]
97
+                img = "http://kinofilmnet.lv"+item[0]
98
+                data2 = item[2][1:]
99
+                desc = "%s %s\n%s"%(item[4],item[5],item[6])
100
+                content.append((title,self.name+"::"+data2,img,desc))                      
101
+            m = re.search('href="/([^"]+)" onclick="[^"]+" ><span>&raquo;', r, re.DOTALL)
102
+            if m:
103
+                data2 = m.group(1)
104
+                content.append(("Next page",self.name+"::"+data2,self.img,"Next page"))    
105
+            return content      
106
+         
107
+        ### Seriāls ###
108
+        elif clist=="filmas" and len(plist)==4:
109
+            r = self.call(path)
110
+            title0 = re.search("<h1>([^<]+)</h1>", r, re.DOTALL).group(1)
111
+            desc0 = re.search('<div class="m-desc full-text clearfix">([^<]+)</div>', r, re.DOTALL).group(1)
112
+            img0 = "http://kinofilmnet.lv" + re.search('<div class="m-img">.*?<img src="([^"]+)" alt', r, re.DOTALL).group(1)
113
+            result = re.findall(r'<summary>([^<]+)</summary><iframe src="https://openload\.co/embed/[^/]+/"', r, re.DOTALL)
114
+            i = 1
115
+            for item in result:
116
+                title = title0+" - " + item
117
+                data2 = data+"?s=%s"%i
118
+                img = img0
119
+                desc = desc0
120
+                content.append((title,self.name+"::"+data2,img,desc))                                      
121
+                i += 1
122
+            return content                    
123
+            
124
+        ### kaut kas neparedzets ###
125
+        else:
126
+            return content                            
127
+              
128
+    def is_video(self,data):
129
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
130
+        if clist=="filmas" and len(plist) == 4 and "s"in qs:
131
+            return True
132
+        if not (clist=="filmas" and len(plist) == 4):
133
+            return False
134
+        if "sezona" in data:
135
+            return False
136
+        r = self.call(path)
137
+        result = re.findall(r'iframe src="https://openload\.co/embed/[^/]+/"', r, re.DOTALL)
138
+        if len(result)>1:
139
+            return False
140
+        else:
141
+            return True
142
+
143
+        
144
+    def call(self, data,params=None,headers=None,lang=""):
145
+        if not headers: headers = self.headers
146
+        url = self.url+data
147
+        result = self._http_request(url,params,headers=headers)
148
+        return result
149
+      
150
+    def get_streams(self,data):
151
+        print "[kinofilmnet] get_streams:", data
152
+        #if not self.is_video(data):
153
+            #return []
154
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
155
+        r = self.call(path)
156
+        title = re.search("<h1>([^<]+)</h1>", r, re.DOTALL).group(1)
157
+        desc = re.search('<div class="m-desc full-text clearfix">([^<]+)</div>', r, re.DOTALL).group(1)
158
+        img = "http://kinofilmnet.lv" + re.search('<div class="m-img">.*?<img src="([^"]+)" alt', r, re.DOTALL).group(1)
159
+        if "s" in qs: # serialā sērija
160
+            result = re.findall(r'<summary>([^<]+)</summary><iframe src="([^"]+)"', r, re.DOTALL)
161
+            i = int(qs["s"])-1
162
+            url0 = result[i][1]
163
+            title = title + " - " + result[i][0]
164
+        else:
165
+            #iframe src="https://openload.co/embed/wlw6Vl9zwL0/" 
166
+            result = re.findall(r'<iframe src="([^"]+)"', r, re.DOTALL)
167
+            if not result:
168
+                return []
169
+            url0 = result[0]
170
+        streams =  resolver.resolve(url0)
171
+        for s in streams:
172
+            if "hqq." in url0:
173
+                s["url"] = util.streamproxy_encode(s["url"],s["headers"])
174
+                s["headers"]={}
175
+            s["name"] = title
176
+            s["desc"] = desc
177
+            s["img"] = img
178
+            s["type"] = self.stream_type(s["url"])
179
+            #s["lang"] = lang        
180
+        return streams
181
+        
182
+if __name__ == "__main__":
183
+    country= "lv"
184
+    c = Source(country)
185
+    if len(sys.argv)>1:
186
+        data= sys.argv[1]
187
+    else:
188
+        data = "home"
189
+    content = c.get_content(data)
190
+    for item in content:
191
+        print item
192
+    #cat = api.get_categories(country)
193
+    #chan = api.get_channels("lv")
194
+    #prog = api.get_programs(channel=6400)
195
+    #prog = api.get_programs(category=55)
196
+    #seas = api.get_seasons(program=6453)
197
+    #str = api.get_streams(660243)
198
+    #res = api.get_videos(802)
199
+    #formats = api.getAllFormats()
200
+    #det = api.detailed("1516")
201
+    #vid = api.getVideos("13170")
202
+    pass

+ 1018
- 0
sources/ltc.py
File diff suppressed because it is too large
View File


+ 279
- 0
sources/mtgplay.py View File

@@ -0,0 +1,279 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+
9
+
10
+try:
11
+    import json
12
+except:
13
+    import simplejson as json
14
+#!/usr/bin/env python
15
+# coding=utf8
16
+import urllib2, urllib
17
+import datetime, re, sys
18
+from SourceBase import SourceBase
19
+
20
+API_URL = 'http://playapi.mtgx.tv/v3/'
21
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
22
+headers0 = headers2dict("""
23
+User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
24
+""")
25
+
26
+REGIONS = [
27
+    ("Latvia",None,"lv",""),
28
+    ("Estonia",None,"ee",""),
29
+    ("Lituania",None,"lt",""),
30
+    ("Sweden",None,"se",""),
31
+    ("Denmark",None,"dk",""),
32
+    ("Norway",None,"no",""),
33
+    ("Bulgaria",None,"bg","")
34
+]
35
+
36
+
37
+class Source(SourceBase):
38
+    
39
+    def __init__(self,country="lv"):
40
+        self.name = "mtgplay"
41
+        self.title = "Skaties.lv (TV3)"
42
+        self.img = "http://skaties.lv/touch-icon-192x192.png"
43
+        self.desc = "MTG skaties.lv satura skatīšanās (LNT,TV3, TV6 u.c.)"
44
+        
45
+        self.country=country
46
+        self.pic_size = "327x250" #"1000x765"
47
+        
48
+    def get_content(self, data):
49
+        print "[mtgplay] get_content:", data
50
+        if "::" in data:
51
+            data = data.split("::")[1]        
52
+        if "/" in data:
53
+            citem,cid = data.split("/")
54
+            clist = ""
55
+        else:
56
+            clist = data.split("?")[0]
57
+            qs = dict(map(lambda x:x.split("="),re.findall("\w+=\w+",data)))
58
+            citem,cid = ("","")
59
+            self.country = qs["country"] if "country" in qs else "lv"
60
+        
61
+        content=[]
62
+        content.append(("..return", "back","","Return back"))
63
+        
64
+        if clist=="home":
65
+            content.extend([
66
+                #("Search", "mtgplay::meklet?country=%s&term={0}"%self.country,"","Search videos"), ### TODO                
67
+                ("TV Live", "mtgplay::videos?country=%s&order=title&type=live"%self.country,"","TV live streams(not always available)"),
68
+                ("Last videos", "mtgplay::videos?country=%s&order=-airdate"%self.country,"","Last aired videos"),
69
+                ("Categories", "mtgplay::categories?country=%s&order=name"%self.country,"","Categories"),
70
+                ("Channels", "mtgplay::channels?country=%s&order=id"%self.country,"","TV channels"),
71
+                ("Programs by name", "mtgplay::formats?country=%s&order=-title"%self.country,"","Programs by name"),             
72
+                ("Programs by popularity", "mtgplay::formats?country=%s&order=-popularity"%self.country,"","Programs by popularity")             
73
+            ])
74
+            return content
75
+        
76
+        r = self.call(data)
77
+        if not r:
78
+            content.append(("Error", "","","Error reading '%s'"%data))
79
+            return content
80
+        
81
+        if clist:
82
+            if r["_links"].has_key("prev"):
83
+                data2 = r["_links"]["prev"]["href"].replace(API_URL,"")
84
+                content.append(("Previous page", self.name+"::"+data2.encode("utf8"),"", "Goto previous page"))
85
+                
86
+            if "_embedded" in r:
87
+                for item in r["_embedded"][clist]:
88
+                    if "title" in item:
89
+                        title = item["title"]
90
+                    elif "name" in item:
91
+                        title = item["name"]
92
+                    #data2 = self.name+"::"+"%s/%s"%(clist,item["id"]) 
93
+                    img = item["_links"]["image"]["href"].replace("{size}",self.pic_size) if "image" in item["_links"] else ""
94
+                    desc = item["summary"] if "summary" in item and item["summary"] else ""
95
+                    
96
+                    ### Video ###
97
+                    if clist=="videos":
98
+                        data2 = "videos/%s"%item["id"]                            
99
+                        summary = item["summary"] if item["summary"] else ""
100
+                        air_at = item["broadcasts"][0]["air_at"] if "broadcasts" in item and len(item["broadcasts"])>0 and "air_at" in item["broadcasts"][0] else ""
101
+                        if not air_at:
102
+                            air_at = item["publish_at"] if "publish_at" in item else ""
103
+                        air_at = air_at[0:16].replace("T"," ") if air_at else ""
104
+                        try: playable_to = item["broadcasts"][0]["playable_to"]
105
+                        except: playable_to =""
106
+                        playable_to = "(till "+playable_to[0:10].replace("T"," ")+")" if playable_to else ""
107
+                        duration = item["duration"] if "duration" in item else ""
108
+                        duration = str(datetime.timedelta(seconds=int(duration))) if duration else ""
109
+                        try:
110
+                            views = item["views"]["total"] if "views" in item and "total" in item["views"] else ""
111
+                            views = views+" views"
112
+                        except: views = ""
113
+                        desc = "Aired: %s %s\nDuration: %s %s\n\n%s"%(air_at, playable_to,duration,views,summary)
114
+                        
115
+                    ### Categories ###     
116
+                    elif clist == "categories":
117
+                        #data2 = item["_links"]["formats"]["href"].replace(API_URL,"")
118
+                        data2 = "formats?category=%s"%item["id"]
119
+                        if "country" in qs: data2 += "&country="+qs["country"]
120
+                        if "category" in qs: data2 += "&category="+qs["category"]
121
+                        if "channel" in qs: data2 += "&channel="+qs["channel"]
122
+                        data2 += "&order=title"
123
+                        
124
+                    ### Channels ###     
125
+                    elif clist == "channels":
126
+                        #data2 = item["_links"]["categories"]["href"].replace(API_URL,"")
127
+                        data2 = "categories?channel=%s"%item["id"]
128
+                        if "country" in qs: data2 += "&country="+qs["country"]
129
+                        if "category" in qs: data2 += "&category="+qs["category"]
130
+                        if "channel" in qs: data2 += "&channel="+qs["channel"]
131
+                        data2 += "&order=name"
132
+                        
133
+                    ### Formats (programs) ###     
134
+                    elif clist == "formats":
135
+                        #data2 = item["_links"]["videos"]["href"].replace(API_URL,"")
136
+                        data2 = "seasons?format=%s"%item["id"]
137
+                        #if "country" in qs: data2 += "&country="+qs["country"]
138
+                        #if "category" in qs: data2 += "&category="+qs["category"]
139
+                        #if "channel" in qs: data2 += "&channel="+qs["channel"]
140
+                        data2 += "&order=title"
141
+                        air_at = item["latest_video"]["publish_at"] if "publish_at" in item["latest_video"] else ""
142
+                        air_at = air_at[0:16].replace("T"," ") if air_at else ""
143
+                        if air_at:
144
+                            desc = "Last video: %s\n"%air_at + desc                        
145
+                        
146
+                    ### Seasons ###     
147
+                    elif clist == "seasons":
148
+                        #data2 = item["_links"]["videos"]["href"].replace(API_URL,"")
149
+                        data2 = "videos?season=%s"%item["id"]
150
+                        #if "country" in qs: data2 += "&country="+qs["country"]
151
+                        #if "category" in qs: data2 += "&category="+qs["category"]
152
+                        #if "channel" in qs: data2 += "&channel="+qs["channel"]
153
+                        data2 += "&order=title"
154
+                        
155
+                        summary = item["summary"] if "summary" in item and item["summary"] else ""
156
+                        try:
157
+                            latest_video = item["latest_video"]["publish_at"]
158
+                            latest_video = latest_video[0:16].replace("T"," ")
159
+                        except: latest_video = ""
160
+                        desc = ("%s\nLatest video: %s"%(summary,latest_video))
161
+                                      
162
+                    content.append((title.encode("utf8"),self.name+"::"+data2.encode("utf8"),img.encode("utf8"),desc.encode("utf8")))
163
+                    
164
+            if r["_links"].has_key("next"):
165
+                data2 = r["_links"]["next"]["href"].replace(API_URL,"").encode("utf8")
166
+                content.append(("Next page", self.name+"::"+data2.encode("utf8"),"","Goto next page"))
167
+                
168
+        elif citem:
169
+            item = r
170
+            if "title" in item:
171
+                title = item["title"]
172
+            elif "name" in item:
173
+                title = r["name"]
174
+            #data2 = self.name+"::"+"%s/%s"%(clist,item["id"]) 
175
+            img = item["_links"]["image"]["href"].replace("{size}",self.pic_size) if "image" in item["_links"] else ""
176
+            desc = item["summary"] if "summary" in item and item["summary"] else ""
177
+            
178
+            dd = "videos/stream/%s"%cid
179
+            r2 = self.call(dd)
180
+            if "streams" in r2 and "hls" in r2["streams"]:
181
+                data2 = r2["streams"]["hls"]
182
+                content = (title.encode("utf8"),data2.encode("utf8"),img.encode("utf8"),desc.encode("utf8"))
183
+            elif "msg" in r2:
184
+                content = (r2["msg"].encode("utf8"),"","","")
185
+            else: 
186
+                content = ("Error getting stream","","","")            
187
+            
188
+        else:
189
+            pass
190
+        return content
191
+    
192
+    def is_video(self,data):
193
+        if "::" in data:
194
+            data = data.split("::")[1]
195
+        cmd = data.split("/")
196
+        if cmd[0]=="videos":
197
+            return True
198
+        else:
199
+            return False
200
+    
201
+    def get_stream(self,id):   
202
+        dd = "videos/stream/%s"%id
203
+        r2 = self.call(dd)
204
+        if "streams" in r2 and "hls" in r2["streams"]:
205
+            data2 = r2["streams"]["hls"]
206
+        else:
207
+            data2 = ""
208
+        return data2.encode("utf8")
209
+        
210
+    def call_all(self, endpoint, params = None):
211
+        url = API_URL % (endpoint)
212
+        if params:
213
+            url += '?' + params
214
+        print "[TVPlay Api] url: ",url
215
+        result = []
216
+        while True:
217
+            content = self._http_request(url)
218
+            if content:
219
+                try:
220
+                    content = json.loads(content)
221
+                except Exception, ex:
222
+                    return {" Error " : "in call_api: %s" % ex}
223
+            else: break
224
+            if content.has_key("_embedded") and content["_embedded"].has_key(endpoint):
225
+                result.extend(content["_embedded"][endpoint])
226
+                pass
227
+            else: break
228
+            if content.has_key("_links") and content["_links"].has_key("next"):
229
+                url = content["_links"]["next"]["href"]
230
+            else: break
231
+        return result
232
+    
233
+    def call(self, data,headers=headers0):
234
+        url = API_URL + data
235
+        #print "[TVPlay Api] url: ",url
236
+        result = []
237
+        content = self._http_request(url)
238
+        if content:
239
+            try:
240
+                result = json.loads(content)
241
+            except Exception, ex:
242
+                return None
243
+        return result
244
+
245
+    def _http_request0(self, url,headers=headers0):
246
+        try:
247
+            r = urllib2.Request(url, headers=headers)
248
+            u = urllib2.urlopen(r)
249
+            content = u.read()
250
+            u.close()
251
+            return content
252
+        except Exception as ex:
253
+            if "read" in ex:
254
+                content = ex.read()
255
+            else:
256
+                content = None
257
+            return content
258
+
259
+if __name__ == "__main__":
260
+    country= "lv"
261
+    c = Source(country)
262
+    if len(sys.argv)>1:
263
+        data= sys.argv[1]
264
+    else:
265
+        data = "home"
266
+    content = c.get_content(data)
267
+    for item in content:
268
+        print item
269
+    #cat = api.get_categories(country)
270
+    #chan = api.get_channels("lv")
271
+    #prog = api.get_programs(channel=6400)
272
+    #prog = api.get_programs(category=55)
273
+    #seas = api.get_seasons(program=6453)
274
+    #str = api.get_streams(660243)
275
+    #res = api.get_videos(802)
276
+    #formats = api.getAllFormats()
277
+    #det = api.detailed("1516")
278
+    #vid = api.getVideos("13170")
279
+    pass

+ 212
- 0
sources/play24.py View File

@@ -0,0 +1,212 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, re, sys
15
+from SourceBase import SourceBase
16
+
17
+API_URL = 'http://replay.lsm.lv/'
18
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
19
+headers0 = headers2dict("""
20
+User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
21
+""")
22
+import HTMLParser
23
+h = HTMLParser.HTMLParser()
24
+    
25
+class Source(SourceBase):
26
+    
27
+    def __init__(self,country="lv"):
28
+        self.name = "play24"
29
+        self.title = "Play24.lv"
30
+        self.img = "http://play24.lv/images/play24-logo-black.png"
31
+        self.desc = "play24.lv (Riga24TV) satura skatīšanās"
32
+        
33
+        self.country=country
34
+        
35
+    def get_content(self, data):
36
+        print "[play24] get_content:", data
37
+        if "::" in data:
38
+            data = data.split("::")[1] 
39
+        path = data.split("?")[0]
40
+        clist = path.split("/")[0]
41
+        params = data[data.find("?"):] if "?" in data else ""
42
+        qs = dict(map(lambda x:x.split("="),re.findall("\w+=\w+",params)))
43
+        lang = qs["lang"] if "lang" in qs else self.country
44
+    
45
+        content=[]
46
+        content.append(("..return", "back","","Return back"))
47
+        
48
+        if clist=="home":
49
+            content.extend([
50
+                ("Live stream", "play24::tiesraide","","TV live streams"),
51
+                ("Last videos", "play24::jaunakie","","Last videos"),
52
+                ("Categories", "play24::kategorijas","","Categories"),
53
+                ("Programs", "play24::raidijumi","","Programs"),             
54
+             ])
55
+            return content
56
+  
57
+        ### Jaunākie video ###
58
+        elif clist=="jaunakie":
59
+            url = "http://play24.lv/"
60
+            r = self._http_request(url)
61
+            for item in re.findall(' <div class="top-article__image">.*?<a class="top-article__image-link" href="([^"]+)">.*?<img.+?src="([^"]+)".+?alt="([^"]+)" />.+?</picture>', r, re.DOTALL):
62
+                title = item[2]
63
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
64
+                img = item[1]
65
+                data2 = item[0].replace("http://play24.lv/","")
66
+                desc = title
67
+                content.append((title,self.name+"::"+data2,img,desc))
68
+            return content
69
+                
70
+        ### Kategorijas ###
71
+        elif clist=="kategorijas":
72
+            url = "http://play24.lv/"
73
+            r = self._http_request(url)
74
+            r2 = r[r.find('<div class="footer-navigation">'):]
75
+            for item in re.findall('<a href="http://play24.lv/(kategorija/[^"]+)" class="navigation__link">([^<]+)</a>', r2, re.DOTALL):
76
+                title = item[1]
77
+                data2 = item[0]
78
+                img = ""
79
+                desc = title
80
+                content.append((title,self.name+"::"+data2,img,desc))
81
+            return content
82
+       
83
+        elif clist=="kategorija":
84
+            url = "http://play24.lv/"+data
85
+            r = self._http_request(url)
86
+            for article in re.findall(r"<article\b[^>]*>(.+?)</article>", r, re.DOTALL):
87
+                m = re.search('<a class="masonry-item__link" href="http://play24\.lv/([^"]+)">', article, re.DOTALL)
88
+                data2 = m.group(1) if m else ""
89
+                m = re.search('<img src="([^"]+)" alt="([^"]+)" />', article, re.DOTALL)
90
+                if m:
91
+                    img = m.group(1)
92
+                    title = m.group(2)
93
+                    title =  h.unescape(title.decode("utf8")).encode("utf8")
94
+                else:
95
+                    img = ""
96
+                    title = ""
97
+                m = re.search(r'<span class="masonry-item__tags">\s+<a href="([^"]+)">([^<]+)</a>.*?</span>', article, re.DOTALL)
98
+                progr = m.group(2) if m else ""
99
+                m = re.search('<span class="masonry-item__date">([^<]+)</span>', article, re.DOTALL)
100
+                date = m.group(1).strip() if m else ""
101
+                         
102
+                if date:
103
+                    title = title + " (%s %s)"%(date,progr)
104
+                desc = title + "\n%s - %s"%(progr,date)
105
+                content.append((title,self.name+"::"+data2,img,desc))
106
+            m = re.search(r'<li><a href="http://play24\.lv/([^"]+)" rel="next">&raquo;</a></li>', r, re.DOTALL)
107
+            if m:
108
+                data2 = m.group(1)
109
+                content.append(("Next page",self.name+"::"+data2,"","Next page"))                                            
110
+            return content
111
+        
112
+        ### Raidijumi (programmas)
113
+        elif clist=="raidijumi":
114
+            url = "http://play24.lv/"
115
+            r = self._http_request(url)
116
+            for item in re.findall(r'<li class="tag-box__item">.*?<a href="http://play24\.lv/(birka/[^"]+)">([^<]+)</a>.*?</li>', r, re.DOTALL):
117
+                title = item[1]
118
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
119
+                data2 = item[0]
120
+                img = ""
121
+                desc = title
122
+                content.append((title,self.name+"::"+data2,img,desc))
123
+            return content
124
+
125
+        ### Programmas (video saraksts)
126
+        elif clist=="birka":
127
+            url = "http://play24.lv/"+data
128
+            r = self._http_request(url)
129
+            for item in re.findall(r'<article\b[^>]*>.+?<a class="masonry-item__link" href="http://play24.lv/([^"]+)">.*?<img src="([^"]+)" alt="([^"]+)" />.*?<span class="masonry-item__tags">.+?<a href="([^"]+)">([^<]+)</a>.*?<span class="masonry-item__date">([^<]+)</span>.*?</article>', r, re.DOTALL):
130
+                title = item[2]
131
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
132
+                title = title + " (%s)"%item[5].strip()
133
+                img = item[1]
134
+                data2 = item[0]
135
+                desc = title + "\n%s - %s"%(item[4],item[5].strip())
136
+                content.append((title,self.name+"::"+data2,img,desc))
137
+            m = re.search(r'<li><a href="http://play24\.lv/([^"]+)" rel="next">&raquo;</a></li>', r, re.DOTALL)
138
+            if m:
139
+                data2 = m.group(1)
140
+                content.append(("Next page",self.name+"::"+data2,"","Next page"))                                            
141
+            return content
142
+
143
+        elif clist == "video" or clist == "tiesraide":
144
+            if clist == "video":  
145
+                url = "http://play24.lv/"+data
146
+                r = self._http_request(url)
147
+                # var ov_video_id = '59422';
148
+                m = re.search(r"var ov_video_id = '(\d+)';", r, re.DOTALL)
149
+                if m:
150
+                    id = m.group(1)
151
+                else:
152
+                    return ("No stream found %s"%data,"","","No stream found")
153
+                m = re.search('<meta name="description" content="([^"]+)" />', r, re.DOTALL)
154
+                desc = m.group(1) if m else ""
155
+                desc = h.unescape(desc.decode("utf8")).encode("utf8")
156
+                
157
+                url = "http://player.tvnet.lv/v/%s"%id
158
+            else:
159
+                url = "http://player.tvnet.lv/l/11"
160
+                desc = ""
161
+            r = self._http_request(url)
162
+            m = re.search('<h1 class="static title">.+?<a href="[^"]+">([^<]+)</a>', r, re.DOTALL)
163
+            title = m.group(1) if m else ""   
164
+            s = {}
165
+            for item in re.findall('source src="([^"]+)" data-stream="([^"]+)" data-quality="([^"]+)"', r, re.DOTALL):
166
+                s[item[1]] = (item[0],item[2])
167
+            data2 = ""
168
+            for t in ("hls","http","rtmp"):
169
+                if t in s:
170
+                    data2 = s[t][0]
171
+                    break
172
+            return (title,data2,"",desc)
173
+               
174
+    
175
+    def is_video(self,data):
176
+        if "::" in data:
177
+            data = data.split("::")[1]
178
+        cmd = data.split("/")
179
+        if cmd[0] in ("video","tiesraide"):
180
+            return True
181
+        else:
182
+            return False
183
+    
184
+    def call(self, data,headers=headers0,lang=""):
185
+        if not lang: lang = self.country
186
+        url = API_URL%lang + data
187
+        #print "[TVPlay Api] url: ",url
188
+        result = []
189
+        content = self._http_request(url)
190
+        return content
191
+
192
+if __name__ == "__main__":
193
+    country= "lv"
194
+    c = Source(country)
195
+    if len(sys.argv)>1:
196
+        data= sys.argv[1]
197
+    else:
198
+        data = "home"
199
+    content = c.get_content(data)
200
+    for item in content:
201
+        print item
202
+    #cat = api.get_categories(country)
203
+    #chan = api.get_channels("lv")
204
+    #prog = api.get_programs(channel=6400)
205
+    #prog = api.get_programs(category=55)
206
+    #seas = api.get_seasons(program=6453)
207
+    #str = api.get_streams(660243)
208
+    #res = api.get_videos(802)
209
+    #formats = api.getAllFormats()
210
+    #det = api.detailed("1516")
211
+    #vid = api.getVideos("13170")
212
+    pass

+ 304
- 0
sources/replay.py View File

@@ -0,0 +1,304 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, re, sys
15
+from SourceBase import SourceBase
16
+
17
+API_URL = 'http://replay.lsm.lv/%s/'
18
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
19
+headers0 = headers2dict("""
20
+User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
21
+""")
22
+import HTMLParser
23
+h = HTMLParser.HTMLParser()
24
+from YouTubeVideoUrl import YouTubeVideoUrl
25
+    
26
+class Source(SourceBase):
27
+    
28
+    def __init__(self,country="lv"):
29
+        self.name = "replay"
30
+        self.title = "Replay.lv (LTV)"
31
+        self.img = "http://replay.lsm.lv/apple-touch-icon.png"
32
+        self.desc = "LSM replay.lv satura skatīšanās"
33
+        
34
+        self.country=country
35
+        self.pic_size = "327x250" #"1000x765"
36
+        
37
+    def get_content(self, data):
38
+        print "[replay] get_content:", data
39
+        if "::" in data:
40
+            data = data.split("::")[1] 
41
+        path = data.split("?")[0]
42
+        clist = path.split("/")[0]
43
+        params = data[data.find("?"):] if "?" in data else ""
44
+        qs = dict(map(lambda x:x.split("="),re.findall("\w+=\w+",params)))
45
+        lang = qs["lang"] if "lang" in qs else self.country
46
+    
47
+        content=[]
48
+        content.append(("..return", "back","","Return back"))
49
+        
50
+        if clist=="home":
51
+            content.extend([
52
+                ("Live streams", "replay::tiesraide","","TV live streams"),
53
+                ("Search LV", "replay::search/?term={0}&lang=lv","","Search content LV"),                
54
+                ("Last videos LV", "replay::visi/jaunakie/?source=ltv&lang=lv","","Last aired videos LV"),
55
+                ("Last videos by categories LV", "replay::kategorijas/?lang=lv","","Last videos by categories LV"),                
56
+                ("All programs LV", "replay::raidijumi/?type=video","","All programs by name LV"),
57
+                ("Programs by categories LV", "replay::categories?lang=lv","","All programs by categories LV"),
58
+                #("Channels", "replay::channels?language=%s"%self.country,"","TV channels"),
59
+                ("Videos by popularity LV", "replay::visi/popularie/?source=ltv&lang=lv","","Programs by popularity"), 
60
+                
61
+                ("Search RU", "replay::search/?term={0}&lang=ru","","Search content RU"),                
62
+                ("Last videos RU", "replay::vse/novie/?source=ltv&lang=ru","","Last aired videos RU"),
63
+                ("Last videos by categories RU", "replay::kategorijas/?lang=ru","","Last videos by categories RU"),                                
64
+                ("All programs RU", "replay::peredachi/?lang=ru&type=video","","All programs by name"),         
65
+                ("Programs by categories RU", "replay::categories?lang=ru","","Programs by categories RU")
66
+            ])
67
+            return content
68
+        
69
+        ### programmu kategorijas ###
70
+        elif clist=="categories":
71
+            url = "http://replay.lsm.lv/lv/raidijumi/?lang=lv&type=video" if lang =="lv" else "http://replay.lsm.lv/ru/peredachi/?lang=ru&type=video"        
72
+            r = self._http_request(url)
73
+            for item in re.findall(r'<a .+href="(\?lang=\w+&type=video&theme=\d+)">([^<]+)</a>\t', r):
74
+                title = item[1]
75
+                data2 = url.split("?")[0]+item[0]
76
+                data2 = data2.replace(API_URL%lang,"")
77
+                img = ""
78
+                desc = title
79
+                content.append((title,self.name+"::"+data2,img,desc))
80
+            return content
81
+        
82
+        ### jaunāko raidijumu kategorijas ###
83
+        elif clist=="kategorijas":
84
+            url = "http://replay.lsm.lv/lv/" if lang =="lv" else "http://replay.lsm.lv/ru/"        
85
+            r = self._http_request(url)
86
+            for item in re.findall(r'<a href="/(lv|ru)/kategorija/(\w+)/">.+?<i class="[^"]+"></i>.+?<span>([^<]+)</span>', r, re.DOTALL):
87
+                title = item[2]
88
+                data2 = "kategorija/%s/?lang=%s"%(item[1],item[0])
89
+                img = ""
90
+                desc = title
91
+                content.append((title,self.name+"::"+data2,img,desc))
92
+            return content
93
+        
94
+        ### Tiešraides kanānālu saraksts
95
+        elif path=="tiesraide":
96
+            url = "http://replay.lsm.lv/styles/main.css"
97
+            r= self._http_request(url)
98
+            for item in re.findall(r'channel-logo--(\w+)\{background-image:url\("([^"]+\.png)"', r):
99
+                ch = item[0]
100
+                title = ch.upper()
101
+                data2 = "tiesraide/%s/"%ch
102
+                img = "http://replay.lsm.lv"+item[1]
103
+                veids = "video "if "tv" in ch else "audio"
104
+                desc = title+" tiesraide (%s)"%veids
105
+                content.append((title,self.name+"::"+data2,img,desc))
106
+            return content
107
+        
108
+        ### Kanāla tiesraide
109
+        elif clist == "tiesraide" and "/" in data:
110
+            ch = data.split('/')[1]
111
+            veids = "video" if "tv" in ch else "audio"           
112
+            #url = "http://replay.lsm.lv/lv/tiesraide/ltv7/"
113
+            url = "http://replay.lsm.lv/lv/tiesraide/%s/"%ch
114
+            r= self._http_request(url)
115
+            
116
+            m = re.search('%s/">.+?<h5>([^<]+)+</h5>.*?<time>([^<]+)</time>'%ch, r, re.DOTALL)
117
+            tagad = m.group(1).strip() if m else ""
118
+            laiks = m.group(2).strip() if m else ""
119
+            laiks = h.unescape(laiks).encode("utf8")
120
+            m = re.search("<h1>([^<]+)</h1>", r)
121
+            title = m.group(1).strip() if m else path.split("/")[1].upper()
122
+            title = "%s - %s (%s)"%(title,tagad,laiks)
123
+            
124
+            if veids == "video":
125
+                m = re.search('<div class="video"><iframe.+src="([^"]+)"', r)
126
+                if not m: 
127
+                    content=("No stream found %s"%data,"","","No stream found")
128
+                    return content           
129
+                url = m.group(1)
130
+                headers = headers2dict("""
131
+            User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
132
+            Referer: http://replay.lsm.lv/lv/ieraksts/ltv/70398/tiesa-runa.-lielbritanija-gatavojas-referendumam-par-tu/
133
+                    """)
134
+                r = self._http_request(url,headers=headers)
135
+            
136
+                m = re.search('<div class="video-player"><iframe.+src="([^"]+)"', r)
137
+                if not m:
138
+                    content=("No stream found %s"%data,"","","No stream found")
139
+                    return content
140
+                url = m.group(1)
141
+                            
142
+                r = self._http_request(url,headers=headers)
143
+                m = re.search('"([^"]+m3u8[^"]+)"', r)
144
+                if not m:
145
+                    content=("No stream found %s"%data,"","","No stream found")
146
+                    return content           
147
+                data2 = m.group(1).replace("\\","")
148
+                
149
+            else: # audio
150
+                lrn = ch.replace("lr","")
151
+                url = "http://www.latvijasradio.lsm.lv/lv/tiesraide/?channel=%s"%lrn
152
+                r = self._http_request(url)
153
+                m = re.search('"file":"([^"]+?m3u8.*?)"', r)
154
+                if not m:
155
+                    content=("No stream found %s"%data,"","","No stream found")
156
+                    return content           
157
+                data2 = m.group(1).replace("\\","")
158
+                
159
+            img = ""
160
+            desc = ""
161
+            content =(title,data2,img,desc)
162
+            return content           
163
+                       
164
+        #m = re.search(r'(\?page=\d+)" class=" paging__prev', r, re.IGNORECASE)
165
+        #if m:
166
+        #    data = re.sub("\?page=\d+", "", data)
167
+        #    data2 = data+m.group(1)
168
+        #    content.append(("Previous page",self.name+"::"+data2,"","Previous page"))                            
169
+        
170
+        r = self.call(data, lang=lang)
171
+        if not r:
172
+            return content
173
+        
174
+        if clist == "search":
175
+            #for r2 in re.findall('<article itemtype="http://schema.org/Article" itemscope class="thumbnail thumbnail--default ">(.+?)</article>', r2, re.DOTALL):
176
+            for item in re.findall('itemprop="image" data-image="([^"]+)".+?<figcaption><h5 itemprop="name"><a itemprop="url" href="([^<]+)">([^<]+)</a></h5></figcaption>', r):
177
+                title = item[2]
178
+                data2 = item[1].replace("/%s/"%lang,"")+"?lang=%s"%lang
179
+                img = "http://replay.lsm.lv" + item[0]
180
+                desc  = title
181
+                content.append((title,self.name+"::"+data2,img,desc))
182
+                
183
+            #for item in re.findall('itemprop="image" data-image="([^"]+)".+?<figcaption><h4 itemprop="about"><a href="([^"]+)">([^<]+)</a></h4>.*?<h5 itemprop="name"><a itemprop="url" href="([^"]+)">([^<]+)</a></h5>.+?datetime="([^"]+)" class="thumbnail__date ">([^<]+)</time>', r2):
184
+            for item in re.findall('itemprop="image" data-image="([^"]+)".+? class="icon-(ltv|lr).+?<figcaption><h4 itemprop="about"><a href="([^"]+)">([^<]+)</a></h4>.*?<h5 itemprop="name"><a itemprop="url" href="([^"]+)">([^<]+)</a></h5>.+?datetime="([^"]+)" class="thumbnail__date ">([^<]+)</time>', r):  
185
+                if item[1]=="lr":continue
186
+                title = "%s - %s (%s)"%(item[3],item[5],item[7])
187
+                data2 = item[4].replace("/%s/"%lang,"")+"?lang=%s"%lang
188
+                img = item[0].replace("https:","http:")
189
+                desc = title
190
+                content.append((title,self.name+"::"+data2,img,desc))
191
+    
192
+        ### Raidijumi (programmas) ###
193
+        elif clist in ( "raidijumi","peredachi"):
194
+            for item in re.findall('<li itemprop="name"><a href="([^"]+)" itemprop="url">([^<]+)', r):
195
+            #for item in re.findall('<li itemprop="name"><a href="([^"]+)" itemprop="url">([^<]+)</a></li>', r):
196
+                title = item[1]
197
+                data2 = item[0].replace("/%s/"%lang,"")+"?lang=%s"%lang
198
+                img = ""
199
+                desc  = ""
200
+                content.append((title,self.name+"::"+data2,img,desc))
201
+                 
202
+        ### Raidijuma ieraksti speciālie###
203
+        elif clist in ( "visi","vse",):
204
+            for item in re.findall('(?i)<figure><a href="([^"]+)" itemprop="image" data-image="([^"]+)".+class="thumbnail__duration">([^<]+)</time></figure><figcaption><h4 itemprop="about"><a href="[^"]+">([^<]+)</a></h4>.+>([^<]+).*</h5>.+>([^<]+)</time></figcaption>', r):
205
+                title = item[3]
206
+                data2 = item[0].replace("/%s/"%lang,"")+"?lang=%s"%lang
207
+                img = item[1].replace("https:","http:")
208
+                desc  = "%s - %s\n%s"%(item[5],item[2],item[4])
209
+                content.append((title,self.name+"::"+data2,img,desc))
210
+                
211
+        ### Raidijuma ieraksti (videos)
212
+        elif clist in ("raidijums","peredacha","kategorija"): 
213
+            for item in re.findall('<article .+ href="([^"]+)".+image="([^"]+)".+class="thumbnail__duration">([^<]+).+">([^<]+).+class="thumbnail__date ">([^"]+)</time></figcaption></article>', r):
214
+                title = item[3]
215
+                data2 = item[0].replace("/%s/"%lang,"")+"?lang=%s"%lang
216
+                img = item[1].replace("https:","http:")
217
+                desc = "%s - %s"%(item[4],item[2])
218
+                content.append((title,self.name+"::"+data2,img,desc))
219
+        
220
+        ### Ieraksts (video) ###
221
+        elif clist in ("ieraksts","statja"):
222
+            m = re.search('src="([^"]+)"></iframe>', r)
223
+            if m:
224
+                url2 = m.group(1)
225
+                headers = headers2dict("""
226
+User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
227
+Referer: http://replay.lsm.lv/lv/ieraksts/ltv/70398/tiesa-runa.-lielbritanija-gatavojas-referendumam-par-tu/
228
+            """)
229
+                r2 = self._http_request(url2,headers=headers)
230
+                m = re.search('"file":"([^"]+)', r2)
231
+                if m:
232
+                    data2 = m.group(1).replace("\\","")
233
+                    m = re.search('"idstring":"([^"]+)', r2)                    
234
+                    title = m.group(1) if m else ""
235
+                    title = title.decode("unicode-escape").encode("utf8")
236
+                    title = title.replace("\n","")
237
+                    img = ""
238
+                    desc = ""
239
+                    if "youtube" in data2:
240
+                        video_id = re.search(r"/watch\?v=([^&]+)",data2).group(1)
241
+                        data2 = YouTubeVideoUrl().extract(video_id)
242
+                        if not data2:
243
+                            content=("No stream found %s"%data,"","","No stream found")
244
+                            return content                           
245
+                    content =(title,data2,img,desc)
246
+                    return content
247
+            content=("No stream found %s"%data,"","","No stream found")
248
+            return content
249
+                
250
+        m = re.search(r'href="\?([^"]+)" class=" paging__next', r)
251
+        if m:
252
+            page = int(re.search("page=(\d+)",m.group(1)).group(1))
253
+            if "page="in data:
254
+                data2 = re.sub("page=\d+","page=%i"%page,data)
255
+            else:
256
+                if "?" in data:
257
+                    data2 =data+"&page=%i"%page
258
+                else:
259
+                    data2 =data+"?page=%i"%page
260
+            content.append(("Next page",self.name+"::"+data2,"","Next page"))                            
261
+                
262
+        return content
263
+    
264
+    def is_video(self,data):
265
+        if "::" in data:
266
+            data = data.split("::")[1]
267
+        cmd = data.split("/")
268
+        if cmd[0] in ("ieraksts","statja"):
269
+            return True
270
+        elif cmd[0]=="tiesraide" and len(cmd)>1:
271
+            return True
272
+        else:
273
+            return False
274
+    
275
+    def call(self, data,headers=headers0,lang=""):
276
+        if not lang: lang = self.country
277
+        url = API_URL%lang + data
278
+        #print "[TVPlay Api] url: ",url
279
+        result = []
280
+        content = self._http_request(url,headers=headers0)
281
+        return content
282
+
283
+
284
+if __name__ == "__main__":
285
+    country= "lv"
286
+    c = Source(country)
287
+    if len(sys.argv)>1:
288
+        data= sys.argv[1]
289
+    else:
290
+        data = "home"
291
+    content = c.get_content(data)
292
+    for item in content:
293
+        print item
294
+    #cat = api.get_categories(country)
295
+    #chan = api.get_channels("lv")
296
+    #prog = api.get_programs(channel=6400)
297
+    #prog = api.get_programs(category=55)
298
+    #seas = api.get_seasons(program=6453)
299
+    #str = api.get_streams(660243)
300
+    #res = api.get_videos(802)
301
+    #formats = api.getAllFormats()
302
+    #det = api.detailed("1516")
303
+    #vid = api.getVideos("13170")
304
+    pass

+ 230
- 0
sources/serialguru.py View File

@@ -0,0 +1,230 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, re, sys,os
15
+import ConfigParser
16
+from SourceBase import SourceBase
17
+
18
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
19
+import HTMLParser
20
+h = HTMLParser.HTMLParser()
21
+    
22
+class Source(SourceBase):
23
+    
24
+    def __init__(self,country=""):
25
+        self.name = "serialguru"
26
+        self.title = "SerialGURU.ru"
27
+        self.img = "http://serialguru.ru/images/xlogo_new.png.pagespeed.ic.0sre2_2OJN.png"
28
+        self.desc = "Serialguru.ru portāla satura skatīšanās"
29
+        self.country=country
30
+        self.headers = headers2dict("""
31
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36
32
+Referer: http://serialguru.ru/        
33
+""")
34
+        self.headers2 = headers2dict("""
35
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36
36
+X-Requested-With: XMLHttpRequest
37
+Content-Type: application/x-www-form-urlencoded; charset=UTF-8
38
+Referer: http://serialguru.ru/        
39
+""")
40
+        self.url = "http://serialguru.ru/"
41
+        #self.login()
42
+        
43
+    def login(self,user="",password=""):
44
+        return True
45
+            
46
+    def get_content(self, data):
47
+        print "[tvdom] get_content:", data
48
+        if "::" in data:
49
+            data = data.split("::")[1] 
50
+        path = data.split("?")[0]
51
+        clist = path.split("/")[0]
52
+        params = data[data.find("?"):] if "?" in data else ""
53
+        qs = dict(map(lambda x:x.split("="),re.findall("[%\w]+=\w+",params)))
54
+        lang = qs["lang"] if "lang" in qs else self.country
55
+    
56
+        content=[]
57
+        content.append(("..return", "back","","Return back"))
58
+        
59
+        if clist=="home":
60
+            content.extend([
61
+                ("Search", "serialguru::search/{0}","","Search content"),                
62
+                ("Last", "serialguru::last","","Last series"),                
63
+                ("Series", "serialguru::serials","","TV Series"),
64
+                ("Shows", "serialguru::tv","","TV Shows"),
65
+                ("Animations", "serialguru::mult","","Animation series"),
66
+                
67
+                #("Archive - all", "tvdom::arhivs_all","","Video archive all"),
68
+            ])
69
+            return content
70
+    
71
+        elif data=="last":
72
+            r = self.call("")
73
+            for item in re.findall(r'<li><a href="(http://serialguru\.ru/[^"]+)"><i>([^<]+)</i>  <i>([^<]+)</i> <b>([^<]+)</b></a></li>', r, re.DOTALL):
74
+                title = item[1] + " - " + item[2]+"/"+item[3]
75
+                img = ""
76
+                data2 = item[0].replace(self.url,"")
77
+                desc = title
78
+                content.append((title,self.name+"::"+data2,img,desc))
79
+            return content
80
+
81
+        elif data=="serials":
82
+            content.extend([
83
+                ("All", "serialguru::serials?o=0&t=S","","All series"),                
84
+                ("Russian", "serialguru::serials?c%5B%5D=53&c%5B%5D=61&c%5B%5D=33&c%5B%5D=42&c%5B%5D=31&o=0&t=S","","Russian countries series"),                
85
+                ("English", "serialguru::serials?c%5B%5D=27&c%5B%5D=26&c%5B%5D=43&c%5B%5D=30&c%5B%5D=34&c%5B%5D=25&o=0&t=S","","English countries series"),
86
+                ("Europe", "serialguru::serials?c%5B%5D=29&c%5B%5D=66&c%5B%5D=44&c%5B%5D=28&c%5B%5D=51&c%5B%5D=65&c%5B%5D=62&c%5B%5D=40&c%5B%5D=45&c%5B%5D=68&c%5B%5D=59&c%5B%5D=39&c%5B%5D=35&c%5B%5D=47&o=0&t=S","","European countries series"),
87
+                ("Other", "serialguru::serials?c%5B%5D=36&c%5B%5D=32&c%5B%5D=67&c%5B%5D=63&c%5B%5D=60&c%5B%5D=64&c%5B%5D=38&c%5B%5D=52&c%5B%5D=41&c%5B%5D=58&c%5B%5D=57&c%5B%5D=37&c%5B%5D=50&c%5B%5D=46&o=0&t=S","","Other countries series"),
88
+                #("Archive - all", "tvdom::arhivs_all","","Video archive all"),
89
+            ])
90
+            return content
91
+        
92
+        elif data=="tv":
93
+            content.extend([
94
+                ("All", "serialguru::tv?o=0&t=S","","All series"),                
95
+                ("Russian", "serialguru::tv?c%5B%5D=53&c%5B%5D=61&c%5B%5D=33&c%5B%5D=42&c%5B%5D=31&o=0&t=P","","Russian countries TV shows"),                
96
+                ("English", "serialguru::tv?c%5B%5D=27&c%5B%5D=26&c%5B%5D=43&c%5B%5D=30&c%5B%5D=34&c%5B%5D=25&o=0&t=P","","English countries TV shows"),
97
+                ("Europe", "serialguru::tv?c%5B%5D=29&c%5B%5D=66&c%5B%5D=44&c%5B%5D=28&c%5B%5D=51&c%5B%5D=65&c%5B%5D=62&c%5B%5D=40&c%5B%5D=45&c%5B%5D=68&c%5B%5D=59&c%5B%5D=39&c%5B%5D=35&c%5B%5D=47&o=0&t=P","","European countries TV shows series"),
98
+                ("Other", "serialguru::tv?c%5B%5D=36&c%5B%5D=32&c%5B%5D=67&c%5B%5D=63&c%5B%5D=60&c%5B%5D=64&c%5B%5D=38&c%5B%5D=52&c%5B%5D=41&c%5B%5D=58&c%5B%5D=57&c%5B%5D=37&c%5B%5D=50&c%5B%5D=46&o=0&t=P","","Other countries TV shows"),
99
+                #("Archive - all", "tvdom::arhivs_all","","Video archive all"),
100
+            ])
101
+            return content
102
+        
103
+        elif data=="mult":
104
+            content.extend([
105
+                ("All", "serialguru::mult?o=0&t=S","","All series"),                
106
+                ("Russian", "serialguru::mult?c%5B%5D=53&c%5B%5D=61&c%5B%5D=33&c%5B%5D=42&c%5B%5D=31&o=0&t=M","","Russian countries animantions"),                
107
+                ("English", "serialguru::mult?c%5B%5D=27&c%5B%5D=26&c%5B%5D=43&c%5B%5D=30&c%5B%5D=34&c%5B%5D=25&o=0&t=M","","English countries animantions"),
108
+                ("Europe", "serialguru::mult?c%5B%5D=29&c%5B%5D=66&c%5B%5D=44&c%5B%5D=28&c%5B%5D=51&c%5B%5D=65&c%5B%5D=62&c%5B%5D=40&c%5B%5D=45&c%5B%5D=68&c%5B%5D=59&c%5B%5D=39&c%5B%5D=35&c%5B%5D=47&o=0&t=M","","European countries animantions"),
109
+                ("Other", "serialguru::mult?c%5B%5D=36&c%5B%5D=32&c%5B%5D=67&c%5B%5D=63&c%5B%5D=60&c%5B%5D=64&c%5B%5D=38&c%5B%5D=52&c%5B%5D=41&c%5B%5D=58&c%5B%5D=57&c%5B%5D=37&c%5B%5D=50&c%5B%5D=46&o=0&t=M","","Other countries animantions"),
110
+                #("Archive - all", "tvdom::arhivs_all","","Video archive all"),
111
+            ])
112
+            return content
113
+        
114
+        elif clist=="search":
115
+            if data.split("/")>1:
116
+                term = data.split("/")[1]
117
+            else:
118
+                return content
119
+            r = self.call("main/autocomplete?term=%s"%(term))
120
+            if r=="null":
121
+                return content
122
+            js = json.loads(r)
123
+            for item in js:
124
+                title = item["name"].encode("utf8")
125
+                data2 = item["url"].encode("utf8")
126
+                img = "http://serialguru.ru/uploads/cover/"+item["image_s"].replace("_s","")+".jpg"
127
+                rating = item["rating"].encode("utf8") if item["rating"] else ""
128
+                desc = title +"\nRating:%s (%s+/%s-)"%(rating,item["plus_cnt"].encode("utf8"),item["minus_cnt"].encode("utf8"))
129
+                content.append((title,self.name+"::"+data2,img,desc))
130
+            return content
131
+                        
132
+        elif path=="serials" or path=="tv" or path=="mult":
133
+            if path=="serials" and not "cat%5B%5D" in data:
134
+                #content.append(("All", "serialguru::"+data+"&cat%5B%5D=","","All series"))
135
+                categories = self.get_categories(path)
136
+                for c in categories:
137
+                    content.append((c[1], "serialguru::"+data+"&cat%5B%5D="+c[0],"",c[1]))
138
+                return content
139
+            else:
140
+                r = self.call("main/load", params[1:], headers=self.headers2)
141
+                for item in re.findall('<li><a href="([^"]+)"><div>.*?<img src="([^"]+)" alt="([^"]+)"[^<]*?><p>([^<]+)<i><span class="r">([^<]+)</span> <span class="plus">([^<]+)</span> <span class="minus">([^<]+)</span></i></p></div>([^<]+)</a></li>', r, re.DOTALL):
142
+                    title = "%s (%s)"%(item[2],item[3])
143
+                    img = item[1].replace("_s.jpg","_l.jpg")
144
+                    data2 = item[0].replace(self.url,"")
145
+                    desc = title +"\nRating:%s (%s+/%s-)"%(item[4],item[5],item[6])
146
+                    content.append((title,self.name+"::"+data2,img,desc))
147
+                page=int(re.search("o=(\d+)",data).group(1))
148
+                data2 = re.sub("o=(\d+)","o=%s"%(page+15),data)
149
+                content.append(("Next page",self.name+"::"+data2,"","Go to next page"))                 
150
+                return content
151
+        
152
+        
153
+        ### Pārraide  
154
+        else:
155
+            r = self.call(clist)
156
+            title0=re.search('<h2>(.+?)</h2>',r,re.DOTALL).group(1)
157
+            m=re.search('<div class="description">(.+?)</div>',r,re.DOTALL)
158
+            desc0=m.group(1) if m else ""
159
+            desc0=desc0.replace("<p>","").replace("</p>","\n").replace('<a href="#">ПОКАЗАТЬ ПОЛНОСТЬЮ</a>',"")
160
+            desc0=title0+"\n"+desc0.strip()
161
+            img0=""
162
+            m = re.search("http://serialguru.ru/main/playlist/\d+",r)
163
+            if m:
164
+                url = m.group()
165
+            else:
166
+                raise Exception ("No stream found")
167
+            r = self._http_request(url)
168
+            js = json.loads(r,"utf8")
169
+            if not "/" in data: # sezonas
170
+                for i,item in enumerate(js["playlist"]):
171
+                    title = title0 + " - " + item["comment"].encode("utf8")
172
+                    img = img0
173
+                    data2 = "%s/%s"%(data,i)
174
+                    desc = desc0
175
+                    content.append((title,self.name+"::"+data2,img,desc))
176
+            else:
177
+                snum = int(data.split("/")[1])
178
+                title1 = js["playlist"][snum]["comment"].encode('utf8')
179
+                for i,item in enumerate(js["playlist"][snum]["playlist"]):
180
+                    title = title0 + " - " + title1+"/"+item["comment"].encode("utf8")
181
+                    img = img0
182
+                    data2 = item["file"].encode("utf8")
183
+                    desc = desc0
184
+                    content.append((title,data2,img,desc))               
185
+            return content
186
+ 
187
+            
188
+    def is_video(self,data):
189
+        if "::" in data:
190
+            data = data.split("::")[1]
191
+        if "live/view" in data:
192
+            return True
193
+        else:
194
+            return False
195
+    
196
+    def get_categories(self,data):
197
+        r = self.call(data)
198
+        r2 = re.search('<td class="category">(.+?)</td>', r, re.DOTALL).group(1)
199
+        items = re.findall(r'<a href="#" data-id="(\d+)">([^<]+)</a>', r2, re.DOTALL)
200
+        return items
201
+
202
+        
203
+    def call(self, data,params = None, headers=None):
204
+        if not headers: headers = self.headers
205
+        #if not lang: lang = self.country
206
+        url = self.url + data
207
+        content = self._http_request(url,params, headers)
208
+        return content
209
+
210
+if __name__ == "__main__":
211
+    country= "lv"
212
+    c = Source(country)
213
+    if len(sys.argv)>1:
214
+        data= sys.argv[1]
215
+    else:
216
+        data = "home"
217
+    content = c.get_content(data)
218
+    for item in content:
219
+        print item
220
+    #cat = api.get_categories(country)
221
+    #chan = api.get_channels("lv")
222
+    #prog = api.get_programs(channel=6400)
223
+    #prog = api.get_programs(category=55)
224
+    #seas = api.get_seasons(program=6453)
225
+    #str = api.get_streams(660243)
226
+    #res = api.get_videos(802)
227
+    #formats = api.getAllFormats()
228
+    #det = api.detailed("1516")
229
+    #vid = api.getVideos("13170")
230
+    pass

+ 828
- 0
sources/swfinterp.py View File

@@ -0,0 +1,828 @@
1
+# This code comes from youtube-dl: https://github.com/rg3/youtube-dl/blob/master/youtube_dl/swfinterp.py
2
+
3
+from __future__ import unicode_literals
4
+
5
+import collections
6
+import io
7
+import struct
8
+import zlib
9
+
10
+
11
+def _extract_tags(file_contents):
12
+    if file_contents[1:3] != b'WS':
13
+        print '[SWFInterpreter] Not an SWF file; header is %r' % file_contents[:3]
14
+    if file_contents[:1] == b'C':
15
+        content = zlib.decompress(file_contents[8:])
16
+    else:
17
+        raise NotImplementedError(
18
+            'Unsupported compression format %r' %
19
+            file_contents[:1])
20
+
21
+    # Determine number of bits in framesize rectangle
22
+    framesize_nbits = struct.unpack('!B', content[:1])[0] >> 3
23
+    framesize_len = (5 + 4 * framesize_nbits + 7) // 8
24
+
25
+    pos = framesize_len + 2 + 2
26
+    while pos < len(content):
27
+        header16 = struct.unpack('<H', content[pos:pos + 2])[0]
28
+        pos += 2
29
+        tag_code = header16 >> 6
30
+        tag_len = header16 & 0x3f
31
+        if tag_len == 0x3f:
32
+            tag_len = struct.unpack('<I', content[pos:pos + 4])[0]
33
+            pos += 4
34
+        assert pos + tag_len <= len(content), \
35
+            ('Tag %d ends at %d+%d - that\'s longer than the file (%d)'
36
+                % (tag_code, pos, tag_len, len(content)))
37
+        yield (tag_code, content[pos:pos + tag_len])
38
+        pos += tag_len
39
+
40
+
41
+class _AVMClass_Object(object):
42
+    def __init__(self, avm_class):
43
+        self.avm_class = avm_class
44
+
45
+    def __repr__(self):
46
+        return '%s#%x' % (self.avm_class.name, id(self))
47
+
48
+
49
+class _ScopeDict(dict):
50
+    def __init__(self, avm_class):
51
+        super(_ScopeDict, self).__init__()
52
+        self.avm_class = avm_class
53
+
54
+    def __repr__(self):
55
+        return '%s__Scope(%s)' % (
56
+            self.avm_class.name,
57
+            super(_ScopeDict, self).__repr__())
58
+
59
+
60
+class _AVMClass(object):
61
+    def __init__(self, name_idx, name, static_properties=None):
62
+        self.name_idx = name_idx
63
+        self.name = name
64
+        self.method_names = {}
65
+        self.method_idxs = {}
66
+        self.methods = {}
67
+        self.method_pyfunctions = {}
68
+        self.static_properties = static_properties if static_properties else {}
69
+
70
+        self.variables = _ScopeDict(self)
71
+        self.constants = {}
72
+
73
+    def make_object(self):
74
+        return _AVMClass_Object(self)
75
+
76
+    def __repr__(self):
77
+        return '_AVMClass(%s)' % (self.name)
78
+
79
+    def register_methods(self, methods):
80
+        self.method_names.update(methods.items())
81
+        self.method_idxs.update(dict(
82
+            (idx, name)
83
+            for name, idx in methods.items()))
84
+
85
+
86
+class _Multiname(object):
87
+    def __init__(self, kind):
88
+        self.kind = kind
89
+
90
+    def __repr__(self):
91
+        return '[MULTINAME kind: 0x%x]' % self.kind
92
+
93
+
94
+def _read_int(reader):
95
+    res = 0
96
+    shift = 0
97
+    for _ in range(5):
98
+        buf = reader.read(1)
99
+        assert len(buf) == 1
100
+        b = struct.unpack('<B', buf)[0]
101
+        res = res | ((b & 0x7f) << shift)
102
+        if b & 0x80 == 0:
103
+            break
104
+        shift += 7
105
+    return res
106
+
107
+
108
+def _u30(reader):
109
+    res = _read_int(reader)
110
+    assert res & 0xf0000000 == 0
111
+    return res
112
+_u32 = _read_int
113
+
114
+
115
+def _s32(reader):
116
+    v = _read_int(reader)
117
+    if v & 0x80000000 != 0:
118
+        v = - ((v ^ 0xffffffff) + 1)
119
+    return v
120
+
121
+
122
+def _s24(reader):
123
+    bs = reader.read(3)
124
+    assert len(bs) == 3
125
+    last_byte = b'\xff' if (ord(bs[2:3]) >= 0x80) else b'\x00'
126
+    return struct.unpack('<i', bs + last_byte)[0]
127
+
128
+
129
+def _read_string(reader):
130
+    slen = _u30(reader)
131
+    resb = reader.read(slen)
132
+    assert len(resb) == slen
133
+    return resb.decode('utf-8')
134
+
135
+
136
+def _read_bytes(count, reader):
137
+    assert count >= 0
138
+    resb = reader.read(count)
139
+    assert len(resb) == count
140
+    return resb
141
+
142
+
143
+def _read_byte(reader):
144
+    resb = _read_bytes(1, reader=reader)
145
+    res = struct.unpack('<B', resb)[0]
146
+    return res
147
+
148
+
149
+StringClass = _AVMClass('(no name idx)', 'String')
150
+ByteArrayClass = _AVMClass('(no name idx)', 'ByteArray')
151
+TimerClass = _AVMClass('(no name idx)', 'Timer')
152
+TimerEventClass = _AVMClass('(no name idx)', 'TimerEvent', {'TIMER': 'timer'})
153
+_builtin_classes = {
154
+    StringClass.name: StringClass,
155
+    ByteArrayClass.name: ByteArrayClass,
156
+    TimerClass.name: TimerClass,
157
+    TimerEventClass.name: TimerEventClass,
158
+}
159
+
160
+
161
+class _Undefined(object):
162
+    def __bool__(self):
163
+        return False
164
+    __nonzero__ = __bool__
165
+
166
+    def __hash__(self):
167
+        return 0
168
+
169
+    def __str__(self):
170
+        return 'undefined'
171
+    __repr__ = __str__
172
+
173
+undefined = _Undefined()
174
+
175
+
176
+class SWFInterpreter(object):
177
+    def __init__(self, file_contents):
178
+        self._patched_functions = {
179
+            (TimerClass, 'addEventListener'): lambda params: undefined,
180
+        }
181
+        code_tag = next(tag
182
+                        for tag_code, tag in _extract_tags(file_contents)
183
+                        if tag_code == 82)
184
+        p = code_tag.index(b'\0', 4) + 1
185
+        code_reader = io.BytesIO(code_tag[p:])
186
+
187
+        # Parse ABC (AVM2 ByteCode)
188
+
189
+        # Define a couple convenience methods
190
+        u30 = lambda *args: _u30(*args, reader=code_reader)
191
+        s32 = lambda *args: _s32(*args, reader=code_reader)
192
+        u32 = lambda *args: _u32(*args, reader=code_reader)
193
+        read_bytes = lambda *args: _read_bytes(*args, reader=code_reader)
194
+        read_byte = lambda *args: _read_byte(*args, reader=code_reader)
195
+
196
+        # minor_version + major_version
197
+        read_bytes(2 + 2)
198
+
199
+        # Constant pool
200
+        int_count = u30()
201
+        self.constant_ints = [0]
202
+        for _c in range(1, int_count):
203
+            self.constant_ints.append(s32())
204
+        self.constant_uints = [0]
205
+        uint_count = u30()
206
+        for _c in range(1, uint_count):
207
+            self.constant_uints.append(u32())
208
+        double_count = u30()
209
+        read_bytes(max(0, (double_count - 1)) * 8)
210
+        string_count = u30()
211
+        self.constant_strings = ['']
212
+        for _c in range(1, string_count):
213
+            s = _read_string(code_reader)
214
+            self.constant_strings.append(s)
215
+        namespace_count = u30()
216
+        for _c in range(1, namespace_count):
217
+            read_bytes(1)  # kind
218
+            u30()  # name
219
+        ns_set_count = u30()
220
+        for _c in range(1, ns_set_count):
221
+            count = u30()
222
+            for _c2 in range(count):
223
+                u30()
224
+        multiname_count = u30()
225
+        MULTINAME_SIZES = {
226
+            0x07: 2,  # QName
227
+            0x0d: 2,  # QNameA
228
+            0x0f: 1,  # RTQName
229
+            0x10: 1,  # RTQNameA
230
+            0x11: 0,  # RTQNameL
231
+            0x12: 0,  # RTQNameLA
232
+            0x09: 2,  # Multiname
233
+            0x0e: 2,  # MultinameA
234
+            0x1b: 1,  # MultinameL
235
+            0x1c: 1,  # MultinameLA
236
+        }
237
+        self.multinames = ['']
238
+        for _c in range(1, multiname_count):
239
+            kind = u30()
240
+            assert kind in MULTINAME_SIZES, 'Invalid multiname kind %r' % kind
241
+            if kind == 0x07:
242
+                u30()  # namespace_idx
243
+                name_idx = u30()
244
+                self.multinames.append(self.constant_strings[name_idx])
245
+            elif kind == 0x09:
246
+                name_idx = u30()
247
+                u30()
248
+                self.multinames.append(self.constant_strings[name_idx])
249
+            else:
250
+                self.multinames.append(_Multiname(kind))
251
+                for _c2 in range(MULTINAME_SIZES[kind]):
252
+                    u30()
253
+
254
+        # Methods
255
+        method_count = u30()
256
+        MethodInfo = collections.namedtuple(
257
+            'MethodInfo',
258
+            ['NEED_ARGUMENTS', 'NEED_REST'])
259
+        method_infos = []
260
+        for method_id in range(method_count):
261
+            param_count = u30()
262
+            u30()  # return type
263
+            for _ in range(param_count):
264
+                u30()  # param type
265
+            u30()  # name index (always 0 for youtube)
266
+            flags = read_byte()
267
+            if flags & 0x08 != 0:
268
+                # Options present
269
+                option_count = u30()
270
+                for c in range(option_count):
271
+                    u30()  # val
272
+                    read_bytes(1)  # kind
273
+            if flags & 0x80 != 0:
274
+                # Param names present
275
+                for _ in range(param_count):
276
+                    u30()  # param name
277
+            mi = MethodInfo(flags & 0x01 != 0, flags & 0x04 != 0)
278
+            method_infos.append(mi)
279
+
280
+        # Metadata
281
+        metadata_count = u30()
282
+        for _c in range(metadata_count):
283
+            u30()  # name
284
+            item_count = u30()
285
+            for _c2 in range(item_count):
286
+                u30()  # key
287
+                u30()  # value
288
+
289
+        def parse_traits_info():
290
+            trait_name_idx = u30()
291
+            kind_full = read_byte()
292
+            kind = kind_full & 0x0f
293
+            attrs = kind_full >> 4
294
+            methods = {}
295
+            constants = None
296
+            if kind == 0x00:  # Slot
297
+                u30()  # Slot id
298
+                u30()  # type_name_idx
299
+                vindex = u30()
300
+                if vindex != 0:
301
+                    read_byte()  # vkind
302
+            elif kind == 0x06:  # Const
303
+                u30()  # Slot id
304
+                u30()  # type_name_idx
305
+                vindex = u30()
306
+                vkind = 'any'
307
+                if vindex != 0:
308
+                    vkind = read_byte()
309
+                if vkind == 0x03:  # Constant_Int
310
+                    value = self.constant_ints[vindex]
311
+                elif vkind == 0x04:  # Constant_UInt
312
+                    value = self.constant_uints[vindex]
313
+                else:
314
+                    return {}, None  # Ignore silently for now
315
+                constants = {self.multinames[trait_name_idx]: value}
316
+            elif kind in (0x01, 0x02, 0x03):  # Method / Getter / Setter
317
+                u30()  # disp_id
318
+                method_idx = u30()
319
+                methods[self.multinames[trait_name_idx]] = method_idx
320
+            elif kind == 0x04:  # Class
321
+                u30()  # slot_id
322
+                u30()  # classi
323
+            elif kind == 0x05:  # Function
324
+                u30()  # slot_id
325
+                function_idx = u30()
326
+                methods[function_idx] = self.multinames[trait_name_idx]
327
+            else:
328
+                print '[SWFInterpreter] Unsupported trait kind %d' % kind
329
+                return None
330
+
331
+            if attrs & 0x4 != 0:  # Metadata present
332
+                metadata_count = u30()
333
+                for _c3 in range(metadata_count):
334
+                    u30()  # metadata index
335
+
336
+            return methods, constants
337
+
338
+        # Classes
339
+        class_count = u30()
340
+        classes = []
341
+        for class_id in range(class_count):
342
+            name_idx = u30()
343
+
344
+            cname = self.multinames[name_idx]
345
+            avm_class = _AVMClass(name_idx, cname)
346
+            classes.append(avm_class)
347
+
348
+            u30()  # super_name idx
349
+            flags = read_byte()
350
+            if flags & 0x08 != 0:  # Protected namespace is present
351
+                u30()  # protected_ns_idx
352
+            intrf_count = u30()
353
+            for _c2 in range(intrf_count):
354
+                u30()
355
+            u30()  # iinit
356
+            trait_count = u30()
357
+            for _c2 in range(trait_count):
358
+                trait_methods, trait_constants = parse_traits_info()
359
+                avm_class.register_methods(trait_methods)
360
+                if trait_constants:
361
+                    avm_class.constants.update(trait_constants)
362
+
363
+        assert len(classes) == class_count
364
+        self._classes_by_name = dict((c.name, c) for c in classes)
365
+
366
+        for avm_class in classes:
367
+            avm_class.cinit_idx = u30()
368
+            trait_count = u30()
369
+            for _c2 in range(trait_count):
370
+                trait_methods, trait_constants = parse_traits_info()
371
+                avm_class.register_methods(trait_methods)
372
+                if trait_constants:
373
+                    avm_class.constants.update(trait_constants)
374
+
375
+        # Scripts
376
+        script_count = u30()
377
+        for _c in range(script_count):
378
+            u30()  # init
379
+            trait_count = u30()
380
+            for _c2 in range(trait_count):
381
+                parse_traits_info()
382
+
383
+        # Method bodies
384
+        method_body_count = u30()
385
+        Method = collections.namedtuple('Method', ['code', 'local_count'])
386
+        self._all_methods = []
387
+        for _c in range(method_body_count):
388
+            method_idx = u30()
389
+            u30()  # max_stack
390
+            local_count = u30()
391
+            u30()  # init_scope_depth
392
+            u30()  # max_scope_depth
393
+            code_length = u30()
394
+            code = read_bytes(code_length)
395
+            m = Method(code, local_count)
396
+            self._all_methods.append(m)
397
+            for avm_class in classes:
398
+                if method_idx in avm_class.method_idxs:
399
+                    avm_class.methods[avm_class.method_idxs[method_idx]] = m
400
+            exception_count = u30()
401
+            for _c2 in range(exception_count):
402
+                u30()  # from
403
+                u30()  # to
404
+                u30()  # target
405
+                u30()  # exc_type
406
+                u30()  # var_name
407
+            trait_count = u30()
408
+            for _c2 in range(trait_count):
409
+                parse_traits_info()
410
+
411
+        assert p + code_reader.tell() == len(code_tag)
412
+
413
+    def patch_function(self, avm_class, func_name, f):
414
+        self._patched_functions[(avm_class, func_name)] = f
415
+
416
+    def extract_class(self, class_name, call_cinit=True):
417
+        try:
418
+            res = self._classes_by_name[class_name]
419
+        except KeyError:
420
+            print '[SWFInterpreter] Class %r not found' % class_name
421
+            return None
422
+
423
+        if call_cinit and hasattr(res, 'cinit_idx'):
424
+            res.register_methods({'$cinit': res.cinit_idx})
425
+            res.methods['$cinit'] = self._all_methods[res.cinit_idx]
426
+            cinit = self.extract_function(res, '$cinit')
427
+            cinit([])
428
+
429
+        return res
430
+
431
+    def extract_function(self, avm_class, func_name):
432
+        p = self._patched_functions.get((avm_class, func_name))
433
+        if p:
434
+            return p
435
+        if func_name in avm_class.method_pyfunctions:
436
+            return avm_class.method_pyfunctions[func_name]
437
+        if func_name in self._classes_by_name:
438
+            return self._classes_by_name[func_name].make_object()
439
+        if func_name not in avm_class.methods:
440
+            print '[SWFInterpreter] Cannot find function %s.%s' % (
441
+                avm_class.name, func_name)
442
+            return None
443
+        m = avm_class.methods[func_name]
444
+
445
+        def resfunc(args):
446
+            # Helper functions
447
+            coder = io.BytesIO(m.code)
448
+            s24 = lambda: _s24(coder)
449
+            u30 = lambda: _u30(coder)
450
+
451
+            registers = [avm_class.variables] + list(args) + [None] * m.local_count
452
+            stack = []
453
+            scopes = collections.deque([
454
+                self._classes_by_name, avm_class.constants, avm_class.variables])
455
+            while True:
456
+                opcode = _read_byte(coder)
457
+                if opcode == 9:  # label
458
+                    pass  # Spec says: "Do nothing."
459
+                elif opcode == 16:  # jump
460
+                    offset = s24()
461
+                    coder.seek(coder.tell() + offset)
462
+                elif opcode == 17:  # iftrue
463
+                    offset = s24()
464
+                    value = stack.pop()
465
+                    if value:
466
+                        coder.seek(coder.tell() + offset)
467
+                elif opcode == 18:  # iffalse
468
+                    offset = s24()
469
+                    value = stack.pop()
470
+                    if not value:
471
+                        coder.seek(coder.tell() + offset)
472
+                elif opcode == 19:  # ifeq
473
+                    offset = s24()
474
+                    value2 = stack.pop()
475
+                    value1 = stack.pop()
476
+                    if value2 == value1:
477
+                        coder.seek(coder.tell() + offset)
478
+                elif opcode == 20:  # ifne
479
+                    offset = s24()
480
+                    value2 = stack.pop()
481
+                    value1 = stack.pop()
482
+                    if value2 != value1:
483
+                        coder.seek(coder.tell() + offset)
484
+                elif opcode == 21:  # iflt
485
+                    offset = s24()
486
+                    value2 = stack.pop()
487
+                    value1 = stack.pop()
488
+                    if value1 < value2:
489
+                        coder.seek(coder.tell() + offset)
490
+                elif opcode == 32:  # pushnull
491
+                    stack.append(None)
492
+                elif opcode == 33:  # pushundefined
493
+                    stack.append(undefined)
494
+                elif opcode == 36:  # pushbyte
495
+                    v = _read_byte(coder)
496
+                    stack.append(v)
497
+                elif opcode == 37:  # pushshort
498
+                    v = u30()
499
+                    stack.append(v)
500
+                elif opcode == 38:  # pushtrue
501
+                    stack.append(True)
502
+                elif opcode == 39:  # pushfalse
503
+                    stack.append(False)
504
+                elif opcode == 40:  # pushnan
505
+                    stack.append(float('NaN'))
506
+                elif opcode == 42:  # dup
507
+                    value = stack[-1]
508
+                    stack.append(value)
509
+                elif opcode == 44:  # pushstring
510
+                    idx = u30()
511
+                    stack.append(self.constant_strings[idx])
512
+                elif opcode == 48:  # pushscope
513
+                    new_scope = stack.pop()
514
+                    scopes.append(new_scope)
515
+                elif opcode == 66:  # construct
516
+                    arg_count = u30()
517
+                    args = list(reversed(
518
+                        [stack.pop() for _ in range(arg_count)]))
519
+                    obj = stack.pop()
520
+                    res = obj.avm_class.make_object()
521
+                    stack.append(res)
522
+                elif opcode == 70:  # callproperty
523
+                    index = u30()
524
+                    mname = self.multinames[index]
525
+                    arg_count = u30()
526
+                    args = list(reversed(
527
+                        [stack.pop() for _ in range(arg_count)]))
528
+                    obj = stack.pop()
529
+
530
+                    if obj == StringClass:
531
+                        if mname == 'String':
532
+                            assert len(args) == 1
533
+                            assert isinstance(args[0], (
534
+                                int, unicode, _Undefined))
535
+                            if args[0] == undefined:
536
+                                res = 'undefined'
537
+                            else:
538
+                                res = unicode(args[0])
539
+                            stack.append(res)
540
+                            continue
541
+                        else:
542
+                            raise NotImplementedError(
543
+                                'Function String.%s is not yet implemented'
544
+                                % mname)
545
+                    elif isinstance(obj, _AVMClass_Object):
546
+                        func = self.extract_function(obj.avm_class, mname)
547
+                        res = func(args)
548
+                        stack.append(res)
549
+                        continue
550
+                    elif isinstance(obj, _AVMClass):
551
+                        func = self.extract_function(obj, mname)
552
+                        res = func(args)
553
+                        stack.append(res)
554
+                        continue
555
+                    elif isinstance(obj, _ScopeDict):
556
+                        if mname in obj.avm_class.method_names:
557
+                            func = self.extract_function(obj.avm_class, mname)
558
+                            res = func(args)
559
+                        else:
560
+                            res = obj[mname]
561
+                        stack.append(res)
562
+                        continue
563
+                    elif isinstance(obj, unicode):
564
+                        if mname == 'split':
565
+                            assert len(args) == 1
566
+                            assert isinstance(args[0], unicode)
567
+                            if args[0] == '':
568
+                                res = list(obj)
569
+                            else:
570
+                                res = obj.split(args[0])
571
+                            stack.append(res)
572
+                            continue
573
+                        elif mname == 'charCodeAt':
574
+                            assert len(args) <= 1
575
+                            idx = 0 if len(args) == 0 else args[0]
576
+                            assert isinstance(idx, int)
577
+                            res = ord(obj[idx])
578
+                            stack.append(res)
579
+                            continue
580
+                    elif isinstance(obj, list):
581
+                        if mname == 'slice':
582
+                            assert len(args) == 1
583
+                            assert isinstance(args[0], int)
584
+                            res = obj[args[0]:]
585
+                            stack.append(res)
586
+                            continue
587
+                        elif mname == 'join':
588
+                            assert len(args) == 1
589
+                            assert isinstance(args[0], unicode)
590
+                            res = args[0].join(obj)
591
+                            stack.append(res)
592
+                            continue
593
+                    raise NotImplementedError(
594
+                        'Unsupported property %r on %r'
595
+                        % (mname, obj))
596
+                elif opcode == 71:  # returnvoid
597
+                    res = undefined
598
+                    return res
599
+                elif opcode == 72:  # returnvalue
600
+                    res = stack.pop()
601
+                    return res
602
+                elif opcode == 73:  # constructsuper
603
+                    # Not yet implemented, just hope it works without it
604
+                    arg_count = u30()
605
+                    args = list(reversed(
606
+                        [stack.pop() for _ in range(arg_count)]))
607
+                    obj = stack.pop()
608
+                elif opcode == 74:  # constructproperty
609
+                    index = u30()
610
+                    arg_count = u30()
611
+                    args = list(reversed(
612
+                        [stack.pop() for _ in range(arg_count)]))
613
+                    obj = stack.pop()
614
+
615
+                    mname = self.multinames[index]
616
+                    assert isinstance(obj, _AVMClass)
617
+
618
+                    # We do not actually call the constructor for now;
619
+                    # we just pretend it does nothing
620
+                    stack.append(obj.make_object())
621
+                elif opcode == 79:  # callpropvoid
622
+                    index = u30()
623
+                    mname = self.multinames[index]
624
+                    arg_count = u30()
625
+                    args = list(reversed(
626
+                        [stack.pop() for _ in range(arg_count)]))
627
+                    obj = stack.pop()
628
+                    if isinstance(obj, _AVMClass_Object):
629
+                        func = self.extract_function(obj.avm_class, mname)
630
+                        res = func(args)
631
+                        assert res is undefined
632
+                        continue
633
+                    if isinstance(obj, _ScopeDict):
634
+                        assert mname in obj.avm_class.method_names
635
+                        func = self.extract_function(obj.avm_class, mname)
636
+                        res = func(args)
637
+                        assert res is undefined
638
+                        continue
639
+                    if mname == 'reverse':
640
+                        assert isinstance(obj, list)
641
+                        obj.reverse()
642
+                    else:
643
+                        raise NotImplementedError(
644
+                            'Unsupported (void) property %r on %r'
645
+                            % (mname, obj))
646
+                elif opcode == 86:  # newarray
647
+                    arg_count = u30()
648
+                    arr = []
649
+                    for i in range(arg_count):
650
+                        arr.append(stack.pop())
651
+                    arr = arr[::-1]
652
+                    stack.append(arr)
653
+                elif opcode == 93:  # findpropstrict
654
+                    index = u30()
655
+                    mname = self.multinames[index]
656
+                    for s in reversed(scopes):
657
+                        if mname in s:
658
+                            res = s
659
+                            break
660
+                    else:
661
+                        res = scopes[0]
662
+                    if mname not in res and mname in _builtin_classes:
663
+                        stack.append(_builtin_classes[mname])
664
+                    else:
665
+                        stack.append(res[mname])
666
+                elif opcode == 94:  # findproperty
667
+                    index = u30()
668
+                    mname = self.multinames[index]
669
+                    for s in reversed(scopes):
670
+                        if mname in s:
671
+                            res = s
672
+                            break
673
+                    else:
674
+                        res = avm_class.variables
675
+                    stack.append(res)
676
+                elif opcode == 96:  # getlex
677
+                    index = u30()
678
+                    mname = self.multinames[index]
679
+                    for s in reversed(scopes):
680
+                        if mname in s:
681
+                            scope = s
682
+                            break
683
+                    else:
684
+                        scope = avm_class.variables
685
+
686
+                    if mname in scope:
687
+                        res = scope[mname]
688
+                    elif mname in _builtin_classes:
689
+                        res = _builtin_classes[mname]
690
+                    else:
691
+                        # Assume unitialized
692
+                        # TODO warn here
693
+                        res = undefined
694
+                    stack.append(res)
695
+                elif opcode == 97:  # setproperty
696
+                    index = u30()
697
+                    value = stack.pop()
698
+                    idx = self.multinames[index]
699
+                    if isinstance(idx, _Multiname):
700
+                        idx = stack.pop()
701
+                    obj = stack.pop()
702
+                    obj[idx] = value
703
+                elif opcode == 98:  # getlocal
704
+                    index = u30()
705
+                    stack.append(registers[index])
706
+                elif opcode == 99:  # setlocal
707
+                    index = u30()
708
+                    value = stack.pop()
709
+                    registers[index] = value
710
+                elif opcode == 102:  # getproperty
711
+                    index = u30()
712
+                    pname = self.multinames[index]
713
+                    if pname == 'length':
714
+                        obj = stack.pop()
715
+                        assert isinstance(obj, (unicode, list))
716
+                        stack.append(len(obj))
717
+                    elif isinstance(pname, unicode):  # Member access
718
+                        obj = stack.pop()
719
+                        if isinstance(obj, _AVMClass):
720
+                            res = obj.static_properties[pname]
721
+                            stack.append(res)
722
+                            continue
723
+
724
+                        assert isinstance(obj, (dict, _ScopeDict)),\
725
+                            'Accessing member %r on %r' % (pname, obj)
726
+                        res = obj.get(pname, undefined)
727
+                        stack.append(res)
728
+                    else:  # Assume attribute access
729
+                        idx = stack.pop()
730
+                        assert isinstance(idx, int)
731
+                        obj = stack.pop()
732
+                        assert isinstance(obj, list)
733
+                        stack.append(obj[idx])
734
+                elif opcode == 104:  # initproperty
735
+                    index = u30()
736
+                    value = stack.pop()
737
+                    idx = self.multinames[index]
738
+                    if isinstance(idx, _Multiname):
739
+                        idx = stack.pop()
740
+                    obj = stack.pop()
741
+                    obj[idx] = value
742
+                elif opcode == 115:  # convert_
743
+                    value = stack.pop()
744
+                    intvalue = int(value)
745
+                    stack.append(intvalue)
746
+                elif opcode == 128:  # coerce
747
+                    u30()
748
+                elif opcode == 130:  # coerce_a
749
+                    value = stack.pop()
750
+                    # um, yes, it's any value
751
+                    stack.append(value)
752
+                elif opcode == 133:  # coerce_s
753
+                    assert isinstance(stack[-1], (type(None), unicode))
754
+                elif opcode == 147:  # decrement
755
+                    value = stack.pop()
756
+                    assert isinstance(value, int)
757
+                    stack.append(value - 1)
758
+                elif opcode == 149:  # typeof
759
+                    value = stack.pop()
760
+                    return {
761
+                        _Undefined: 'undefined',
762
+                        unicode: 'String',
763
+                        int: 'Number',
764
+                        float: 'Number',
765
+                    }[type(value)]
766
+                elif opcode == 160:  # add
767
+                    value2 = stack.pop()
768
+                    value1 = stack.pop()
769
+                    res = value1 + value2
770
+                    stack.append(res)
771
+                elif opcode == 161:  # subtract
772
+                    value2 = stack.pop()
773
+                    value1 = stack.pop()
774
+                    res = value1 - value2
775
+                    stack.append(res)
776
+                elif opcode == 162:  # multiply
777
+                    value2 = stack.pop()
778
+                    value1 = stack.pop()
779
+                    res = value1 * value2
780
+                    stack.append(res)
781
+                elif opcode == 164:  # modulo
782
+                    value2 = stack.pop()
783
+                    value1 = stack.pop()
784
+                    res = value1 % value2
785
+                    stack.append(res)
786
+                elif opcode == 168:  # bitand
787
+                    value2 = stack.pop()
788
+                    value1 = stack.pop()
789
+                    assert isinstance(value1, int)
790
+                    assert isinstance(value2, int)
791
+                    res = value1 & value2
792
+                    stack.append(res)
793
+                elif opcode == 171:  # equals
794
+                    value2 = stack.pop()
795
+                    value1 = stack.pop()
796
+                    result = value1 == value2
797
+                    stack.append(result)
798
+                elif opcode == 175:  # greaterequals
799
+                    value2 = stack.pop()
800
+                    value1 = stack.pop()
801
+                    result = value1 >= value2
802
+                    stack.append(result)
803
+                elif opcode == 192:  # increment_i
804
+                    value = stack.pop()
805
+                    assert isinstance(value, int)
806
+                    stack.append(value + 1)
807
+                elif opcode == 208:  # getlocal_0
808
+                    stack.append(registers[0])
809
+                elif opcode == 209:  # getlocal_1
810
+                    stack.append(registers[1])
811
+                elif opcode == 210:  # getlocal_2
812
+                    stack.append(registers[2])
813
+                elif opcode == 211:  # getlocal_3
814
+                    stack.append(registers[3])
815
+                elif opcode == 212:  # setlocal_0
816
+                    registers[0] = stack.pop()
817
+                elif opcode == 213:  # setlocal_1
818
+                    registers[1] = stack.pop()
819
+                elif opcode == 214:  # setlocal_2
820
+                    registers[2] = stack.pop()
821
+                elif opcode == 215:  # setlocal_3
822
+                    registers[3] = stack.pop()
823
+                else:
824
+                    raise NotImplementedError(
825
+                        'Unsupported opcode %d' % opcode)
826
+
827
+        avm_class.method_pyfunctions[func_name] = resfunc
828
+        return resfunc

+ 277
- 0
sources/tvdom.py View File

@@ -0,0 +1,277 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, re, sys,os
15
+from collections import OrderedDict
16
+from SourceBase import SourceBase
17
+
18
+API_URL = 'http://replay.lsm.lv/'
19
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
20
+headers0 = headers2dict("""
21
+User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; Nexus 5 Build/KTU84P) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
22
+""")
23
+import HTMLParser
24
+h = HTMLParser.HTMLParser()
25
+
26
+class Source(SourceBase):
27
+
28
+    def __init__(self,country="lv"):
29
+        self.name = "tvdom"
30
+        self.title = "TVDom.tv"
31
+        self.img = "https://tvdom.tv/front/assets/images/logo.png"
32
+        self.desc = "TVDom.tv portāla satura skatīšanās"
33
+        self.headers = headers0
34
+
35
+        self.country=country
36
+        self.session = None
37
+        self.token = None
38
+
39
+        cur_directory = os.path.dirname(os.path.abspath(__file__))
40
+        self.config_file = os.path.join(cur_directory,self.name+".cfg")
41
+        self.options = OrderedDict([("user","lietotajs"),("password","parole")])
42
+        self.options_read()
43
+
44
+    def login(self,user="",password=""):
45
+        self.options_read()        
46
+        if not user: user=self.options["user"]
47
+        if not password: password = self.options["password"]
48
+        headers = headers2dict("""
49
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
50
+Accept: */*
51
+Accept-Language: en-US,en;q=0.5
52
+Accept-Encoding: gzip, deflate, br
53
+Content-Type: application/x-www-form-urlencoded; charset=UTF-8
54
+X-Requested-With: XMLHttpRequest
55
+Referer: https://tvdom.tv/
56
+        """)
57
+        url = "https://tvdom.tv/infinity/on_register_user"
58
+        params = "email=%s&password=%s&remember=false&auth_type=login"%(user,password)
59
+        import requests
60
+        r = requests.post(url, data=params, headers=headers)	
61
+        js = json.loads(r.content)
62
+        if 'success' in r.content:
63
+            self.token = js["access_token"] 
64
+            if 'PHPSESSID' in r.cookies:
65
+                self.session = r.cookies["PHPSESSID"]
66
+            return True
67
+        else:
68
+            raise Exception(js["error"])
69
+
70
+
71
+    def get_content(self, data):
72
+        print "[tvdom] get_content:", data
73
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
74
+        lang = qs["lang"] if "lang" in qs else self.country
75
+        content=[]
76
+        content.append(("..return", "back","","Return back"))
77
+
78
+        if clist=="home":
79
+            content.extend([
80
+                ("Live stream", "tvdom::tiesraides","","TV live streams"),
81
+                ("Archive - categories", "tvdom::arhivs","","Video archive by categories"),
82
+                ("Search", "tvdom::search/?srch-term={0}","","Search archive"),
83
+
84
+                #("Archive - all", "tvdom::arhivs_all","","Video archive all"),
85
+            ])
86
+            return content
87
+
88
+        ### Tiesraides kanalu saraksts ###
89
+        elif data=="tiesraides":
90
+            ch_name = {"49":"Дом Кино","50":"Карусель","51":"Время","52":"Музыка Первого","53":"Телекафе"}
91
+            url = "https://tvdom.tv/"
92
+            r = self._http_request(url)
93
+            #channels1 = re.findall(r'data-xprs_status="\d" data-href="/tiesraides/([^/]+)/[^"]+">.+?src="([^"]+)" alt="([^"]+)">', r, re.DOTALL)
94
+            channels2 = re.findall(r'<a class="channel-name">([^<]*)</a>\s+<div class="redirect-to-url" data-href="/tiesraides/([^/]+)/[^"]+">.+?<img style="width:100%;" src="([^"]+)".+?<h3>([^<]+)</h3>.+?<p class="unix">([^<]+)</p>', r, re.DOTALL)
95
+            channels = {}
96
+            for item in channels2:
97
+                title = item[0]
98
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
99
+                img = "https://tvdom.tv"+item[2]
100
+                data2 = "tiesraides/%s/"%item[1]
101
+                desc = "%s\n%s\n%s"%(title,item[3],item[4])		    
102
+                channels[item[1]]={"title":title,"img":img,"desc":desc}
103
+                #content.append((title,self.name+"::"+data2,img,desc))
104
+
105
+            for r2 in re.findall(r'<div class="thumbnail-menu pull-left updater"(.+?)</div>\s+</div>', r, re.DOTALL):
106
+                ch = re.search('data-href="/tiesraides/([^/]+)/[^"]+"', r2, re.DOTALL).group(1)
107
+                data2 = "tiesraides/"+ch
108
+                m = re.search('src="(.+?)" alt="(.+?)">', r2, re.DOTALL)
109
+                title = m.group(2)
110
+                img = m.group(1)
111
+                desc = title
112
+                if ch in channels:
113
+                    img = channels[ch]["img"]
114
+                    desc = channels[ch]["desc"]
115
+                else:
116
+                    m = re.search('<img src="([^"]+)" alt="aaaaaa([^"]+)"><h3>.+<p class="unix">([^<]+)</p>', r2, re.DOTALL)
117
+                    if m:
118
+                        desc  = "%s\n%s\n%s"%(title,m.group(1),m.group(3))
119
+                        img = m.group(2)
120
+                content.append((title,self.name+"::"+data2,img,desc))
121
+            return content
122
+
123
+        elif clist == "tiesraides":
124
+            if not self.session:
125
+                self.login()
126
+            url = "https://tvdom.tv/" + data
127
+            headers = self.headers
128
+            headers["Cookie"] = "PHPSESSID=%s; neverending_story=1;"%self.session
129
+            r = self._http_request(url,headers=headers)
130
+            m = re.search("var streamConnectionUrl = '([^']+)'", r, re.DOTALL)
131
+            if m:
132
+                data2 = m.group(1)
133
+            else:
134
+                return ("No stream found %s"%data,"","","No stream found")
135
+            m = re.search('title: "([^"]+)"', r, re.DOTALL)
136
+            title = m.group(1) if m else data2
137
+            m = re.search('<div id="panel">([^<]+)<', r, re.DOTALL)
138
+            desc = m.group(1) if m else title
139
+            m = re.search('<div id="panel">([^<]+)<', r, re.DOTALL)
140
+            desc = m.group(1) if m else title 
141
+            m = re.search('var promo_image *= "([^"]+)', r, re.DOTALL)
142
+            img = m.group(1) if m else ""            
143
+            return (title,data2,img,desc)
144
+
145
+        ### Search ###
146
+        elif clist=="search":
147
+            url = "https://tvdom.tv/" + data
148
+            r = self._http_request(url)
149
+            for item in re.findall(r'<li data-xprs-search="\d+" data-href="([^"]+)".*?<img class="img-responsive" src="([^"]+)".*?<h3>([^<]+)</h3>.*?<h5>([^<]+)</h5>', r, re.DOTALL):
150
+                title = item[2] + " "+ item[3]
151
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
152
+                img = "https://tvdom.tv" + item[1]
153
+                data2 = item[0][1:]
154
+                desc = title
155
+                content.append((title,self.name+"::"+data2,img,desc))
156
+            return content
157
+
158
+        ### Arhīva kategorijas ###
159
+        elif data=="arhivs":
160
+            url = "https://tvdom.tv/"+data
161
+            r = self._http_request(url)
162
+            for item in re.findall('pointer" href="/([^"]+)">([^<]+)</a>', r, re.DOTALL):
163
+                title = item[1]
164
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
165
+                img = ""
166
+                data2 = item[0]
167
+                desc = title
168
+                content.append((title,self.name+"::"+data2,img,desc))
169
+            return content
170
+
171
+        ### Arhīva kategorijas programmas ###
172
+        elif clist=="arhivs":
173
+            url = "https://tvdom.tv/"+data
174
+            r = self._http_request(url)
175
+            for item in re.findall(r"""<li><div class="thumbnail pull-left" onclick="location\.href='([^']+)'" data-toggle="popover" title="([^"]+)" data-content="([^"]*)".+?<img class="img-responsive archive-image" src="([^"]+)""", r, re.DOTALL):
176
+            #for item in re.findall(r"""<li><div class="thumbnail pull-left" onclick="location\.href='([^']+)'" data-toggle="popover" title="([^"]+)" data-content="([^"]+)".+?<img class="img-responsive archive-image" src="([^"]+)""", r, re.DOTALL):
177
+                title = item[1].replace("&lt;br&gt;"," - ")
178
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
179
+                img = "https://tvdom.tv"+item[3]
180
+                data2 = item[0][1:]
181
+                desc = item[2]
182
+                content.append((title,self.name+"::"+data2,img,desc))
183
+            return content
184
+
185
+        ### Arhīva programmas video saraksts ###
186
+        elif clist=="play_arhivs" and len(data.split("/"))==3 and not re.search("_\d+",plist[2]):
187
+            url = "https://tvdom.tv/"+data
188
+            r = self._http_request(url)
189
+            vid=re.search(r"id:(\d+), type: type", r, re.DOTALL).group(1)
190
+            data2 = data+"_"+vid
191
+            m = re.search('program_title        = "([^"]+)"', r, re.DOTALL)
192
+            title = m.group(1) if m else data2
193
+            m = re.search('<a class="episode">Pārraides laiks ēterā: <span>([^<]+)</span></a>', r, re.DOTALL)
194
+            datums = m.group(1) if m else ""
195
+            title = title + " " + datums
196
+            m = re.search('<div id="panel">([^<]+)<', r, re.DOTALL)
197
+            desc = m.group(1) if m else title
198
+            m = re.search('<div id="panel">([^<]+)<', r, re.DOTALL)
199
+            desc = m.group(1) if m else title 
200
+            m = re.search('var share_image *= "([^"]+)', r, re.DOTALL)
201
+            img = m.group(1) if m else ""            
202
+            content.append((title,self.name+"::"+data2,img,desc)) 
203
+            i = r.find('<span class="slider-top-title"')
204
+            if i>0: r = r[:i]
205
+            for item in re.findall('<div class="col-md-9 redirect-to-url same-event" data-href="/([^"]+)">.+?image" src="([^"]+)".+?<h3 class="same-title">([^<]+)</h3>.*?<h5 class="same-online">([^<]+)</h5>', r, re.DOTALL):
206
+                title = item[2] + " " + item[3]
207
+                title =  h.unescape(title.decode("utf8")).encode("utf8")
208
+                img = "https://tvdom.tv"+item[1]
209
+                data2 = item[0]
210
+                desc = title # TODO
211
+                content.append((title,self.name+"::"+data2,img,desc))
212
+            return content
213
+
214
+        ### Arhīva video
215
+        elif clist=="play_arhivs" and len(data.split("/"))==3 and re.search("_\d+",plist[2]):
216
+            url = "https://tvdom.tv/" + data
217
+            headers = self.headers
218
+            headers["Cookie"] = "PHPSESSID=%s; neverending_story=1;"%self.session
219
+            r = self._http_request(url,headers=headers)
220
+            m = re.search('var streamConnectionUrl  = "([^"]+)"', r, re.DOTALL)
221
+            if m:
222
+                data2 = m.group(1)
223
+            else:
224
+                return ("No stream found %s"%data,"","","No stream found")
225
+            m = re.search('program_title        = "([^"]+)"', r, re.DOTALL)
226
+            title = m.group(1) if m else data2
227
+            m = re.search('<a class="episode">Pārraides laiks ēterā: <span>([^<]+)</span></a>', r, re.DOTALL)
228
+            datums = m.group(1) if m else ""
229
+            title = title + " " + datums
230
+            m = re.search('<div id="panel">([^<]+)<', r, re.DOTALL)
231
+            desc = m.group(1) if m else title
232
+            m = re.search('<div id="panel">([^<]+)<', r, re.DOTALL)
233
+            desc = m.group(1) if m else title 
234
+            m = re.search('var share_image *= "([^"]+)', r, re.DOTALL)
235
+            img = m.group(1) if m else ""            
236
+            return (title,data2,img,desc)
237
+
238
+
239
+    def is_video(self,data):
240
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
241
+        cmd = data.split("/")
242
+        if cmd[0] in ("tiesraides") and len(cmd)>1:
243
+            return True
244
+        elif cmd[0]=="play_arhivs" and len(cmd)==3 and re.search("_\d+",plist[2]):
245
+            return True
246
+        else:
247
+            return False
248
+
249
+    def call(self, data,headers=headers0,lang=""):
250
+        if not lang: lang = self.country
251
+        url = API_URL%lang + data
252
+        #print "[TVPlay Api] url: ",url
253
+        result = []
254
+        content = self._http_request(url)
255
+        return content
256
+
257
+if __name__ == "__main__":
258
+    country= "lv"
259
+    c = Source(country)
260
+    if len(sys.argv)>1:
261
+        data= sys.argv[1]
262
+    else:
263
+        data = "home"
264
+    content = c.get_content(data)
265
+    for item in content:
266
+        print item
267
+    #cat = api.get_categories(country)
268
+    #chan = api.get_channels("lv")
269
+    #prog = api.get_programs(channel=6400)
270
+    #prog = api.get_programs(category=55)
271
+    #seas = api.get_seasons(program=6453)
272
+    #str = api.get_streams(660243)
273
+    #res = api.get_videos(802)
274
+    #formats = api.getAllFormats()
275
+    #det = api.detailed("1516")
276
+    #vid = api.getVideos("13170")
277
+    pass

+ 175
- 0
sources/ustvnow.py View File

@@ -0,0 +1,175 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import urllib2, urllib
14
+import datetime, re, sys,os
15
+from collections import OrderedDict
16
+from SourceBase import SourceBase
17
+
18
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
19
+headers0 = headers2dict("""
20
+Host: m-api.ustvnow.com
21
+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
22
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
23
+DNT: 1
24
+Connection: keep-alive
25
+""")
26
+import HTMLParser
27
+h = HTMLParser.HTMLParser()
28
+
29
+class Source(SourceBase):
30
+
31
+    def __init__(self,country="lv"):
32
+        self.name = "ustvnow"
33
+        self.title = "USTVNow"
34
+        self.img = "http://watch.ustvnow.com/assets/ustvnow/img/ustvnow_og_image.png"
35
+        self.desc = "USTVNow kanālu tiešraide"
36
+        self.headers = headers0
37
+
38
+        self.country=country
39
+        self.token = ""
40
+        cur_directory = os.path.dirname(os.path.abspath(__file__))
41
+        self.config_file = os.path.join(cur_directory,self.name+".cfg")
42
+        self.options = OrderedDict([("user","lietotajs"),("password","parole")])
43
+        self.options_read()
44
+
45
+    def login(self,user="",password=""):
46
+        if not user: user=self.options["user"]
47
+        if not password: password = self.options["password"]
48
+        self.options_read()        
49
+        headers = headers2dict("""
50
+        Host: m-api.ustvnow.com
51
+        Accept-Language: en-US,en;q=0.5
52
+        User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
53
+        Accept: text/html,application/xhtml+xml,application/xml
54
+        Connection: keep-alive
55
+        """)
56
+
57
+        url = "http://m-api.ustvnow.com/iphone/1/live/login?username=%s&password=%s&device=gtv&redir=0"%(user,password)
58
+        r = self._http_request(url,headers)
59
+        if 'success' in r:
60
+            self.token = re.search('"token":"([^"]+)',r).group(1)
61
+            return True
62
+        else:
63
+            return False
64
+
65
+    def get_content(self, data):
66
+        print "[tvdom] get_content:", data
67
+        if "::" in data:
68
+            data = data.split("::")[1] 
69
+        path = data.split("?")[0]
70
+        clist = path.split("/")[0]
71
+        params = data[data.find("?"):] if "?" in data else ""
72
+        qs = dict(map(lambda x:x.split("="),re.findall("\w+=\w+",params)))
73
+        lang = qs["lang"] if "lang" in qs else self.country
74
+
75
+        content=[]
76
+        content.append(("..return", "back","","Return back"))
77
+
78
+        if clist=="home":
79
+            content.extend([
80
+                ("TV live streams", "ustvnow::tvlive","","TV live streams"),
81
+                ("Movies", "ustvnow::movies","","Movies (not implemented yet"),
82
+                ("Recordings", "ustvnow::recordings","","Recordings (not implemented yet"),
83
+            ])
84
+            return content
85
+
86
+        if clist=="movies":
87
+            return content
88
+
89
+        if clist=="recordings":
90
+            return content
91
+
92
+        ### Tiesraides kanalu saraksts ###
93
+        elif data=="tvlive":
94
+            if not self.token:
95
+                if not self.login():
96
+                    raise Exception("Can not login\nPlease check USTVNow username/password in\n/usr/lib/enigma2/python/Plugins/Extensions/sources/ustvnow.cfg file")
97
+            data = "live/channelguide?token=%s"%self.token
98
+            self.r = self.call(data)
99
+            if not self.r:
100
+                return content
101
+            for item in self.r["results"]:
102
+                if item["order"] == 1:    
103
+                    title = item["stream_code"]
104
+                    title =  h.unescape(title.decode("utf8")).encode("utf8")
105
+                    img = "http://m-api.ustvnow.com/"+item["prg_img"] #item["img"]
106
+                    data2 = "live/view?scode=%s&token=%s"%(item["scode"],self.token)
107
+                    desc = "%s\n%s (+%s')\n%s"%(item["title"],item["event_time"],int(item["actualremainingtime"])/60,item["description"])
108
+                    content.append((title,self.name+"::"+data2,img,desc))
109
+            return content
110
+
111
+        ### Tiesraides kanāls ###
112
+        elif path == "live/view":
113
+            url = "http://m-api.ustvnow.com/stream/1/%s"%data
114
+            r = self._http_request(url)
115
+            if not r:
116
+                return ("No stream found %s"%data,"","","No stream found")
117
+            r = json.loads(r)
118
+            if self.r:
119
+                ch = qs["scode"]
120
+                for item in self.r["results"]:
121
+                    if item["order"] == 1 and item["scode"] == ch:
122
+                        title = item["stream_code"]
123
+                        title = "%s - %s (%s)"%(item["stream_code"],item["title"],item["event_time"])
124
+                        img = "http://m-api.ustvnow.com/"+item["prg_img"]
125
+                        data2 = "live/view?scode=%s&token=%s"%(item["scode"],self.token)
126
+                        desc = "%s\n%s (+%s')\n%s"%(item["title"],item["event_time"],int(item["actualremainingtime"])/60,item["description"])
127
+            else:
128
+                title = data
129
+            data2 = r["stream"]
130
+            desc = title
131
+            img = ""          
132
+            return (title,data2,img,desc)               
133
+
134
+    def is_video(self,data):
135
+        if "::" in data:
136
+            data = data.split("::")[1]
137
+        if "live/view" in data:
138
+            return True
139
+        else:
140
+            return False
141
+
142
+    def call(self, data,headers=headers0,lang=""):
143
+        if not lang: lang = self.country
144
+        url = "http://m-api.ustvnow.com/gtv/1/"+data
145
+        content = self._http_request(url)
146
+        result = None
147
+        if content:
148
+            try:
149
+                result = json.loads(content)
150
+            except Exception, ex:
151
+                return None
152
+        return result
153
+
154
+
155
+if __name__ == "__main__":
156
+    country= "lv"
157
+    c = Source(country)
158
+    if len(sys.argv)>1:
159
+        data= sys.argv[1]
160
+    else:
161
+        data = "home"
162
+    content = c.get_content(data)
163
+    for item in content:
164
+        print item
165
+    #cat = api.get_categories(country)
166
+    #chan = api.get_channels("lv")
167
+    #prog = api.get_programs(channel=6400)
168
+    #prog = api.get_programs(category=55)
169
+    #seas = api.get_seasons(program=6453)
170
+    #str = api.get_streams(660243)
171
+    #res = api.get_videos(802)
172
+    #formats = api.getAllFormats()
173
+    #det = api.detailed("1516")
174
+    #vid = api.getVideos("13170")
175
+    pass

+ 477
- 0
sources/viaplay.py View File

@@ -0,0 +1,477 @@
1
+#!/usr/bin/env python
2
+# coding=utf8
3
+#
4
+# This file is part of PlayStream - enigma2 plugin to play video streams from various sources
5
+# Copyright (c) 2016 ivars777 (ivars777@gmail.com)
6
+# Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
7
+#
8
+try:
9
+    import json
10
+except:
11
+    import simplejson as json
12
+
13
+import requests, urlparse, urllib
14
+import datetime, time,re, sys,os
15
+from collections import OrderedDict
16
+from SourceBase import SourceBase
17
+try:
18
+    import util
19
+except:
20
+    parent = os.path.dirname(os.path.abspath(__file__))
21
+    parent = os.sep.join(parent.split(os.sep)[:-1])
22
+    sys.path.insert(0,parent)
23
+    import util
24
+
25
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
26
+    
27
+class Source(SourceBase):
28
+    
29
+    def __init__(self,language="en"):
30
+        self.name = "viaplay"
31
+        self.title = "viaplay.lv"
32
+        self.img = "https://yt3.ggpht.com/-noVdjbNR-V8/AAAAAAAAAAI/AAAAAAAAAAA/yZ9XNP5urLY/s900-c-k-no-mo-rj-c0xffffff/photo.jpg"
33
+        self.desc = "Viaplay.lv saturs"
34
+        self.url = "https://viaplay.lv/"
35
+        self.headers = headers2dict("""
36
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
37
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
38
+Connection: keep-alive
39
+Upgrade-Insecure-Requests: 1
40
+        """)
41
+        #self.language=language
42
+        cur_directory = os.path.dirname(os.path.abspath(__file__))
43
+        self.config_file = os.path.join(cur_directory,self.name+".cfg")
44
+        self.options = OrderedDict([("user","change_user"),("password","change_password"),("device","")])
45
+        self.options_read()
46
+        self.device = self.options["device"]
47
+        self.r = None # requests
48
+        self.play_session = None
49
+        self.s = None
50
+        
51
+    def login(self,user="",password=""):
52
+        self.options_read()
53
+        if not user: user=self.options["user"]
54
+        if not password: password = self.options["password"]
55
+        self.s = requests.Session()
56
+        
57
+        ### Dabu sesijas ID ===
58
+        headers = headers2dict("""
59
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
60
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
61
+Accept-Language: en-US,en;q=0.5
62
+Accept-Encoding: gzip, deflate, br
63
+Referer: https://viaplay.lv/
64
+Cookie: ott_cookies_confirmed=1; 
65
+DNT: 1
66
+Connection: keep-alive
67
+Upgrade-Insecure-Requests: 1
68
+""")   
69
+        r = requests.get(self.url,headers=headers)
70
+        if not "PLAY_SESSION" in r.cookies:
71
+            return False
72
+        self.play_session = r.cookies["PLAY_SESSION"]
73
+        self.csrfToken = re.search("csrfToken=(.+)",self.play_session).group(1)
74
+        
75
+        ### Ielogojamies ###
76
+        headers = headers2dict("""
77
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
78
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
79
+Accept-Language: en-US,en;q=0.5
80
+Accept-Encoding: gzip, deflate, br
81
+Referer: https://viaplay.lv/
82
+Cookie: ott_cookies_confirmed=1; PLAY_SESSION=e618c42b377a65021298ff63309d5a907988ed1b-PSESSIONID=b010ea1b-fc5e-4a18-aa15-ebbe8b57b3f0&csrfToken=b4eb35263d9be16ef9f7b2f5d10a8ee99dfe75a8-1478051634814-63682b20f1e7e5579de6d056
83
+DNT: 1
84
+Connection: keep-alive
85
+Upgrade-Insecure-Requests: 1
86
+Content-Type: application/x-www-form-urlencoded
87
+""")
88
+        url = "https://viaplay.lv/tdi/login/nav/formular?csrfToken=%s"%self.csrfToken
89
+        params = "nav_redirectUri=https%3A%2F%2Fviaplay.lv%2F&nav_email={}&nav_password={}".format(urllib.quote(user),urllib.quote(password))
90
+        headers["Cookie"] = "ott_cookies_confirmed=1; PLAY_SESSION=%s;"%self.play_session
91
+        if self.device:
92
+            headers["Cookie"] += "ott_dids=%s"%self.device
93
+        #cookie = dict(PLAY_SESSION=self.play_session,_hjIncludedInSample=1, mobileAppPromo="shown")
94
+        r = requests.post(url,params,headers=headers,allow_redirects=False)
95
+        if not "Set-Cookie" in r.headers:
96
+            self.play_session = None
97
+            return False
98
+        self.ott = r.cookies["ott_web_sac"]
99
+        
100
+        ### Dabu iekārtas ID ###
101
+        if not self.device:
102
+            headers = headers2dict("""
103
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
104
+Accept: application/xml, text/xml, */*; q=0.01
105
+Accept-Language: en-US,en;q=0.5
106
+Accept-Encoding: gzip, deflate, br
107
+Content-Type: application/x-www-form-urlencoded; charset=UTF-8
108
+X-Requested-With: XMLHttpRequest
109
+Referer: https://viaplay.lv/movies/me-and-earl-and-the-dying-girl
110
+DNT: 1
111
+Connection: keep-alive    """)
112
+            url = "https://viaplay.lv/tdi/account/device/create?_infuse=1&csrfToken=%s"%self.csrfToken
113
+            params = "successRedirectUri=https%3A%2F%2Fviaplay.lv%2Fmovies%2F&slotId=&title=Enigma2"
114
+            headers["Cookie"] = "PLAY_SESSION=%s; ott_cookies_confirmed=1; ott_web_sac=%s;"%(self.play_session,self.ott)
115
+            #cookie = dict(PLAY_SESSION=self.play_session,_hjIncludedInSample=1, mobileAppPromo="shown")
116
+            r = requests.post(url,params,headers=headers,allow_redirects=False)
117
+            if not ("Set-Cookie" in r.headers and "ott_dids" in r.headers["Set-Cookie"]):
118
+                self.play_session = None
119
+                return False
120
+            self.device =  r.cookies["ott_dids"]
121
+            self.options["device"] = self.device
122
+            self.options_write(self.options)
123
+        return True
124
+    
125
+    def logout(self):
126
+        return True
127
+    
128
+    def is_logedin(self):
129
+        if self.play_session:
130
+            return True
131
+        else:
132
+            return False
133
+
134
+    def get_video_info(self,vid):
135
+        import demjson
136
+        ### Dabu strimus ###
137
+        headers = headers2dict("""
138
+Host: viaplay.lv
139
+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
140
+Accept: application/xml, text/xml, */*; q=0.01
141
+Accept-Language: en-US,en;q=0.5
142
+Accept-Encoding: gzip, deflate, br
143
+X-Requested-With: XMLHttpRequest
144
+DNT: 1
145
+Connection: keep-alive
146
+Referer: https://viaplay.lv/
147
+""")   
148
+        url = "https://viaplay.lv/prehravac/init?_infuse=1&productId=%s"%vid #t110623
149
+        headers["Cookie"] = "ott_cookies_confirmed=1; ott_dids=%s; PLAY_SESSION=%s"%(self.device,self.play_session)
150
+        r = requests.get(url,headers=headers,allow_redirects=False)
151
+        statuss = re.search("<status>(.+?)</status>", r.content).group(1)
152
+        if statuss.lower() <> "ok":
153
+            raise Exception(statuss)
154
+        #print r.content
155
+        m = re.search(r"<!\[CDATA\[\s+var TDIPlayerOptions = (.+?);[\n\t\s]+\]\]>\s+</script>", r.content, re.DOTALL)
156
+        if not m:
157
+            raise "Can not find stream info"
158
+        txt = m.group(1)
159
+        txt = re.sub("// .+$", "", txt, flags=re.MULTILINE)
160
+        #print txt
161
+        #for m in re.finditer("// .+$", txt, re.MULTILINE):
162
+        #    txt = txt[:m.start()] + txt[m.end():]
163
+        #print txt
164
+        js = demjson.decode(txt)
165
+        return js
166
+        #return txt
167
+            
168
+    
169
+    def get_content(self, data):
170
+        print "[%s] get_content:"%self.name, data
171
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
172
+        content=[]
173
+        content.append(("..return", "back","","Return back"))
174
+        
175
+        if clist=="home":
176
+            content.extend([
177
+                ("Search", "viaplay::search-results-all?query={0}",self.img,"Meklēt"),                
178
+                ("Filmas", "viaplay::movies",self.img,"Filmas"),
179
+                ("Seriāli", "viaplay::series",self.img,"Seriāli"),
180
+                ("Bērniem", "viaplay::kids",self.img,"Bērniem"),
181
+                ("Dokumentalās filmas", "viaplay::documentary",self.img,"Dokumentalās filmas"),
182
+                ("Sports", "viaplay::live",self.img,"Sports"),
183
+             ])
184
+            return content
185
+        
186
+        ### Meklēt ###
187
+        elif clist=="search-results-all":
188
+            url = "https://viaplay.lv/"+data
189
+            r = self._http_request(url)
190
+            result = re.findall(r'<div id="product-(\w+)".+?<a href="([^"]+)">.+?<img data-srcset="([^ ]+).+?alt="([^"]+)">.+?<h3 class="is-size-h6">([^<]+)</h3>.*?<p>([^<]+).+?</p>.+?<p class="promo-notice">([^<]+)</p>.+?<p class="info">([^<]+)</p>', r, re.DOTALL)
191
+            for item in result:
192
+                vid = item[0]
193
+                data2 = item[1].replace("https://viaplay.lv/","")
194
+                img = item[2]
195
+                ep = item[3]
196
+                title= item[4]
197
+                seas = item[5].replace("\n","").replace("\t","")
198
+                desc = item[6]
199
+                desc2 = item[7]
200
+                if ep==title:
201
+                    title = "%s (%s)"%(title,seas)
202
+                else:
203
+                    title = "%s - %s%s"%(title,seas,ep)
204
+                desc = "%s\n%s\n%s"%(title,desc2,desc)
205
+                content.append((title,self.name+"::"+data2,img,desc))             
206
+            return content
207
+        
208
+        ### Sadalas ##  
209
+        elif data in ["movies","series","kids","documentary"]:
210
+            r = self._http_request(self.url+data)
211
+            # https://viaplay.lv/tdi/movies/next?sections[]=MOVIES&genres[]=a3591&sort[]=latest&offset=0
212
+            # https://viaplay.lv/tdi/series/next?sections[]=SERIES&sort[]=latest&offset=0
213
+            # https://viaplay.lv/tdi/kids/next?sections[]=KIDS&cat[]=SERIES&cat[]=MOVIE&sort[]=latest&offset=18
214
+            # https://viaplay.lv/kids?sections[]=KIDS&cat[]=SERIES&sort[]=latest
215
+            sections =  {"movies":"MOVIES","series":"SERIES","kids":"KIDS","documentary":"DOCUMENTS"}
216
+            nosaukums = {"movies":"Flmas","series":"Seriāli","kids":"Bērnu","documentary":"Dokumentalās"}
217
+            #availability = {"new":"jaunākās","last":"pēdējā iespēja"}
218
+            sort = OrderedDict([("latest","jaunākais"),("title","pēc nosaukuma"),("popular","pēc popularitātes"),("year","pēc gada")])
219
+            for s in sort:
220
+                if data in ("movies","series"):
221
+                    title = "%s - %s"%(nosaukums[data],sort[s])
222
+                    data2 = "%s/next?sections[]=%s&sort[]=%s"%(data,sections[data],s)
223
+                    content.append((title,self.name+"::"+data2,self.img,title)) 
224
+                else:
225
+                    title = "%s filmas - %s"%(nosaukums[data],sort[s])
226
+                    data2 = "%s/next?sections[]=%s&cat[]=MOVIE&sort[]=%s"%(data,sections[data],s)
227
+                    content.append((title,self.name+"::"+data2,self.img,title)) 
228
+                    title = "%s seriāli - %s"%(nosaukums[data],sort[s])
229
+                    data2 = "%s/next?sections[]=%s&cat[]=SERIES&sort[]=%s"%(data,sections[data],s)
230
+                    content.append((title,self.name+"::"+data2,self.img,title))             
231
+                    
232
+            # Pievienojam žanru sarakstu 
233
+            result = re.findall(r'name="genres\[\]" value="([^"]+)">.+?class="">([^<]+)</label>', r, re.DOTALL)
234
+            for item in result:
235
+                s = "latest"
236
+                genre = item[1].replace("&amp;","&")
237
+                title = "%s: %s"%(nosaukums[data],genre)
238
+                data2 = "%s/next?sections[]=%s&genres[]=%s&sort[]=%s"%(data,sections[data],item[0],s)
239
+                content.append((title,self.name+"::"+data2,self.img,title))                 
240
+                
241
+            return content
242
+        
243
+        ### Filmu/seriālu/sēriju saraksts ###
244
+        elif clist in ("movies","series","kids","documentary") and plist[1] == "next":
245
+            url = "https://viaplay.lv/tdi/"+data
246
+            r = self._http_request(url)
247
+            if clist == "series" and "season" in qs:
248
+                result = re.findall(r'<div id="product-(\w+)".+?<a href="([^"]+)">.+?<img data-srcset="([^ ]+).+?alt="([^"]+)">.+?<h3 class="is-size-h6">([^<]+)</h3>.*?<p>([^<]+).+?</p>.+?<p class="promo-notice">([^<]+)</p>.+?<p class="info">([^<]+)</p>', r, re.DOTALL)
249
+                for item in result:
250
+                    vid = item[0]
251
+                    data2 = item[1].replace("https://viaplay.lv/","")
252
+                    img = item[2]
253
+                    ep = item[3]
254
+                    title= item[4]
255
+                    seas = item[5]
256
+                    desc = item[6]
257
+                    desc2 = item[7]
258
+                    title = "%s - %s%s"%(title,seas,ep)
259
+                    desc = "%s\n%s\n%s"%(title,desc2,desc)
260
+                    content.append((title,self.name+"::"+data2,img,desc))  
261
+            else: # filmas
262
+                result = re.findall(r'<div id="product-(\w+)".+?<a href="([^"]+)">.+?<img data-srcset="([^ ]+).+?alt="([^"]+)">.+?<p>([^<]+)</p>.+?<p class="promo-notice">([^<]+).+?<p class="is-strong detail">(.+?)</p>.+?<p class="info">([^<]+)</p>', r, re.DOTALL)
263
+                for item in result:
264
+                    vid = item[0]
265
+                    data2 = item[1].replace("https://viaplay.lv/","")
266
+                    img = item[2]
267
+                    title = item[3]
268
+                    year = item[4]
269
+                    year  = year.replace("\n","").replace("\t","")
270
+                    title = title +"(%s)"%year
271
+                    desc= item[5]
272
+                    genre = re.findall(">([^<]+)<", item[6], re.DOTALL)
273
+                    genre = ("".join(genre)).replace("&amp;","&")
274
+                    desc2 = item[7]
275
+                    desc = "%s\n%s\n%s"%(genre,desc2,desc)
276
+                    content.append((title,self.name+"::"+data2, img,desc))   
277
+            m = re.search(r"data\('href', 'https://viaplay\.lv/tdi/([^']+)'\)", r, re.DOTALL)
278
+            if m:
279
+                data2 = m.group(1)
280
+                content.append(("Next page",self.name+"::"+data2,img,"Next page"))                 
281
+            return content
282
+        
283
+        ### Seriāls ###
284
+        elif clist == "series" and len(plist)==2:
285
+            url = "https://viaplay.lv/"+data
286
+            r = self._http_request(url)
287
+            result = re.findall(r'<li>.*?<a class="tdi" href="https://viaplay\.lv/([^"]+)" data-related-ancestor="\.js-tdi-items-filter-and-items">([^<]+)</a>.*?</li>', r, re.DOTALL)
288
+            for item in result:
289
+                title = item[1]
290
+                data2 = item[0]
291
+                data2 = data2.replace("series/","series/next/")
292
+                data2 = data2+"&sort[]=ord"
293
+                #series/littlest-pet-shop?season=t6821
294
+                #series/next/peppa-pig?season=t8430
295
+                # &sort[]=ord
296
+                if "availability=" in data2: continue
297
+                content.append((title,self.name+"::"+data2,self.img,title)) #TODO bilde
298
+            return content
299
+                              
300
+    def is_video(self,data):
301
+        source,data,path,plist,clist,params,qs = self.parse_data(data)        
302
+        if clist in ("movies","documentary","kids") and len(plist)>1 and plist[1]<>"next":
303
+            return True
304
+        elif clist == "series"  and len(plist)>1 and plist[1] == "episode":
305
+            return True
306
+        else:
307
+            return False
308
+        
309
+    def get_streams(self, data):
310
+        print "[viaplay] get_streams:", data
311
+        if not self.is_video(data):
312
+            return []
313
+        source,data,path,plist,clist,params,qs = self.parse_data(data)
314
+        if not self.is_logedin():
315
+            self.login()
316
+        if not self.is_logedin():
317
+            raise Exception("Could not login to viaplay.lv, check username/password in options")
318
+        
319
+        streams = []
320
+        url = "https://viaplay.lv/"+data
321
+        r = self._http_request(url)
322
+        if clist=="series":
323
+            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)
324
+            if not m:
325
+                raise Exception("Problem getting video information")
326
+            title = m.group(1).replace("\n"," ").replace("\t","").strip()
327
+            title = re.sub("<[^>]+>","",title).strip()
328
+            desc2 = m.group(2).replace("\n"," ").replace("\t","").strip()
329
+            desc2 = re.sub("<[^>]+>","",desc2).strip()
330
+            desc = m.group(3)
331
+            desc = "%s\n%s"%(desc2,desc)
332
+            vid = re.search('data-productid="(\w+)"',r).group(1)
333
+        else:
334
+            m = re.search(r'<h1 class="is-strong is-bottom-sticked is-size-h2" jnp-id="(\w+)">([^<]+)</h1>.*?<h2 class="is-strong is-size-h4">([^<]+)</h2>.*?<p class="is-size-h6 is-strong is-bottom-sticked">(.+?)<div class="toggler-content">\s+<p>(.+?)</p>', r, re.DOTALL)
335
+            if not m:
336
+                raise Exception("Problem getting video information")
337
+            title = m.group(2).strip()
338
+            title2 = m.group(3).strip()
339
+            title = "%s | %s"%(title,title2)
340
+            desc = m.group(5).strip()
341
+            desc2 = m.group(4).strip()
342
+            desc2 = re.sub("<[^>]+>","",desc2)
343
+            desc2 = desc2.replace("\n"," ").replace("\t","")
344
+            desc = "%s\n%s"%(desc2,desc)
345
+            vid = m.group(1)
346
+        
347
+        js = self.get_video_info(vid)
348
+        #for m in re.finditer(r"lang: '(?P<lang>\w+)',\s+src: '(?P<url>[^']+)',\s+type: '(?P<mime>[^']+)',\s+drm: \[(?P<drm>.+?)\]\s*\}", r, re.DOTALL):               
349
+        if not js:
350
+            return []
351
+        tracks = js["tracks"]
352
+        #if not tracks["HLS"]:
353
+        #    raise Exception("Encrypted DASH playing not yet implemented")
354
+        
355
+        captions = []
356
+        llist = ["fr","en","ru","lv"]
357
+        for st in js["plugins"]["settings"]["subtitles"]:
358
+            sub = {}
359
+            sub["url"] = st["src"]
360
+            sub["lang"] = st["srclang"]
361
+            sub["name"] = st["label"]
362
+            sub["type"] = "vtt"
363
+            sub["order"] = llist.index(sub["lang"])*10 if sub["lang"] in llist else 0            
364
+            captions.append(sub)       
365
+        captions = sorted(captions,key=lambda item: item["order"],reverse=True) 
366
+        
367
+        for s in tracks["HLS"] if tracks["HLS"]  else tracks["DASH"] :
368
+            stype = "DASH" if "dash" in s["type"] else "HLS"
369
+            if "drm" in s: ### 
370
+                # TODO, encrypted stream
371
+                continue
372
+            url = s["src"]
373
+            urlp = util.streamproxy_encode(s["src"])
374
+            stream = util.item()
375
+            stream["url"]=urlp
376
+            stream["lang"]=s["lang"]
377
+            stream["quality"]="variant"
378
+            stream["bitrate"]= "1000000"
379
+            stream["name"]= title
380
+            stream["desc"]=desc
381
+            stream["type"]=stype
382
+            stream["subs"] = captions
383
+            print urlp
384
+            if stype=="DASH": streams.append(stream)
385
+            
386
+            if stype == "HLS": # izvelkam individuālos strimus
387
+                r = requests.get(url)
388
+                result = re.findall("#EXT-X-STREAM-INF:BANDWIDTH=(\d+),RESOLUTION=(\d+x\d+)\n(\w+.m3u8)", r.content)            
389
+                if not result:
390
+                    continue
391
+                for s2 in result:
392
+                    ### TODO vajag lietot cookie ar tokenu no playlista requesta
393
+                    if "set-cookie" in r.headers:
394
+                        headers = {"Cookie":r.headers["set-cookie"]}
395
+                    else:
396
+                        headers={}
397
+                    #url2 = re.sub(r"(http.*://.+/)\w+.m3u8", r"\1"+s2[2], url)
398
+                    url2 = "/".join(url.split("/")[:-1])+"/"+s2[2]
399
+                    #r2 = requests.get(url2,headers=headers)
400
+                    #if "set-cookie" in r2.headers:
401
+                        #headers = {"Cookie":r2.headers["set-cookie"]}
402
+                    #else:
403
+                        #headers={}                   
404
+                    url2p=util.streamproxy_encode(url2,headers)
405
+                    stream = util.item()
406
+                    stream["url"]=url2p
407
+                    stream["lang"]=s["lang"]
408
+                    stream["quality"]="%s"%(s2[1])
409
+                    stream["name"]= title
410
+                    stream["desc"]=desc
411
+                    stream["bitrate"]=s2[0]
412
+                    stream["type"]="DASH" if "dash" in s["type"] else "HLS"
413
+                    streams.append(stream)
414
+                        
415
+        ### TODO - sakārtot sarakstu, lai pirmais ir labakais video
416
+        qlist = ["","512","640","758","1024","variant"]
417
+        llist = ["lt","et","fr","en","ru","lv"]        
418
+        for s in streams:
419
+            lv = llist.index(s["lang"])*10000000 if s["lang"] in llist else 0
420
+            #qv=qlist.index(s["quality"]) if s["quality"] in qlist else 0
421
+            qv = int(s["bitrate"]) if s["bitrate"] else 0
422
+            s["order"] = lv+qv
423
+            #print s["lang"],s["quality"],s["bitrate"],s["order"]
424
+            
425
+        streams = sorted(streams,key=lambda item: item["order"],reverse=True)
426
+        return streams
427
+        
428
+    def call(self, data,params = None, headers=None):
429
+        if not headers: headers = self.headers
430
+        #if not lang: lang = self.country
431
+        url = "https://viaplay.lv/tdi/" + data
432
+        content = self._http_request(url, params, headers)
433
+        return content        
434
+
435
+if __name__ == "__main__":
436
+    if len(sys.argv)>1:
437
+        data= sys.argv[1]
438
+    else:
439
+        data = "kids/child-and-karlson"
440
+    c = Source()
441
+    print "login: %s"%c.login()
442
+    if "/" in data:
443
+        streams = c.get_streams(data)
444
+        util.play_video(streams)
445
+    else:
446
+        vinfo = c.get_video_info(data)
447
+        if "HLS" in vinfo["tracks"] and vinfo["tracks"]["HLS"]:
448
+            url = vinfo["tracks"]["HLS"][0]["src"]
449
+            urlp = util.streamproxy_encode(url)
450
+            util.player(urlp)
451
+        else:
452
+            print "No HLS stream"
453
+    sys.exit()
454
+
455
+    r = requests.get("https://viaplay.lv/movies?sections[]=MOVIES")
456
+    result = re.findall(r'<div id="product-(\w+)".+?<a href="([^"]+)">.+?<img data-srcset="([^ ]+).+?alt="([^"]+)">.+?<p class="promo-notice">([^<]+)<', r.content, re.DOTALL)
457
+    for item in result:
458
+        vid = item[0]
459
+        url = item[1]
460
+        img = item[2]
461
+        title = item[3]
462
+        desc= item[4]
463
+        print "\n%s (%s):"%(title,vid)
464
+        vinfo = c.get_video_info(vid)
465
+        if "HLS" in vinfo["tracks"]:
466
+            for s in vinfo["tracks"]["HLS"]:
467
+                print "HLS %s: \n%s"%(s["lang"],s["src"])
468
+                
469
+        if "DASH" in vinfo["tracks"]:
470
+            for s in vinfo["tracks"]["DASH"]:
471
+                print "DASH %s: \n%s"%(s["lang"],s["src"])
472
+        #except Exception,ex:
473
+            #print ex.message
474
+    #content = c.get_content(data)
475
+    #for item in content:
476
+    #    print item
477
+    pass

+ 291
- 0
streamproxy.py View File

@@ -0,0 +1,291 @@
1
+#!/usr/bin/python
2
+""" 
3
+StreamProxy daemon (based on Livestream daemon)
4
+Ensures persistent cookies, User-Agents and others tricks to play protected HLS/DASH streams
5
+"""
6
+import os
7
+import sys
8
+import time
9
+import atexit
10
+import re
11
+
12
+from signal import SIGTERM
13
+
14
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
15
+from SocketServer import ThreadingMixIn
16
+from urllib import unquote, quote
17
+import urllib,urlparse
18
+#import cookielib,urllib2
19
+import requests
20
+from requests.packages.urllib3.exceptions import InsecureRequestWarning
21
+requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
22
+
23
+HOST_NAME = ""
24
+PORT_NUMBER = 88
25
+DEBUG = True
26
+
27
+headers2dict = lambda  h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
28
+sessions = {}
29
+
30
+class StreamHandler(BaseHTTPRequestHandler):
31
+
32
+    def do_HEAD(self):
33
+        self.send_response(200)
34
+        self.send_header("Server", "StreamProxy")
35
+        self.send_header("Content-type", "text/html")
36
+        self.end_headers()
37
+
38
+    def do_GET(self):
39
+        """Respond to a GET request."""
40
+        SPLIT_CHAR = "~"
41
+        SPLIT_CODE = "%7E"
42
+        EQ_CODE = "%3D"
43
+        COL_CODE = "%3A"
44
+        
45
+        p = self.path.split("~")
46
+        url = urllib.unquote(p[0][1:])
47
+        url = url.replace(COL_CODE, ":")       
48
+        headers = headers2dict("""
49
+        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
50
+        """)
51
+        if len(p)>1:
52
+            for h in p[1:]:
53
+                headers[h.split("=")[0]]=urllib.unquote(h.split("=")[1])
54
+        #self.fetch_offline(self.wfile)
55
+        try:
56
+            self.fetch_url2(self.wfile, url, headers)
57
+        except Exception as e:
58
+            print "Got Exception: ", str(e)
59
+        
60
+    def fetch_offline(self,wfile):
61
+        self.send_response(200)
62
+        self.send_header("Server", "StreamProxy")
63
+        self.send_header("Content-type", "video/mp4")
64
+        self.end_headers()
65
+        self.wfile.write(open("offline.mp4", "rb").read())
66
+        self.wfile.close()
67
+        
68
+    
69
+    def fetch_url2(self, wfile, url, headers):
70
+        if DEBUG: print "\n***********************************************************"
71
+        self.log_message("fetch_url: %s", url)
72
+        #self.log_message("headers: %s", headers)
73
+        
74
+        base_url = "/".join(url.split("/")[0:-1])
75
+        if base_url not in sessions:
76
+            sessions[base_url] = requests.Session()
77
+            #cj = cookielib.CookieJar()
78
+            #sessions[base_url] = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))    
79
+        else:
80
+            pass
81
+        ses=sessions[base_url]
82
+        if DEBUG: print "**Request headers: "
83
+        ses.headers.update(headers)
84
+        #ses.addheaders=[]
85
+        for h in ses.headers:
86
+            #ses.addheaders.append((h,headers[h]))
87
+            if DEBUG: print h,"=",ses.headers[h]
88
+        r = ses.get(url, stream=True,verify=False)
89
+        #r = ses.open(url)
90
+        code = r.status_code #r.status_code
91
+        if DEBUG: print "**Response:", code #r.status_code
92
+        if DEBUG: print "**Response headers: "
93
+        for h in r.headers: 
94
+            if DEBUG: print h,"=",r.headers[h]
95
+        self.send_response(code)
96
+        for h in r.headers:
97
+            if h in ("user-agent","server"):continue
98
+            if h=="connection":
99
+                if DEBUG: print h," skipped"
100
+                continue
101
+            self.send_header(h, r.headers[h])
102
+            if DEBUG:print h,"=",r.headers[h]
103
+        self.end_headers()
104
+        
105
+        CHUNK_SIZE = 4 * 1024
106
+        if code == 200:
107
+            #while True:
108
+                #chunk = r.read(CHUNK_SIZE)
109
+                #if not chunk:
110
+                    #break
111
+                #wfile.write(chunk)
112
+            #pass
113
+            #wfile.close()
114
+            for chunk in r.iter_content(1024):
115
+                try:
116
+                    #print "#",
117
+                    wfile.write(chunk) 
118
+                except Exception as e:
119
+                    print "Exception: ", str(e)
120
+                    return
121
+            if DEBUG: print "  = file downloaded = "
122
+            time.sleep(2)
123
+            #self.wfile.close()
124
+            
125
+            
126
+        else:
127
+            print code
128
+            self.fetch_offline(wfile)
129
+            pass
130
+  
131
+class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
132
+    """Handle requests in a separate thread."""
133
+
134
+def start():
135
+    httpd = ThreadedHTTPServer((HOST_NAME, PORT_NUMBER), StreamHandler)
136
+    print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
137
+    try:
138
+        httpd.serve_forever()
139
+    except KeyboardInterrupt:
140
+        pass
141
+    httpd.server_close()
142
+    print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)
143
+
144
+
145
+class Daemon:
146
+    """
147
+    A generic daemon class.
148
+    Usage: subclass the Daemon class and override the run() method
149
+    """
150
+    def __init__(self, pidfile, stdin="/dev/null", stdout="/dev/null", stderr="/dev/null"):
151
+        self.stdin = stdin
152
+        self.stdout = stdout
153
+        self.stderr = stderr
154
+        self.pidfile = pidfile
155
+
156
+    def daemonize(self):
157
+        """
158
+        do the UNIX double-fork magic, see Stevens' "Advanced 
159
+        Programming in the UNIX Environment" for details (ISBN 0201563177)
160
+        http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
161
+        """
162
+        try: 
163
+            pid = os.fork() 
164
+            if pid > 0:
165
+                # exit first parent
166
+                sys.exit(0) 
167
+        except OSError, e: 
168
+            sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
169
+            sys.exit(1)
170
+
171
+        # decouple from parent environment
172
+        os.chdir("/") 
173
+        os.setsid() 
174
+        os.umask(0) 
175
+
176
+        # do second fork
177
+        try: 
178
+            pid = os.fork() 
179
+            if pid > 0:
180
+                # exit from second parent
181
+                sys.exit(0) 
182
+        except OSError, e: 
183
+            sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
184
+            sys.exit(1) 
185
+
186
+        # redirect standard file descriptors
187
+        sys.stdout.flush()
188
+        sys.stderr.flush()
189
+        si = file(self.stdin, "r")
190
+        so = file(self.stdout, "a+")
191
+        se = file(self.stderr, "a+", 0)
192
+        os.dup2(si.fileno(), sys.stdin.fileno())
193
+        os.dup2(so.fileno(), sys.stdout.fileno())
194
+        os.dup2(se.fileno(), sys.stderr.fileno())
195
+
196
+        # write pidfile
197
+        atexit.register(self.delpid)
198
+        pid = str(os.getpid())
199
+        file(self.pidfile,"w+").write("%s\n" % pid)
200
+
201
+    def delpid(self):
202
+        os.remove(self.pidfile)
203
+
204
+    def start(self):
205
+        """
206
+        Start the daemon
207
+        """
208
+        # Check for a pidfile to see if the daemon already runs
209
+        try:
210
+            pf = file(self.pidfile,"r")
211
+            pid = int(pf.read().strip())
212
+            pf.close()
213
+        except IOError:
214
+            pid = None
215
+
216
+        if pid:
217
+            message = "pidfile %s already exist. Daemon already running?\n"
218
+            sys.stderr.write(message % self.pidfile)
219
+            sys.exit(1)
220
+
221
+        # Start the daemon
222
+        self.daemonize()
223
+        self.run()
224
+
225
+    def stop(self):
226
+        """
227
+        Stop the daemon
228
+        """
229
+        # Get the pid from the pidfile
230
+        try:
231
+            pf = file(self.pidfile,"r")
232
+            pid = int(pf.read().strip())
233
+            pf.close()
234
+        except IOError:
235
+            pid = None
236
+
237
+        if not pid:
238
+            message = "pidfile %s does not exist. Daemon not running?\n"
239
+            sys.stderr.write(message % self.pidfile)
240
+            return # not an error in a restart
241
+
242
+        # Try killing the daemon process	
243
+        try:
244
+            while 1:
245
+                os.kill(pid, SIGTERM)
246
+                time.sleep(0.1)
247
+        except OSError, err:
248
+            err = str(err)
249
+            if err.find("No such process") > 0:
250
+                if os.path.exists(self.pidfile):
251
+                    os.remove(self.pidfile)
252
+            else:
253
+                print str(err)
254
+                sys.exit(1)
255
+
256
+    def restart(self):
257
+        """
258
+        Restart the daemon
259
+        """
260
+        self.stop()
261
+        self.start()
262
+
263
+    def run(self):
264
+        """
265
+        You should override this method when you subclass Daemon. It will be called after the process has been
266
+        daemonized by start() or restart().
267
+        """
268
+
269
+class ProxyDaemon(Daemon):
270
+    def run(self):
271
+        start()
272
+
273
+if __name__ == "__main__":
274
+    daemon = ProxyDaemon("/var/run/streamproxy.pid")
275
+    if len(sys.argv) == 2:
276
+        if "start" == sys.argv[1]:
277
+            daemon.start()
278
+        elif "stop" == sys.argv[1]:
279
+            daemon.stop()
280
+        elif "restart" == sys.argv[1]:
281
+            daemon.restart()
282
+        elif "manualstart" == sys.argv[1]:
283
+            start()
284
+        else:
285
+            print "Unknown command"
286
+            sys.exit(2)
287
+        sys.exit(0)
288
+    else:
289
+        print "usage: %s start|stop|restart|manualstart" % sys.argv[0]
290
+        sys.exit(2)
291
+

+ 552
- 0
util.py View File

@@ -0,0 +1,552 @@
1
+# -*- coding: UTF-8 -*-
2
+# /*
3
+# *      Copyright (C) 2011 Libor Zoubek,ivars777
4
+# *
5
+# *
6
+# *  This Program is free software; you can redistribute it and/or modify
7
+# *  it under the terms of the GNU General Public License as published by
8
+# *  the Free Software Foundation; either version 2, or (at your option)
9
+# *  any later version.
10
+# *
11
+# *  This Program is distributed in the hope that it will be useful,
12
+# *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+# *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+# *  GNU General Public License for more details.
15
+# *
16
+# *  You should have received a copy of the GNU General Public License
17
+# *  along with this program; see the file COPYING.  If not, write to
18
+# *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
+# *  http://www.gnu.org/copyleft/gpl.html
20
+# *
21
+# */
22
+import os
23
+import re
24
+import sys
25
+import urllib
26
+import urllib2
27
+import traceback
28
+import cookielib
29
+import requests
30
+from htmlentitydefs import name2codepoint as n2cp
31
+import HTMLParser
32
+import StringIO
33
+
34
+#import threading
35
+#import Queue
36
+import pickle
37
+import string
38
+import simplejson as json
39
+#from demjson import demjson
40
+#import demjson
41
+import json
42
+#from bs4 import BeautifulSoup
43
+
44
+UA = 'Mozilla/6.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.5) Gecko/2008092417 Firefox/3.0.3'
45
+LOG = 2
46
+
47
+_cookie_jar = None
48
+CACHE_COOKIES = 'cookies'
49
+
50
+def play_video(streams):
51
+    if len(streams)>1:
52
+        for i,s in enumerate(streams):
53
+            
54
+            print "%s: [%s,%s,%s] %s"%(i,s["quality"],s["lang"].encode("utf8"),s["type"],s["name"])
55
+        a = raw_input("Select stram to play: ")
56
+        try:
57
+            n = int(a)
58
+        except:
59
+            n = 0
60
+        if n>=len(streams):
61
+            stream = streams[-1]
62
+        else:
63
+            stream = streams[n]
64
+    else:
65
+        stream = streams[0]
66
+
67
+    title = stream["name"]
68
+    url = stream["url"]
69
+    suburl = ""
70
+    print url
71
+    if "subs" in stream and stream["subs"]:
72
+        suburl = stream["subs"][0]["url"]
73
+        print "\n**Download subtitles %s - %s"%(title,suburl)  
74
+        subs = urllib2.urlopen(suburl).read()
75
+        if subs:
76
+            fname0 = re.sub("[/\n\r\t,]","_",title)                
77
+            subext = ".srt"
78
+            subfile = os.path.join("",fname0+subext)
79
+            if ".xml" in suburl:
80
+                subs = ttaf2srt(subs) 
81
+            with open(subfile,"w") as f:
82
+                f.write(subs)
83
+        else:
84
+            print "\n Error downloading subtitle %s"%suburl
85
+    return player(url,stream["name"],suburl)
86
+            
87
+def player(url,title="",suburl=""):
88
+    from subprocess import call
89
+    print "\n**Play stream %s\n%s"%(title,url.encode("utf8"))
90
+    cmd1 = [r"c:\Program Files\VideoLAN\VLC\vlc.exe",url,
91
+           "--meta-title",title.decode("utf8").encode(sys.getfilesystemencoding()),
92
+           "--http-user-agent","Enigma2"
93
+    ]
94
+    # gst-launch-1.0 -v souphttpsrc ssl-strict=false proxy=127.0.0.1:8888 extra-headers="Origin:adadadasd"  location="http://bitdash-a.akamaihd.net/content/sintel/sintel.mpd" ! decodebin! autovideosink
95
+    cmd2 = [
96
+        r"C:\gstreamer\1.0\x86_64\bin\gst-launch-1.0","-v",
97
+        "playbin", 'uri="%s"'%url,
98
+        #"souphttpsrc", "ssl-strict=false",       
99
+        #"proxy=127.0.0.1:8888",
100
+        #'location="%s"'%url,
101
+        #'!decodebin!autovideosink'
102
+    ]
103
+    cmd = cmd1 if url.startswith("https") else cmd2
104
+    ret = call(cmd)
105
+    #if ret:
106
+        #a = raw_input("*** Error, continue")
107
+    return
108
+
109
+SPLIT_CHAR = "~"
110
+SPLIT_CODE = "%7E"
111
+EQ_CODE = "%3D"
112
+COL_CODE = "%3A"
113
+PROXY_URL = "http://localhost:88/"
114
+
115
+def streamproxy_encode(url,headers=[]):
116
+    if not "?" in url:
117
+        url = url+"?"
118
+    url2 = url.replace(SPLIT_CHAR,SPLIT_CODE).replace(":",COL_CODE)
119
+    url2 = PROXY_URL + url2
120
+    if headers:
121
+        headers2 = []
122
+        for h in headers:
123
+            headers2.append("%s=%s"%(h,headers[h].replace("=",EQ_CODE).replace(SPLIT_CHAR,SPLIT_CODE)))
124
+        headers2 = SPLIT_CHAR.join(headers2)
125
+        url2 = url2+SPLIT_CHAR+headers2
126
+    return url2
127
+
128
+def streamproxy_decode(urlp):
129
+    import urlparse
130
+    path = urlp.replace(re.search("http://[^/]+",urlp).group(0),"")
131
+    p = path.split(SPLIT_CHAR)
132
+    url = urllib.unquote(p[0][1:])
133
+    #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"}    
134
+    headers={}
135
+    if len(p)>1:
136
+        for h in p[1:]:
137
+            headers[h.split("=")[0]]=urllib.unquote(h.split("=")[1])
138
+    return url,headers
139
+
140
+class Captions(object):
141
+    def __init__(self,uri):
142
+        self.subs = []
143
+        self.styles = {}
144
+        if uri.startswith("http"):
145
+            r = requests.get(uri)
146
+        if r.status_code == 200:
147
+            self.loads(r.content)
148
+            
149
+    def loads(self,s):
150
+        if "WEBVTT" in s[:s.find("\n")]: # vtt captions
151
+            self.load_vtt(s)
152
+        elif "<?xml" in s[:s.find("\n")]:
153
+            self.load_ttaf(s)
154
+        else:
155
+            return
156
+        
157
+    def load_ttaf(self,s):
158
+        for r2 in re.findall("<style .+?/>", s):
159
+            st = {}
160
+            for a in re.findall(r'(\w+)="([^ "]+)"', r2):
161
+                st[a[0]] = a[1]
162
+                if a[0] == "id":
163
+                    sid = a[1]
164
+            self.styles[sid] = st  
165
+        for r2 in re.findall("<p .+?</p>", s):
166
+            sub = {}
167
+            sub["begin"] = str2sec(re.search('begin="([^"]+)"', r2).group(1)) if re.search('begin="([^"]+)"', r2) else -1
168
+            sub["end"] = str2sec(re.search('end="([^"]+)"', r2).group(1)) if re.search('end="([^"]+)"', r2) else -1
169
+            sub["style"] = re.search('style="([^"]+)"', r2).group(1) if re.search('style="([^"]+)"', r2) else None
170
+            sub["text"] = re.search("<p[^>]+>(.+)</p>", r2).group(1).replace("\n","")
171
+            sub["text"] = re.sub("<br\s*?/>","\n",sub["text"])
172
+            sub["text"] = re.sub("<.+?>"," ",sub["text"])
173
+            self.subs.append(sub)  
174
+        pass
175
+                  
176
+    def load_vtt(self,s):
177
+        f = StringIO.StringIO(s)
178
+        while True:
179
+            line = f.readline()
180
+            if not line:
181
+                break
182
+            m = re.search(r"([\d\.\,:]+)\s*-->\s*([\d\.\,\:]+)",line)
183
+            if m:
184
+                sub = {}
185
+                sub["begin"] = str2sec(m.group(1))
186
+                sub["end"] = str2sec(m.group(2))
187
+                sub["style"] = None
188
+                sub["text"] = []
189
+                line = f.readline()
190
+                while line.strip():
191
+                    txt = line.strip()
192
+                    if isinstance(txt,unicode):
193
+                        txt = txt.encode("utf8")
194
+                    sub["text"].append(txt)
195
+                    line = f.readline()
196
+                sub["text"] = "\n".join(sub["text"])
197
+                self.subs.append(sub)                    
198
+            else:
199
+                continue
200
+        pass
201
+            
202
+def str2sec(r):
203
+    # Convert str time to miliseconds
204
+    r= r.replace(",",".")
205
+    m = re.search(r"(\d+\:)*(\d+)\:(\d+\.\d+)", r)
206
+    if m:
207
+        sec = int(m.group(1)[:-1])*60*60*1000 if m.group(1) else 0 
208
+        sec += int(m.group(2))*60*1000 + int(float(m.group(3))*1000)
209
+        return sec
210
+    else:
211
+        return -1
212
+
213
+#c = Captions("http://195.13.216.2/mobile-vod/mp4:lb_barbecue_fr_lq.mp4/lb_barbecue_lv.vtt")  
214
+#c = Captions("http://www.bbc.co.uk/iplayer/subtitles/ng/modav/bUnknown-0edd6227-0f38-411c-8d46-fa033c4c61c1_b05ql1s3_1479853893356.xml")
215
+#url = "http://195.13.216.2/mobile-vod/mp4:ac_now_you_see_me_2_en_lq.mp4/ac_now_you_see_me_2_lv.vtt"
216
+#c = Captions(url)
217
+
218
+#pass
219
+
220
+
221
+def ttaf2srt(s):
222
+    out = u""
223
+    i = 0
224
+    for p,txt in re.findall("<p ([^>]+)>(.+?)</p>", s, re.DOTALL):
225
+        i +=1
226
+        begin = re.search('begin="(.+?)"',p).group(1)
227
+        begin = begin.replace(".",",")
228
+        end = re.search('end="(.+?)"',p).group(1)
229
+        end = end.replace(".",",")
230
+        txt2 = re.sub("<br */>","\n",txt)
231
+        out += "%s\n%s --> %s\n%s\n\n"%(i,begin,end,txt2)
232
+    return out    
233
+
234
+
235
+def item():
236
+    stream0 = {'name': '', 'url': '', 'quality': '???', 'surl': '', 'subs': '', 'headers': {},"desc":"","img":"","lang":"","type":"","order":0}            
237
+    return stream0
238
+
239
+class _StringCookieJar(cookielib.LWPCookieJar):
240
+
241
+    def __init__(self, string=None, filename=None, delayload=False, policy=None):
242
+        cookielib.LWPCookieJar.__init__(self, filename, delayload, policy)
243
+        if string and len(string) > 0:
244
+            self._cookies = pickle.loads(str(string))
245
+
246
+    def dump(self):
247
+        return pickle.dumps(self._cookies)
248
+
249
+
250
+def init_urllib(cache=None):
251
+    """
252
+    Initializes urllib cookie handler
253
+    """
254
+    global _cookie_jar
255
+    data = None
256
+    if cache is not None:
257
+        data = cache.get(CACHE_COOKIES)
258
+    _cookie_jar = _StringCookieJar(data)
259
+    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(_cookie_jar))
260
+    urllib2.install_opener(opener)
261
+
262
+
263
+def cache_cookies(cache):
264
+    """
265
+    Saves cookies to cache
266
+    """
267
+    global _cookie_jar
268
+    if _cookie_jar:
269
+        cache.set(CACHE_COOKIES, _cookie_jar.dump())
270
+
271
+
272
+def request0(url, headers={}):
273
+    debug('request: %s' % url)
274
+    req = urllib2.Request(url, headers=headers)
275
+    req.add_header('User-Agent', UA)
276
+    try:
277
+        response = urllib2.urlopen(req)
278
+        data = response.read()
279
+        response.close()
280
+    except urllib2.HTTPError, error:
281
+        data = error.read()    
282
+
283
+    debug('len(data) %s' % len(data))
284
+    return data
285
+
286
+def request(url, headers={}):
287
+    debug('request: %s' % url)
288
+    #req = urllib2.Request(url, headers=headers)
289
+    #req.add_header('User-Agent', UA)
290
+    if 'User-Agent' not in headers:
291
+        headers['User-Agent']= UA
292
+    try:
293
+        r = requests.get(url, headers=headers)
294
+        data = r.content
295
+    except:
296
+        data = r.content   
297
+
298
+    debug('len(data) %s' % len(data))
299
+    return data
300
+
301
+def post(url, data, headers={}):
302
+    postdata = urllib.urlencode(data)
303
+    #req = urllib2.Request(url, postdata, headers)
304
+    #req.add_header('User-Agent', UA)
305
+    import requests
306
+    if 'User-Agent' not in headers:
307
+        headers['User-Agent']= UA    
308
+    try:
309
+        r = requests.post(url, data=postdata,headers=headers)
310
+        data = r.content
311
+    except urllib2.HTTPError, error:
312
+        data = r.content   
313
+    return data
314
+
315
+def post0(url, data, headers={}):
316
+    postdata = urllib.urlencode(data)
317
+    req = urllib2.Request(url, postdata, headers)
318
+    req.add_header('User-Agent', UA)
319
+    try:
320
+        response = urllib2.urlopen(req)
321
+        data = response.read()
322
+        response.close()
323
+    except urllib2.HTTPError, error:
324
+        data = error.read()    
325
+    return data
326
+
327
+
328
+def post_json(url, data, headers={}):
329
+    postdata = json.dumps(data)
330
+    headers['Content-Type'] = 'application/json'
331
+    req = urllib2.Request(url, postdata, headers)
332
+    req.add_header('User-Agent', UA)
333
+    response = urllib2.urlopen(req)
334
+    data = response.read()
335
+    response.close()
336
+    return data
337
+
338
+
339
+#def run_parallel_in_threads(target, args_list):
340
+    #result = Queue.Queue()
341
+    ## wrapper to collect return value in a Queue
342
+
343
+    #def task_wrapper(*args):
344
+        #result.put(target(*args))
345
+
346
+    #threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list]
347
+    #for t in threads:
348
+        #t.start()
349
+    #for t in threads:
350
+        #t.join()
351
+    #return result
352
+
353
+
354
+def substr(data, start, end):
355
+    i1 = data.find(start)
356
+    i2 = data.find(end, i1)
357
+    return data[i1:i2]
358
+
359
+
360
+def save_to_file(url, file):
361
+    try:
362
+        return save_data_to_file(request(url), file)
363
+    except:
364
+        traceback.print_exc()
365
+
366
+
367
+def save_data_to_file(data, file):
368
+    try:
369
+        f = open(file, 'wb')
370
+        f.write(data)
371
+        f.close()
372
+        info('File %s saved' % file)
373
+        return True
374
+    except:
375
+        traceback.print_exc()
376
+
377
+
378
+def read_file(file):
379
+    if not os.path.exists(file):
380
+        return ''
381
+    f = open(file, 'r')
382
+    data = f.read()
383
+    f.close()
384
+    return data
385
+
386
+
387
+def _substitute_entity(match):
388
+    ent = match.group(3)
389
+    if match.group(1) == '#':
390
+        # decoding by number
391
+        if match.group(2) == '':
392
+            # number is in decimal
393
+            return unichr(int(ent))
394
+        elif match.group(2) == 'x':
395
+            # number is in hex
396
+            return unichr(int('0x' + ent, 16))
397
+    else:
398
+        # they were using a name
399
+        cp = n2cp.get(ent)
400
+        if cp:
401
+            return unichr(cp)
402
+        else:
403
+            return match.group()
404
+
405
+
406
+def decode_html(data):
407
+    if not type(data) == str:
408
+        return data
409
+    try:
410
+        if not type(data) == unicode:
411
+            data = unicode(data, 'utf-8', errors='ignore')
412
+        entity_re = re.compile(r'&(#?)(x?)(\w+);')
413
+        return entity_re.subn(_substitute_entity, data)[0]
414
+    except:
415
+        traceback.print_exc()
416
+        print[data]
417
+        return data
418
+
419
+def unescape(s0):
420
+    #s2 = re.sub("&#\w+;",HTMLParser.HTMLParser().unescape("\1"),s)
421
+    s0 = s0.replace("&amp;","&")
422
+    for s in re.findall("&#\w+;",s0):
423
+        s2 = HTMLParser.HTMLParser().unescape(s)
424
+        if isinstance(s0,str):
425
+            s2 = s2.encode("utf8")
426
+        s0 = s0.replace(s,s2)
427
+        pass
428
+    return s0
429
+
430
+def debug(text):
431
+    if LOG > 1:
432
+        print('[DEBUG] ' + str([text]))
433
+
434
+def info(text):
435
+    if LOG > 0:
436
+        print('[INFO] ' + str([text]))
437
+
438
+def error(text):
439
+    print('[ERROR] ' + str([text]))
440
+
441
+_diacritic_replace = {u'\u00f3': 'o',
442
+                      u'\u0213': '-',
443
+                      u'\u00e1': 'a',
444
+                      u'\u010d': 'c',
445
+                      u'\u010c': 'C',
446
+                      u'\u010f': 'd',
447
+                      u'\u010e': 'D',
448
+                      u'\u00e9': 'e',
449
+                      u'\u011b': 'e',
450
+                      u'\u00ed': 'i',
451
+                      u'\u0148': 'n',
452
+                      u'\u0159': 'r',
453
+                      u'\u0161': 's',
454
+                      u'\u0165': 't',
455
+                      u'\u016f': 'u',
456
+                      u'\u00fd': 'y',
457
+                      u'\u017e': 'z',
458
+                      u'\xed': 'i',
459
+                      u'\xe9': 'e',
460
+                      u'\xe1': 'a',
461
+                      }
462
+
463
+
464
+def replace_diacritic(string):
465
+    ret = []
466
+    for char in string:
467
+        if char in _diacritic_replace:
468
+            ret.append(_diacritic_replace[char])
469
+        else:
470
+            ret.append(char)
471
+    return ''.join(ret)
472
+
473
+
474
+def params(url=None):
475
+    if not url:
476
+        url = sys.argv[2]
477
+    param = {}
478
+    paramstring = url
479
+    if len(paramstring) >= 2:
480
+        params = url
481
+        cleanedparams = params.replace('?', '')
482
+        if (params[len(params) - 1] == '/'):
483
+            params = params[0:len(params) - 2]
484
+        pairsofparams = cleanedparams.split('&')
485
+        param = {}
486
+        for i in range(len(pairsofparams)):
487
+            splitparams = {}
488
+            splitparams = pairsofparams[i].split('=')
489
+            if (len(splitparams)) == 2:
490
+                param[splitparams[0]] = splitparams[1]
491
+    for p in param.keys():
492
+        param[p] = param[p].decode('hex')
493
+    return param
494
+
495
+
496
+def int_to_base(number, base):
497
+    digs = string.digits + string.letters
498
+    if number < 0:
499
+        sign = -1
500
+    elif number == 0:
501
+        return digs[0]
502
+    else:
503
+        sign = 1
504
+    number *= sign
505
+    digits = []
506
+    while number:
507
+        digits.append(digs[number % base])
508
+        number /= base
509
+    if sign < 0:
510
+        digits.append('-')
511
+    digits.reverse()
512
+    return ''.join(digits)
513
+
514
+
515
+def extract_jwplayer_setup(data):
516
+    """
517
+    Extracts jwplayer setup configuration and returns it as a dictionary.
518
+
519
+    :param data: A string to extract the setup from
520
+    :return: A dictionary containing the setup configuration
521
+    """
522
+    data = re.search(r'<script.+?}\(\'(.+)\',\d+,\d+,\'([\w\|]+)\'.*</script>', data, re.I | re.S)
523
+    if data:
524
+        replacements = data.group(2).split('|')
525
+        data = data.group(1)
526
+        for i in reversed(range(len(replacements))):
527
+            if len(replacements[i]) > 0:
528
+                data = re.sub(r'\b%s\b' % int_to_base(i, 36), replacements[i], data)
529
+        data = re.search(r'\.setup\(([^\)]+?)\);', data)
530
+        if data:
531
+            return json.loads(data.group(1).decode('string_escape'))
532
+        #return demjson.decode(data.group(1).decode('string_escape')) ### III
533
+    return None
534
+
535
+
536
+#def parse_html(url):
537
+#    return BeautifulSoup(request(url), 'html5lib', from_encoding='utf-8')
538
+
539
+if __name__ == "__main__":
540
+    s = 'B\xc4\x93thovena D\xc4\x81rgumu Taka (2014)/Beethoven&#x27;s Treasure [LV]'
541
+    #s = s.decode("utf8")
542
+    #s=unescape(s)
543
+    #url = "http://localhost:88/https://walterebert.com/playground/video/hls/ts/480x270.m3u8?token=xxxx~User-Agent=Enigma2~Cookie=xxxxx"
544
+    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"
545
+    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"}
546
+    urlp = streamproxy_encode(url,headers)
547
+    print urlp
548
+    player(urlp)
549
+    
550
+    pass
551
+    
552
+