Skip to content

Commit

Permalink
Move download info to the Worker class
Browse files Browse the repository at this point in the history
Move download informations from the downloaders.py module
to the downloadmanager.py Worker class. By moving those
informations to the Worker class we don't need to add them
in each downloader separately every time a new one gets
implemented, giving us a better design overall.
  • Loading branch information
MrS0m30n3 committed Oct 5, 2015
1 parent 5f2cb87 commit 6b3c6ac
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 102 deletions.
112 changes: 51 additions & 61 deletions youtube_dl_gui/downloaders.py
Expand Up @@ -98,9 +98,6 @@ class YoutubeDLDownloader(object):
log_data (function): Optional callback function to write data to
the log file.
Note:
For available data keys check self._data under __init__().
Warnings:
The caller is responsible for calling the close() method after he has
finished with the object in order for the object to be able to properly
Expand Down Expand Up @@ -134,18 +131,6 @@ def __init__(self, youtubedl_path, data_hook=None, log_data=None):

self._return_code = self.OK
self._proc = None
self._data = {
'playlist_index': None,
'playlist_size': None,
'extension': None,
'filesize': None,
'filename': None,
'percent': None,
'status': None,
'speed': None,
'path': None,
'eta': None
}

self._encoding = self._get_encoding()
self._stderr_queue = Queue()
Expand All @@ -171,7 +156,7 @@ def download(self, url, options):
STOPPED (5): The download process was stopped by the user.
"""
self._reset()
self._return_code = self.OK

cmd = self._get_cmd(url, options)
self._create_process(cmd)
Expand All @@ -183,8 +168,9 @@ def download(self, url, options):
stdout = stdout.decode(self._encoding, 'ignore')

if stdout:
self._sync_data(extract_data(stdout))
self._hook_data()
data_dict = extract_data(stdout)
self._extract_info(data_dict)
self._hook_data(data_dict)

# Read stderr after download process has been completed
# We don't need to read stderr in real time
Expand Down Expand Up @@ -231,68 +217,60 @@ def _is_warning(self, stderr):

def _last_data_hook(self):
"""Set the last data information based on the return code. """
data_dictionary = {}

if self._return_code == self.OK:
self._data['status'] = 'Finished'
data_dictionary['status'] = 'Finished'
elif self._return_code == self.ERROR:
self._data['status'] = 'Error'
self._data['speed'] = ''
self._data['eta'] = ''
data_dictionary['status'] = 'Error'
data_dictionary['speed'] = ''
data_dictionary['eta'] = ''
elif self._return_code == self.WARNING:
self._data['status'] = 'Warning'
self._data['speed'] = ''
self._data['eta'] = ''
data_dictionary['status'] = 'Warning'
data_dictionary['speed'] = ''
data_dictionary['eta'] = ''
elif self._return_code == self.STOPPED:
self._data['status'] = 'Stopped'
self._data['speed'] = ''
self._data['eta'] = ''
data_dictionary['status'] = 'Stopped'
data_dictionary['speed'] = ''
data_dictionary['eta'] = ''
elif self._return_code == self.ALREADY:
self._data['status'] = 'Already Downloaded'
data_dictionary['status'] = 'Already Downloaded'
else:
self._data['status'] = 'Filesize Abort'

self._hook_data()

def _reset(self):
"""Reset the data. """
self._return_code = self.OK
data_dictionary['status'] = 'Filesize Abort'

for key in self._data:
self._data[key] = None
self._hook_data(data_dictionary)

def _sync_data(self, data):
"""Synchronise self._data with data. It also filters some keys.
def _extract_info(self, data):
"""Extract informations about the download process from the given data.
Args:
data (dictionary): Python dictionary that contains different
keys. The keys are not standar the dictionary can also be
empty when there are no data to extract. See extract_data().
"""
for key in data:
if key == 'status':
if data['status'] == 'Already Downloaded':
# Set self._return_code to already downloaded
# and trash that key
self._set_returncode(self.ALREADY)
data['status'] = None

if data['status'] == 'Filesize Abort':
# Set self._return_code to filesize abort
# and trash that key
self._set_returncode(self.FILESIZE_ABORT)
data['status'] = None

self._data[key] = data[key]
if 'status' in data:
if data['status'] == 'Already Downloaded':
# Set self._return_code to already downloaded
# and trash that key
self._set_returncode(self.ALREADY)
data['status'] = None

if data['status'] == 'Filesize Abort':
# Set self._return_code to filesize abort
# and trash that key
self._set_returncode(self.FILESIZE_ABORT)
data['status'] = None

def _log(self, data):
"""Log data using the callback function. """
if self.log_data is not None:
self.log_data(data)

def _hook_data(self):
"""Pass self._data back to the data_hook. """
def _hook_data(self, data):
"""Pass data back to the caller. """
if self.data_hook is not None:
self.data_hook(self._data)
self.data_hook(data)

