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.

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