Python module (submodule repositary), which provides content (video streams) from various online stream sources to corresponding Enigma2, Kodi, Plex plugins

run.py 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. #!/usr/bin/env python
  2. # coding=utf8
  3. import sys, os, traceback
  4. from Tkinter import *
  5. try:
  6. from ttk import *
  7. except:
  8. pass
  9. import tkMessageBox as tkm
  10. import tkSimpleDialog as tkd
  11. import PIL, StringIO
  12. from PIL import ImageTk, Image
  13. import requests, urllib2
  14. from ContentSources import ContentSources
  15. from sources.SourceBase import stream_type
  16. import util
  17. class Main(Frame):
  18. def __init__(self, sources):
  19. self.root = Tk()
  20. self.root.geometry("1050x600")
  21. Frame.__init__(self, self.root)
  22. img = PhotoImage(file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon.gif'))
  23. #img = PhotoImage(file= 'icon.gif')
  24. self.root.tk.call('wm', 'iconphoto', self.root._w, img)
  25. self.pack(fill=BOTH, expand=1)
  26. self.initUI()
  27. items = ["item %s" % i for i in range(20)]
  28. if sources:
  29. self.sources = sources
  30. else:
  31. self.sources = ContentSources("sources")
  32. self.picons_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "picons")
  33. self.tmp_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "tmp")
  34. if not os.path.exists(self.tmp_path):
  35. os.mkdir(self.tmp_path)
  36. self.history = []
  37. self.cur_index = 0
  38. self.cur = ("Home", "config::home", "", "PlayStream home")
  39. def initUI(self):
  40. self.master.title("PlayStream")
  41. self.txt1 = Label(self)
  42. self.txt1.pack(side=TOP, fill=X, padx=15)
  43. self.txt1.configure(font=("Tahoma", 14, "bold"))
  44. self.txt1.config(text="")
  45. self.txt2 = Label(self)
  46. self.txt2.pack(side=TOP, fill=X, padx=15)
  47. self.txt2.config(text="")
  48. self.txt2.configure(font=("Tahoma", 12))
  49. frame1 = Frame(self)
  50. frame1.pack(side=TOP, fill=X, padx=15)
  51. scrollbar = Scrollbar(frame1, orient=VERTICAL)
  52. self.listbox = Listbox(frame1, width=80, height=30, yscrollcommand=scrollbar.set)
  53. scrollbar.config(command=self.listbox.yview)
  54. #self.listbox.config(yscrollcommand=scrollbar.set)
  55. self.listbox.pack(side=LEFT)
  56. scrollbar.pack(side=LEFT, fill=Y)
  57. self.listbox.focus_set()
  58. self.listbox.bind('<<ListboxSelect>>', lambda evt: self.list_action(evt, "Changed"))
  59. self.listbox.bind('<Double-1> ', lambda evt:self.list_action(None, "Return"))
  60. self.listbox.bind('<Key> ', self.list_action)
  61. self.pic = Canvas(frame1, width=400, height=250)
  62. self.pic.pack(side=TOP, expand=1)
  63. frame2 = Frame(frame1, height=250, width=400)
  64. frame2.pack(side=TOP)
  65. scrollbar2 = Scrollbar(frame2)
  66. self.desc = Text(frame2, height=15, width=70, wrap="word", yscrollcommand=scrollbar2.set) #, borderwidth=0, highlightthickness=0)
  67. scrollbar2.config(command=self.desc.yview)
  68. scrollbar2.pack(side="right", fill="y")
  69. self.desc.configure(font=("Tahoma", 10))
  70. self.desc.pack(anchor=N)
  71. frame3 = Frame(self, height=40)
  72. frame3.pack(side=TOP, fill=X, padx=15)
  73. self.b_select = Button(frame3, text="Select",
  74. command=lambda :self.list_action(None, "Return"), width=10)
  75. self.b_select.pack(side=LEFT, anchor=S)
  76. self.b_back = Button(frame3, text="Back",
  77. command=lambda :self.list_action(None, "Escape"), width=10)
  78. self.b_back.pack(side=LEFT, anchor=S)
  79. self.b_options = Button(frame3, text="Options",
  80. command=lambda :self.list_action(None, "Options"), width=10)
  81. self.b_options.pack(side=LEFT, anchor=S)
  82. self.b_config = Button(frame3, text="Config",
  83. command=lambda :self.list_action(None, "Config"), width=10)
  84. self.b_config.pack(side=LEFT, anchor=S)
  85. self.b_exit = Button(frame3, text="Exit",
  86. command=self.quit, width=10)
  87. self.b_exit.pack(side=LEFT, anchor=S)
  88. def list_action(self, evt=None, key=None):
  89. cs = int(self.listbox.curselection()[0])
  90. self.cur_index = cs
  91. if (not key) and evt:
  92. w = evt.widget
  93. value = w.get(cs)
  94. key = evt.keysym
  95. #print 'Key: %s' % (key)
  96. data = self.content[cs][1]
  97. cur2 = self.content[cs]
  98. if key == "Changed":
  99. self.show_desc()
  100. self.show_pic()
  101. elif key == "Return" and data <> "back":
  102. if not self.sources.is_video(data):
  103. if "{0}" in data:
  104. a = tkd.askstring("Search", "Search for")
  105. cur2 = (cur2[0],cur2[1].format(a),cur2[2],cur2[3])
  106. self.history.append((self.cur, cs))
  107. self.cur = self.content[cs]
  108. self.cur_index = 0
  109. try:
  110. self.show_content(cur2)
  111. except Exception as e:
  112. print unicode(e)
  113. traceback.print_exc()
  114. tkm.showerror("Error getting content", unicode(e))
  115. prev = self.history.pop()
  116. self.cur = prev[0]
  117. self.cur_index = prev[1]
  118. self.show_content(self.cur, self.cur_index)
  119. return
  120. else:
  121. self.play_video(self.content[cs])
  122. self.listbox.selection_set(self.cur_index)
  123. self.listbox.activate(self.cur_index)
  124. self.listbox.focus_set()
  125. elif key == "BackSpace" or key == "Escape" or (key == "Return" and data == "back"):
  126. if self.history:
  127. prev = self.history.pop()
  128. self.cur = prev[0]
  129. self.cur_index = prev[1]
  130. self.show_content(self.cur, self.cur_index)
  131. elif key == "Right":
  132. index2 = self.cur_index + 20
  133. index2 = min(index2, self.listbox.index(END) - 1)
  134. self.set_list_item(index2)
  135. elif key == "Left":
  136. index2 = self.cur_index - 20
  137. index2 = max(index2, 0)
  138. self.set_list_item(index2)
  139. elif key in ("i", "I"):
  140. print "TODO - indo"
  141. def set_list_item(self, index):
  142. self.cur_index = index
  143. self.listbox.selection_clear(0, END)
  144. self.listbox.selection_set(index)
  145. self.listbox.activate(index)
  146. self.listbox.focus_set()
  147. self.update()
  148. def show_content(self, cur2, cur_index=0):
  149. data = cur2[1]
  150. self.txt1.config(text=cur2[0])
  151. self.content = self.sources.get_content(data)
  152. self.listbox.delete(0, END)
  153. for item in self.content:
  154. self.listbox.insert(END, item[0])
  155. self.listbox.selection_set(cur_index)
  156. self.listbox.activate(cur_index)
  157. self.listbox.focus_set()
  158. self.show_desc()
  159. self.show_pic()
  160. def show_desc(self):
  161. cs = self.listbox.curselection()[0]
  162. self.txt2.config(text=self.content[cs][0])
  163. self.desc.config(state=NORMAL)
  164. self.desc.delete("1.0", END)
  165. self.desc.insert(END, self.content[cs][3])
  166. self.desc.insert(END, "\n\n"+self.content[cs][1])
  167. self.desc.insert(END, "\n"+self.content[cs][2])
  168. self.desc.config(state=DISABLED)
  169. def show_pic(self):
  170. cs = self.listbox.curselection()[0]
  171. img = self.content[cs][2]
  172. self.pic.delete(ALL)
  173. if not img:
  174. return
  175. if not img.startswith("http"):
  176. img_path = os.path.join(self.picons_path, img)
  177. if img and os.path.exists(img_path):
  178. im = Image.open(img_path)
  179. else:
  180. im = None
  181. print "No image found ", img_path
  182. elif img:
  183. fcache = img.replace(":", "_").replace("/", "-").replace(":", "_")
  184. fcache = os.path.join(self.tmp_path, fcache)
  185. if os.path.exists(fcache):
  186. im = Image.open(fcache)
  187. else:
  188. r = requests.get(img)
  189. if r.status_code == 200:
  190. img_data = r.content
  191. im = Image.open(StringIO.StringIO(r.content))
  192. with open(fcache, "wb") as f:
  193. f.write(r.content)
  194. else:
  195. im = None
  196. if im:
  197. im.thumbnail((400, 250))
  198. image = ImageTk.PhotoImage(im)
  199. imagesprite = self.pic.create_image(200, 125,image=image)
  200. self.pic.image = image
  201. def play_video(self, cur2):
  202. if self.sources.stream_type(cur2[1]):
  203. stream = util.item()
  204. stream["url"] = cur2[1]
  205. stream["name"] = cur2[0]
  206. streams = [stream]
  207. else:
  208. try:
  209. streams = self.sources.get_streams(cur2[1])
  210. except Exception as e:
  211. print unicode(e)
  212. traceback.print_exc()
  213. tkm.showerror("Error getting streams", unicode(e))
  214. return
  215. if not streams:
  216. tkm.showerror("Error getting streams", "No streams found")
  217. return
  218. if len(streams) > 1:
  219. lst = []
  220. for i,s in enumerate(streams):
  221. s = {k:v.decode("utf8") if v and isinstance(v, str) else v for k,v in zip(s.keys(), s.values())}
  222. lst.append(("[%s,%s] %s"%(s["lang"],s["quality"],s["name"]),i))
  223. a = ChoiceBox("ChoiceBox test", "Select stream", lst, parent=self, width=40).result
  224. stream = streams[a]
  225. else:
  226. stream = streams[0]
  227. stream = util.stream_change(stream)
  228. title = stream["name"] if not "nfo" in stream or not stream["nfo"] else util.nfo2title(stream["nfo"])
  229. desc = stream["desc"] if not "nfo" in stream or not stream["nfo"] else util.nfo2desc(stream["nfo"])
  230. img = stream["img"]
  231. url = stream["url"]
  232. suburl = ""
  233. print url
  234. if "subs" in stream and stream["subs"]:
  235. suburl = stream["subs"][0]["url"]
  236. print "\n**Download subtitles %s - %s"%(title,suburl)
  237. subs = urllib2.urlopen(suburl).read()
  238. if subs:
  239. fname0 = re.sub("[/\n\r\t,:\?]","_",title)
  240. subext = ".srt"
  241. subfile = os.path.join(self.tmp_path,fname0+subext)
  242. if ".xml" in suburl:
  243. subs = util.ttaf2srt(subs)
  244. with open(subfile,"w") as f:
  245. f.write(subs)
  246. else:
  247. print "\n Error downloading subtitle %s"%suburl
  248. print "\n**Play stream %s\n%s" % (title, url.encode("utf8"))
  249. player(url,title,suburl,stream["headers"])
  250. def start(self):
  251. self.root.mainloop()
  252. class ChoiceBox(tkd.Dialog):
  253. #a = ChoiceBox("ChoiceBox test", "Select stream", items, parent=self).result
  254. def __init__(self, title, prompt, items, width=20, height=15,
  255. initialvalue=0, parent = None):
  256. if not parent:
  257. import Tkinter
  258. parent = Tkinter._default_root
  259. self.prompt = prompt
  260. self.items = items
  261. self.width = width
  262. self.height = height
  263. self.result = None
  264. self.initialvalue = initialvalue
  265. tkd.Dialog.__init__(self, parent, title)
  266. def body(self, master):
  267. w = Label(self, text=self.prompt, justify=LEFT)
  268. w.pack(side=TOP)
  269. frame1 = Frame(self)
  270. frame1.pack(side=TOP, fill=X, expand=1, padx=5)
  271. scrollbar = Scrollbar(frame1, orient=VERTICAL)
  272. self.listbox = Listbox(frame1, yscrollcommand=scrollbar.set, width=self.width, height=self.height)
  273. scrollbar.config(command=self.listbox.yview)
  274. #self.listbox.config(yscrollcommand=scrollbar.set)
  275. self.listbox.pack(side=LEFT, fill=BOTH, expand=1)
  276. scrollbar.pack(side=LEFT, fill=Y)
  277. for item in self.items:
  278. self.listbox.insert(END, item)
  279. self.listbox.selection_set(self.initialvalue)
  280. self.listbox.activate(self.initialvalue)
  281. self.listbox.bind('<Double-1> ', self.list_select)
  282. self.listbox.bind('<Key> ', self.list_key)
  283. self.update()
  284. self.geometry("+%d+%d"%(self.parent.winfo_rootx()+100, self.parent.winfo_rooty()+100 ))
  285. return self.listbox
  286. def list_key(self, evt):
  287. key = evt.keysym
  288. #print 'Key %s' % (key)
  289. if key == "Return":
  290. self.ok()
  291. elif key == "Escape":
  292. self.cancel()
  293. def list_select(self, evt):
  294. self.ok()
  295. def apply(self):
  296. self.result = self.listbox.curselection()[0]
  297. class Info(tkd.Dialog):
  298. #a = ChoiceBox("ChoiceBox test", "Select stream", items, parent=self).result
  299. def __init__(self, title, items, width=20, height=15,
  300. initialvalue=0, parent = None):
  301. if not parent:
  302. import Tkinter
  303. parent = Tkinter._default_root
  304. self.prompt = prompt
  305. self.items = items
  306. self.width = width
  307. self.height = height
  308. self.result = None
  309. self.initialvalue = initialvalue
  310. tkd.Dialog.__init__(self, parent, title)
  311. def body(self, master):
  312. w = Label(self, text=self.prompt, justify=LEFT)
  313. w.pack(side=TOP)
  314. frame1 = Frame(self)
  315. frame1.pack(side=TOP, fill=X, expand=1, padx=5)
  316. scrollbar = Scrollbar(frame1, orient=VERTICAL)
  317. self.listbox = Listbox(frame1, yscrollcommand=scrollbar.set, width=self.width, height=self.height)
  318. scrollbar.config(command=self.listbox.yview)
  319. #self.listbox.config(yscrollcommand=scrollbar.set)
  320. self.listbox.pack(side=LEFT, fill=BOTH, expand=1)
  321. scrollbar.pack(side=LEFT, fill=Y)
  322. for item in self.items:
  323. self.listbox.insert(END, item)
  324. self.listbox.selection_set(self.initialvalue)
  325. self.listbox.activate(self.initialvalue)
  326. self.listbox.bind('<Double-1> ', self.list_select)
  327. self.listbox.bind('<Key> ', self.list_key)
  328. self.update()
  329. self.geometry("+%d+%d"%(self.parent.winfo_rootx()+100, self.parent.winfo_rooty()+100 ))
  330. return self.listbox
  331. def list_key(self, evt):
  332. key = evt.keysym
  333. #print 'Key %s' % (key)
  334. if key == "Return":
  335. self.ok()
  336. elif key == "Escape":
  337. self.cancel()
  338. def list_select(self, evt):
  339. self.ok()
  340. def apply(self):
  341. self.result = self.listbox.curselection()[0]
  342. def run(sources=None, data="config::home", title="Home"):
  343. app = Main(sources)
  344. app.show_content((title, data, "", ""))
  345. app.start()
  346. def run_cli(sources, data="config::home"):
  347. #options = sources.options_read("ltc")
  348. #print options
  349. history = []
  350. cur = ("Home",data,None,None)
  351. content = sources.get_content(cur[1])
  352. exit_loop = False
  353. while True:
  354. print
  355. for i,item in enumerate(content):
  356. s = "%i: %s - %s %s"%(i,item[0],item[1],item[2])
  357. print s #.encode(sys.stdout.encoding,"replace")
  358. while True:
  359. a = raw_input("Enter number, (-) for download, q for exit: ")
  360. if a in ("q","Q","x","X"):
  361. exit_loop = True
  362. print "Exiting"
  363. break
  364. try:
  365. n = int(a)
  366. break
  367. except:
  368. print "Not number!"
  369. if exit_loop: break
  370. download = False
  371. if n<0:
  372. n = abs(n)
  373. download = True
  374. cur2 = content[n]
  375. data0 = cur2[1].split("::")[1] if "::" in cur2[1] else cur2[1]
  376. if not data0:
  377. pass
  378. elif cur2[1] == "back":
  379. cur = history.pop()
  380. elif sources.is_video(cur2[1]):
  381. if sources.stream_type(cur2[1]):
  382. stream = util.item()
  383. stream["url"] = cur2[1]
  384. stream["name"] = cur2[0]
  385. streams = [stream]
  386. else:
  387. try:
  388. if not download:
  389. streams = sources.get_streams(cur2[1])
  390. else:
  391. stream = util.item()
  392. stream["url"] = cur2[1]
  393. stream["name"] = cur2[0]
  394. stream["url"] = util.streamproxy_encode2(stream["url"])
  395. print stream["url"]
  396. streams = [stream]
  397. except Exception as e:
  398. print unicode(e)
  399. traceback.print_exc()
  400. streams = []
  401. if streams:
  402. if not download:
  403. play_video(streams)
  404. else:
  405. #urlp = util.streamproxy_encode2(streams[0]["url"])
  406. #print urlp
  407. #util.player(urlp)
  408. #Downloader.download_video(streams)
  409. pass
  410. else:
  411. print "**No stream to play - %s "%(
  412. cur2[1])
  413. raw_input("Press any key")
  414. #import os
  415. #os.system('"c:\Program Files (x86)\VideoLAN\VLC\vlc.exe" "%s"'%cur2[1])
  416. else:
  417. if "{0}" in cur2[1]:
  418. a = raw_input("Enter value:")
  419. cur2 = (cur2[0],cur2[1].format(a),cur2[2],cur2[3])
  420. history.append(cur)
  421. cur = cur2
  422. try:
  423. content = sources.get_content(cur[1])
  424. except Exception as e:
  425. print unicode(e)
  426. traceback.print_exc()
  427. raw_input("Continue?")
  428. def play_video(streams, select=False):
  429. if len(streams)>1 and select:
  430. for i,s in enumerate(streams):
  431. print "%s: [%s,%s,%s] %s"%(i,s["quality"],s["lang"],s["type"],s["name"])
  432. a = raw_input("Select stram to play: ")
  433. try:
  434. n = int(a)
  435. except:
  436. n = 0
  437. if n>=len(streams):
  438. stream = streams[-1]
  439. else:
  440. stream = streams[n]
  441. else:
  442. stream = streams[0]
  443. stream = util.stream_change(stream)
  444. title = stream["name"] if not "nfo" in stream or not stream["nfo"] else util.nfo2title(stream["nfo"])
  445. desc = stream["desc"] if not "nfo" in stream or not stream["nfo"] else util.nfo2desc(stream["nfo"])
  446. img = stream["img"]
  447. url = stream["url"]
  448. suburl = ""
  449. print url
  450. if "subs" in stream and stream["subs"]:
  451. suburl = stream["subs"][0]["url"]
  452. print "\n**Download subtitles %s - %s"%(title,suburl)
  453. subs = urllib2.urlopen(suburl).read()
  454. if subs:
  455. fname0 = re.sub("[/\n\r\t,:\?]","_",title)
  456. subext = ".srt"
  457. subfile = os.path.join("",fname0+subext)
  458. if ".xml" in suburl:
  459. subs = ttaf2srt(subs)
  460. with open(subfile,"w") as f:
  461. f.write(subs)
  462. else:
  463. print "\n Error downloading subtitle %s"%suburl
  464. print "\n**Play stream %s\n%s" % (title, url.encode("utf8"))
  465. return player(url,title,suburl,stream["headers"])
  466. def player(url, title = "", suburl= "",headers={}):
  467. from subprocess import call
  468. cmd1 = [r"c:\Program Files\VideoLAN\VLC\vlc.exe",url,
  469. "--meta-title",title.decode("utf8").encode(sys.getfilesystemencoding()),
  470. "--http-user-agent","Enigma2"
  471. ]
  472. # 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
  473. cmd2 = [
  474. r"C:\gstreamer\1.0\x86_64\bin\gst-launch-1.0","-v",
  475. "playbin", 'uri="%s"'%url,
  476. #"souphttpsrc", "ssl-strict=false",
  477. #"proxy=127.0.0.1:8888",
  478. #'location="%s"'%url,
  479. #'!decodebin!autovideosink'
  480. ]
  481. cmd3 = ["ffplay.exe",url]
  482. cmd = cmd3 if url.startswith("https") else cmd2
  483. ret = call(cmd)
  484. #if ret:
  485. #a = raw_input("*** Error, continue")
  486. return
  487. if __name__ == "__main__":
  488. show_hidden = False
  489. if len(sys.argv)>1:
  490. data= sys.argv[1]
  491. else:
  492. data = "config::home"
  493. #sources = ContentSources("sources")
  494. sources = None
  495. run(sources, data)