Enigma2 plugin to to play various online streams (mostly Latvian).

VideoDownload.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import os
  2. from os import path
  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.Directories import fileExists
  11. from content.Downloader import DownloadWithProgress, DownloadWithProgressFragmented,get_header
  12. def timer_callback(timer,callback):
  13. if "callback" in dir(timer):
  14. timer.callback.append(callback)
  15. else:
  16. timer_conn = timer.timeout.connect(callback)
  17. ###########################################################################################
  18. class DownloadJob(Job):
  19. def __init__(self, url, outputfile, title, downloadStop,headers=None,stream_type="http"):
  20. Job.__init__(self, title)
  21. DownloadTask(self, url, outputfile, title, downloadStop,headers,stream_type)
  22. class DownloadTask(Task):
  23. def __init__(self, job, url, outputfile, title, downloadStop,headers={},stream_type="http"):
  24. Task.__init__(self, job, _('Downloading'))
  25. self.job = job
  26. self.title = title
  27. self.url = url
  28. self.outputfile = outputfile
  29. self.headers = headers
  30. self.stream_type = stream_type
  31. self.downloadStop = downloadStop
  32. self.job.currentbytes = 0
  33. self.job.totalbytes = -1
  34. def run(self, callback):
  35. self.callback = callback
  36. if self.stream_type == "hls":
  37. self.download = DownloadWithProgressFragmented(self.url, self.outputfile,self.headers)
  38. else:
  39. self.download = DownloadWithProgress(self.url, self.outputfile, self.headers)
  40. self.download.addProgress(self.downloadProgress)
  41. self.download.start().addCallback(self.downloadFinished).addErrback(self.downloadFailed)
  42. def downloadProgress(self, currentbytes, totalbytes):
  43. self.job.currentbytes = currentbytes
  44. self.job.totalbytes = totalbytes
  45. progress = self.job.currentbytes/float(self.job.totalbytes) * 100
  46. self.setProgress(progress)
  47. def downloadFinished(self, result):
  48. Task.processFinished(self, 0)
  49. self.setProgress(self.end)
  50. self.downloadStop("Success - " + self.title)
  51. def downloadFailed(self, failure_instance=None, error_message=''):
  52. print '[PlayStream] Video download failed'
  53. if error_message == '' and failure_instance is not None:
  54. error_message = failure_instance.getErrorMessage()
  55. print '[PlayStream]', str(error_message)
  56. Task.processFinished(self, 1)
  57. self.downloadStop("Failed - "+ self.title)
  58. class HLSDownloadJob(Job):
  59. def __init__(self, url, destfile, title,downloadStop,headers=None):
  60. Job.__init__(self, title)
  61. AddHLSProcessTask(self, url, destfile, title, downloadStop,headers)
  62. ######################################################################################################
  63. class AddHLSProcessTask(Task):
  64. def __init__(self, job, url, destfile, title,downloadStop,headers=None):
  65. Task.__init__(self, job, title)
  66. self.job = job
  67. self.title = title
  68. cmdline = '/usr/bin/gst-launch-1.0 "%s" ! hlsdemux ! filesink location="%s"'%(url,destfile)
  69. self.setCmdline(cmdline)
  70. self.url = url
  71. self.destfile = destfile
  72. self.downloadStop = downloadStop
  73. self.job.currentbytes = 0
  74. self.job.totalbytes = 0
  75. #self.setProgress(100)
  76. self.ProgressTimer = eTimer()
  77. timer_callback(self.ProgressTimer, self.ProgressUpdate)
  78. def ProgressUpdate(self):
  79. if not fileExists(self.destfile, 'r'):
  80. return
  81. self.job.currentbytes = path.getsize(self.destfile)
  82. progress = self.job.currentbytes/float(self.job.totalbytes) * 100 if self.job.totalbytes else 0
  83. self.setProgress(progress)
  84. #self.setProgress(int((path.getsize(self.destfile)/float(self.totalbytes))*100))
  85. self.ProgressTimer.start(5000, True)
  86. def prepare(self):
  87. self.job.totalbytes = -1 # TODO getsize(self.url)
  88. self.ProgressTimer.start(5000, True)
  89. def afterRun(self):
  90. #self.setProgress(100)
  91. self.ProgressTimer.stop()
  92. self.downloadStop(self.title)
  93. class VideoDownloadList(Screen):
  94. screenWidth = getDesktop(0).size().width()
  95. if screenWidth and screenWidth == 1920:
  96. skin = """<screen position="center,center" size="945,555">
  97. <widget source="list" render="Listbox" position="center,45" size="900,405" \
  98. scrollbarMode="showOnDemand" >
  99. <convert type="TemplatedMultiContent" >
  100. {"template": [
  101. MultiContentEntryText(pos=(15,1), size=(465,33), \
  102. font=0, flags=RT_HALIGN_LEFT, text=1), # Title
  103. MultiContentEntryText(pos=(345,1), size=(225,33), \
  104. font=0, flags=RT_HALIGN_RIGHT, text=2), # State
  105. MultiContentEntryProgress(pos=(585,6), size=(150,33), \
  106. percent=-3), # Progress
  107. MultiContentEntryText(pos=(750,1), size=(120,33), \
  108. font=0, flags=RT_HALIGN_LEFT, text=4), # Percentage
  109. ],
  110. "fonts": [gFont("Regular",30)],
  111. "itemHeight": 45}
  112. </convert>
  113. </widget>
  114. <ePixmap position="center,484" size="210,60" pixmap="skin_default/buttons/red.png" \
  115. transparent="1" alphatest="on" />
  116. <widget source="key_red" render="Label" position="center,485" zPosition="2" \
  117. size="210,60" valign="center" halign="center" font="Regular;33" transparent="1" />
  118. </screen>"""
  119. else:
  120. skin = """<screen position="center,center" size="630,370">
  121. <widget source="list" render="Listbox" position="center,30" size="600,270" \
  122. scrollbarMode="showOnDemand" >
  123. <convert type="TemplatedMultiContent" >
  124. {"template": [
  125. MultiContentEntryText(pos=(10,1), size=(210,22), \
  126. font=0, flags=RT_HALIGN_LEFT, text=1), # Title
  127. MultiContentEntryText(pos=(230,1), size=(150,22), \
  128. font=0, flags=RT_HALIGN_RIGHT, text=2), # State
  129. MultiContentEntryProgress(pos=(390,4), size=(100,22), \
  130. percent=-3), # Progress
  131. MultiContentEntryText(pos=(500,1), size=(80,22), \
  132. font=0, flags=RT_HALIGN_LEFT, text=4), # Percentage
  133. ],
  134. "fonts": [gFont("Regular",20)],
  135. "itemHeight": 30}
  136. </convert>
  137. </widget>
  138. <ePixmap position="center,323" size="140,40" pixmap="skin_default/buttons/red.png" \
  139. transparent="1" alphatest="on" />
  140. <widget source="key_red" render="Label" position="center,328" zPosition="2" \
  141. size="140,30" valign="center" halign="center" font="Regular;22" transparent="1" />
  142. </screen>"""
  143. def __init__(self, session):
  144. Screen.__init__(self, session)
  145. self['key_red'] = StaticText(_('Exit'))
  146. self['list'] = List([])
  147. self['actions'] = ActionMap(['SetupActions', 'ColorActions'],
  148. {
  149. 'cancel': self.close,
  150. 'ok': self.ok,
  151. 'red': self.close,
  152. 'blue': self.abort
  153. }, -2)
  154. self.onLayoutFinish.append(self.layoutFinished)
  155. self.onClose.append(self.cleanVariables)
  156. self.progressTimer = eTimer()
  157. timer_callback(self.progressTimer,self.updateDownloadList)
  158. def layoutFinished(self):
  159. self.setTitle(_('Active video downloads'))
  160. self.updateDownloadList()
  161. def cleanVariables(self):
  162. del self.progressTimer
  163. def updateDownloadList(self):
  164. self.progressTimer.stop()
  165. downloadList = []
  166. for job in job_manager.getPendingJobs():
  167. progress = job.progress / float(job.end) * 100
  168. currentbytes = job.currentbytes if "currentbytes" in dir(job) else 0
  169. downloadList.append((job, job.name, job.getStatustext(),
  170. int(progress), "%.1fM"%(currentbytes/1024.0/1024.0) ))
  171. self['list'].updateList(downloadList)
  172. if downloadList:
  173. self.progressTimer.startLongTimer(2)
  174. def ok(self):
  175. current = self['list'].getCurrent()
  176. if current:
  177. from Screens.TaskView import JobView
  178. self.session.open(JobView, current[0])
  179. def abort(self):
  180. current = self['list'].getCurrent()
  181. job = current[0]
  182. if job.status == job.NOT_STARTED:
  183. job_manager.active_jobs.remove(job)
  184. self.close(False)
  185. elif job.status == job.IN_PROGRESS:
  186. job.cancel()
  187. else:
  188. self.close(False)
  189. class VideoDirBrowser(Screen):
  190. def __init__(self, session, downloadDir):
  191. Screen.__init__(self, session)
  192. self.skinName = ['VideoDirBrowser', 'FileBrowser']
  193. self['key_red'] = StaticText(_('Cancel'))
  194. self['key_green'] = StaticText(_('Use'))
  195. if not os.path.exists(downloadDir):
  196. downloadDir = '/'
  197. self.filelist = FileList(downloadDir, showFiles = False)
  198. self['filelist'] = self.filelist
  199. self['FilelistActions'] = ActionMap(['SetupActions', 'ColorActions'],
  200. {
  201. 'cancel': self.cancel,
  202. 'red': self.cancel,
  203. 'ok': self.ok,
  204. 'green': self.use
  205. }, -2)
  206. self.onLayoutFinish.append(self.layoutFinished)
  207. def layoutFinished(self):
  208. self.setTitle(_('Please select the download directory'))
  209. def ok(self):
  210. if self.filelist.canDescent():
  211. self.filelist.descent()
  212. def use(self):
  213. currentDir = self['filelist'].getCurrentDirectory()
  214. dirName = self['filelist'].getFilename()
  215. if currentDir is None or \
  216. (self.filelist.canDescent() and dirName and len(dirName) > len(currentDir)):
  217. self.close(dirName)
  218. def cancel(self):
  219. self.close(False)