def _proc_is_alive(self):
"""Returns True if self._proc is alive else False. """
Expand Down Expand Up @@ -366,11 +344,23 @@ def extract_data(stdout):
stdout (string): String that contains the youtube-dl stdout.
Returns:
Python dictionary. For available keys check self._data under
YoutubeDLDownloader.__init__().
Python dictionary. The returned dictionary can be empty if there are
no data to extract else it may contain one or more of the
following keys:
'status' : Contains the status of the download process.
'path' : Destination path.
'extension' : The file extension.
'filename' : The filename without the extension.
'percent' : The percentage of the video being downloaded.
'eta' : Estimated time for the completion of the download process.
'speed' : Download speed.
'filesize' : The size of the video file being downloaded.
'playlist_index' : The playlist index of the current video file being downloaded.
'playlist_size' : The number of videos in the playlist.
"""
data_dictionary = dict()
data_dictionary = {}

if not stdout:
return data_dictionary
Expand Down
91 changes: 50 additions & 41 deletions youtube_dl_gui/downloadmanager.py
Expand Up @@ -207,7 +207,7 @@ def _youtubedl_path(self):
class Worker(Thread):

"""Simple worker which downloads the given url using a downloader
from the 'downloaders' module.
from the downloaders.py module.
Attributes:
WAIT_TIME (float): Time in seconds to sleep.
Expand All @@ -225,6 +225,9 @@ class Worker(Thread):
If the log_manager is set (not None) then the caller has to make
sure that the log_lock is also set.
Note:
For available data keys see self._data under the __init__() method.
"""

WAIT_TIME = 0.1
Expand All @@ -237,25 +240,37 @@ def __init__(self, opt_manager, youtubedl, log_manager=None, log_lock=None):

self._downloader = YoutubeDLDownloader(youtubedl, self._data_hook, self._log_data)
self._options_parser = OptionsParser()
self._running = True
self._url = None
self._index = -1
self._successful = 0
self._running = True

self._data = {
'playlist_index': None,
'playlist_size': None,
'extension': None,
'filesize': None,
'filename': None,
'percent': None,
'status': None,
'index': None,
'speed': None,
'path': None,
'eta': None,
'url': None
}

self.start()

def run(self):
while self._running:
if self._url is not None:
if self._data['url'] is not None:
options = self._options_parser.parse(self.opt_manager.options)
ret_code = self._downloader.download(self._url, options)
ret_code = self._downloader.download(self._data['url'], options)

if (ret_code == YoutubeDLDownloader.OK or
ret_code == YoutubeDLDownloader.ALREADY):
self._successful += 1

# Reset url value
self._url = None
self._reset()

time.sleep(self.WAIT_TIME)

Expand All @@ -272,8 +287,8 @@ def download(self, item):
download process.
"""
self._url = item['url']
self._index = item['index']
self._data['url'] = item['url']
self._data['index'] = item['index']

def stop_download(self):
"""Stop the download process of the worker. """
Expand All @@ -286,13 +301,18 @@ def close(self):

def available(self):
"""Return True if the worker has no job else False. """
return self._url is None
return self._data['url'] is None

@property
def successful(self):
"""Return the number of successful downloads for current worker. """
return self._successful

def _reset(self):
"""Reset self._data back to the original state. """
for key in self._data:
self._data[key] = None

def _log_data(self, data):
"""Callback method for self._downloader.
Expand All @@ -309,43 +329,32 @@ def _log_data(self, data):
self.log_lock.release()

def _data_hook(self, data):
"""Callback method to be used with the YoutubeDLDownloader object.
"""Callback method for self._downloader.
This method takes the data from the downloader, merges the
playlist_info with the current status(if any) and sends the
data back to the GUI using the self._talk_to_gui method.
This method updates self._data and sends them back to the GUI
using the self._talk_to_gui() method.
Args:
data (dictionary): Python dictionary which contains information
about the download process. (See YoutubeDLDownloader class).
about the download process. For more info see the
extract_data() function under the downloaders.py module.
"""
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']
# Update each key
for key in data:
self._data[key] = data[key]

data['status'] += playlist_info
# Build the playlist status
if self._data['status'] is not None and self._data['playlist_index'] is not None:
self._data['status'] = '{status} {index}/{size}'.format(
status=self._data['status'],
index=self._data['playlist_index'],
size=self._data['playlist_size']
)

self._talk_to_gui(data)
self._talk_to_gui()

def _talk_to_gui(self, data):
"""Send data back to the GUI after inserting the index. """
data['index'] = self._index
CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, data)


if __name__ == '__main__':
"""Direct call of the module for testing.
Raises:
ValueError: Attempted relative import in non-package
def _talk_to_gui(self):
"""Send self._data back to the GUI. """
CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, self._data)

Note:
Before you run the tests change relative imports else an exceptions
will be raised. You need to change relative imports on all the modules
you are gonna use.
"""
print "No tests available"

0 comments on commit 6b3c6ac

Please sign in to comment.