You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
4.6 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. #! /usr/bin/env python
  2. import sys
  3. import subprocess
  4. from os import name
  5. from time import sleep
  6. from wx import CallAfter
  7. from threading import Thread
  8. from wx.lib.pubsub import setuparg1
  9. from wx.lib.pubsub import pub as Publisher
  10. from .Utils import (
  11. remove_spaces,
  12. string_to_array,
  13. get_encoding,
  14. encode_list
  15. )
  16. OS_TYPE = name
  17. MAX_DOWNLOAD_THREADS = 3
  18. PUBLISHER_TOPIC = 'download'
  19. class DownloadManager(Thread):
  20. def __init__(self, options, downloadlist):
  21. super(DownloadManager, self).__init__()
  22. self.downloadlist = downloadlist
  23. self.options = options
  24. self.running = True
  25. self.procList = []
  26. self.procNo = 0
  27. self.start()
  28. def run(self):
  29. while self.running:
  30. if self.downloadlist:
  31. # Extract url, index from data
  32. url, index = self.extract_data()
  33. # Wait for your turn if there are not more positions in 'queue'
  34. while self.procNo >= MAX_DOWNLOAD_THREADS:
  35. proc = self.check_queue(0.5)
  36. if proc != None:
  37. self.procList.remove(proc)
  38. self.procNo -= 1
  39. # If we still running create new ProcessWrapper thread
  40. if self.running:
  41. self.procList.append(ProcessWrapper(self.options, url, index))
  42. self.procNo += 1
  43. else:
  44. # Return True if at least one process is alive else return False
  45. if not self.downloading():
  46. self.running = False
  47. else:
  48. sleep(1)
  49. # If we reach here close down all child threads
  50. self.terminate_all()
  51. CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['finish', -1])
  52. def add_download_item(self, downloadItem):
  53. self.downloadlist.append(downloadItem)
  54. def extract_data(self):
  55. data = self.downloadlist.pop(0)
  56. url = data['url']
  57. index = data['index']
  58. return url, index
  59. def terminate_all(self):
  60. for proc in self.procList:
  61. if proc.isAlive():
  62. proc.close()
  63. proc.join()
  64. def downloading(self):
  65. for proc in self.procList:
  66. if proc.isAlive():
  67. return True
  68. return False
  69. def check_queue(self, t=1):
  70. for proc in self.procList:
  71. if not self.running: break
  72. if not proc.isAlive():
  73. return proc
  74. sleep(t)
  75. return None
  76. def close(self):
  77. self.procNo = 0
  78. self.running = False
  79. CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['close', -1])
  80. class ProcessWrapper(Thread):
  81. def __init__(self, options, url, index):
  82. super(ProcessWrapper, self).__init__()
  83. self.options = options
  84. self.index = index
  85. self.url = url
  86. self.proc = None
  87. self.stopped = False
  88. self.err = False
  89. self.start()
  90. def run(self):
  91. self.proc = subprocess.Popen(self.get_cmd(),
  92. stdout=subprocess.PIPE,
  93. stderr=subprocess.PIPE,
  94. startupinfo=self.set_process_info())
  95. # while subprocess is alive and NOT the current thread
  96. while self.proc_is_alive():
  97. # read output
  98. output = self.read()
  99. if output != '':
  100. # process output
  101. data = self.proc_output(output)
  102. if self.err:
  103. CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['error', self.index])
  104. else:
  105. CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, data)
  106. if not self.err and not self.stopped:
  107. CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['finish', self.index])
  108. def close(self):
  109. self.proc.kill()
  110. self.stopped = True
  111. CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['close', self.index])
  112. def proc_is_alive(self):
  113. return self.proc.poll() == None
  114. def read(self):
  115. output = self.proc.stdout.readline()
  116. if output == '':
  117. output = self.proc.stderr.readline()
  118. if output != '':
  119. self.err = True
  120. return output.rstrip()
  121. def proc_output(self, output):
  122. data = remove_spaces(string_to_array(output))
  123. data.append(self.index)
  124. data = self.filter_data(data)
  125. return data
  126. def filter_data(self, data):
  127. ''' Filters data for output exceptions '''
  128. filter_list = ['Destination:', '100%', 'Resuming']
  129. if len(data) > 3:
  130. if data[0] == '[download]':
  131. if data[1] in filter_list or len(data[1]) > 11:
  132. return ['ignore', self.index]
  133. if data[1] == 'Downloading':
  134. if data[2] == 'video':
  135. return ['playlist', data[3], data[5], self.index]
  136. else:
  137. return ['ignore', self.index]
  138. else:
  139. if data[1] == 'UnicodeWarning:':
  140. self.err = False
  141. return ['ignore', self.index]
  142. return data
  143. def get_cmd(self):
  144. enc = get_encoding()
  145. if enc != None:
  146. data = encode_list(self.options + [self.url], enc)
  147. return self.options + [self.url]
  148. def set_process_info(self):
  149. if OS_TYPE == 'nt':
  150. info = subprocess.STARTUPINFO()
  151. info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  152. return info
  153. else:
  154. return None