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.
191 lines
5.4 KiB
191 lines
5.4 KiB
#!/usr/bin/env python2
|
|
|
|
''' Youtube-dlG module to download videos & handle each download. '''
|
|
|
|
import time
|
|
import os.path
|
|
from threading import Thread
|
|
|
|
from wx import CallAfter
|
|
from wx.lib.pubsub import setuparg1
|
|
from wx.lib.pubsub import pub as Publisher
|
|
|
|
from .parsers import OptionsParser
|
|
from .updthread import UpdateThread
|
|
from .downloaders import YoutubeDLDownloader
|
|
|
|
from .utils import YOUTUBEDL_BIN
|
|
|
|
|
|
class DownloadManager(Thread):
|
|
|
|
""" DownloadManager """
|
|
|
|
PUBLISHER_TOPIC = 'dlmanager'
|
|
WORKERS_NUMBER = 3
|
|
WAIT_TIME = 0.1
|
|
|
|
def __init__(self, urls_list, opt_manager, log_manager=None):
|
|
super(DownloadManager, self).__init__()
|
|
self.opt_manager = opt_manager
|
|
self.log_manager = log_manager
|
|
self.urls_list = urls_list
|
|
|
|
self._time_it_took = 0
|
|
self._successful = 0
|
|
self._running = True
|
|
|
|
self._workers = self._init_workers()
|
|
self.start()
|
|
|
|
@property
|
|
def successful(self):
|
|
return self._successful
|
|
|
|
@property
|
|
def time_it_took(self):
|
|
return self._time_it_took
|
|
|
|
def increase_succ(self):
|
|
self._successful += 1
|
|
|
|
def run(self):
|
|
self._check_youtubedl()
|
|
self._time_it_took = time.time()
|
|
|
|
while self._running:
|
|
for worker in self._workers:
|
|
if worker.available() and self.urls_list:
|
|
worker.download(self.urls_list.pop(0))
|
|
|
|
time.sleep(self.WAIT_TIME)
|
|
|
|
if not self.urls_list and self._jobs_done():
|
|
break
|
|
|
|
# Clean up
|
|
for worker in self._workers:
|
|
worker.close()
|
|
worker.join()
|
|
|
|
self._time_it_took = time.time() - self._time_it_took
|
|
|
|
if not self._running:
|
|
self._talk_to_gui('closed')
|
|
else:
|
|
self._talk_to_gui('finished')
|
|
|
|
def active(self):
|
|
counter = 0
|
|
for worker in self._workers:
|
|
if not worker.available():
|
|
counter += 1
|
|
|
|
counter += len(self.urls_list)
|
|
|
|
return counter
|
|
|
|
def stop_downloads(self):
|
|
self._talk_to_gui('closing')
|
|
self._running = False
|
|
for worker in self._workers:
|
|
worker.stop_download()
|
|
|
|
def add_url(self, url):
|
|
self.urls_list.append(url)
|
|
|
|
def _talk_to_gui(self, data):
|
|
CallAfter(Publisher.sendMessage, self.PUBLISHER_TOPIC, data)
|
|
|
|
def _check_youtubedl(self):
|
|
if not os.path.exists(self._youtubedl_path()):
|
|
UpdateThread(self.opt_manager.options['youtubedl_path'], True).join()
|
|
|
|
def _jobs_done(self):
|
|
for worker in self._workers:
|
|
if not worker.available():
|
|
return False
|
|
|
|
return True
|
|
|
|
def _youtubedl_path(self):
|
|
path = self.opt_manager.options['youtubedl_path']
|
|
path = os.path.join(path, YOUTUBEDL_BIN)
|
|
return path
|
|
|
|
def _init_workers(self):
|
|
youtubedl = self._youtubedl_path()
|
|
return [Worker(self.opt_manager, youtubedl, self.increase_succ, self.log_manager) for i in xrange(self.WORKERS_NUMBER)]
|
|
|
|
|
|
class Worker(Thread):
|
|
|
|
PUBLISHER_TOPIC = 'dlworker'
|
|
WAIT_TIME = 0.1
|
|
|
|
def __init__(self, opt_manager, youtubedl, increase_succ, log_manager=None):
|
|
super(Worker, self).__init__()
|
|
self.increase_succ = increase_succ
|
|
self.opt_manager = opt_manager
|
|
|
|
self._downloader = YoutubeDLDownloader(youtubedl, self._data_hook, log_manager)
|
|
self._options_parser = OptionsParser()
|
|
self._running = True
|
|
self._url = None
|
|
self._index = -1
|
|
|
|
self.start()
|
|
|
|
def run(self):
|
|
while self._running:
|
|
if self._url is not None:
|
|
options = self._options_parser.parse(self.opt_manager.options)
|
|
ret_code = self._downloader.download(self._url, options)
|
|
|
|
if (ret_code == YoutubeDLDownloader.OK or
|
|
ret_code == YoutubeDLDownloader.ALREADY):
|
|
self.increase_succ()
|
|
|
|
# Reset
|
|
self._url = None
|
|
|
|
time.sleep(self.WAIT_TIME)
|
|
|
|
def download(self, item):
|
|
self._url = item['url']
|
|
self._index = item['index']
|
|
|
|
def stop_download(self):
|
|
self._downloader.stop()
|
|
|
|
def close(self):
|
|
self._running = False
|
|
self._downloader.stop()
|
|
|
|
def available(self):
|
|
return self._url is None
|
|
|
|
def _data_hook(self, data):
|
|
if data['status'] is not None and data['playlist_index'] is not None:
|
|
playlist_info = ' '
|
|
playlist_info += data['playlist_index']
|
|
playlist_info += '/'
|
|
playlist_info += data['playlist_size']
|
|
|
|
data['status'] += playlist_info
|
|
|
|
self._talk_to_gui(data)
|
|
|
|
def _talk_to_gui(self, data):
|
|
data['index'] = self._index
|
|
CallAfter(Publisher.sendMessage, self.PUBLISHER_TOPIC, data)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
''' Direct call of module for testing. Before
|
|
you run the tests change relative imports or you will
|
|
get [ValueError: Attempted relative import in non-package].
|
|
You need to change relative imports on all the modules
|
|
you are gonna use.'''
|
|
print "No tests available"
|
|
|