Browse Source

Refactor code

rename YoutubeDLInterpreter -> YDLOptionsParser
pep8
etc..
doc-issue-template
MrS0m30n3 11 years ago
parent
commit
93868c8e24
11 changed files with 1326 additions and 1266 deletions
  1. 36
      youtube_dl_gui/DownloadObject.py
  2. 162
      youtube_dl_gui/DownloadThread.py
  3. 70
      youtube_dl_gui/LogManager.py
  4. 174
      youtube_dl_gui/OptionsHandler.py
  5. 47
      youtube_dl_gui/UpdateThread.py
  6. 105
      youtube_dl_gui/Utils.py
  7. 175
      youtube_dl_gui/YDLOptionsParser.py
  8. 1653
      youtube_dl_gui/YoutubeDLGUI.py
  9. 163
      youtube_dl_gui/YoutubeDLInterpreter.py
  10. 6
      youtube_dl_gui/__init__.py
  11. 1
      youtube_dl_gui/data.py

36
youtube_dl_gui/DownloadObject.py

@ -24,7 +24,12 @@ class DownloadObject(object):
download() download()
Params: URL to download Params: URL to download
Options list e.g. ['--help'] Options list e.g. ['--help']
Return: On download error return False else True.
Return: See downlaoad() return codes
Return-Codes:
OK : 'Url downloaded successfully'
ERROR : 'Error occured while downloading'
ALREADY: 'Url is already downloaded'
stop() stop()
Params: None Params: None
@ -37,6 +42,11 @@ class DownloadObject(object):
See self._init_data(). See self._init_data().
''' '''
# download() return codes
OK = 0
ERROR = 1
ALREADY = -1
STDERR_IGNORE = '' # Default filter for our self._log() method STDERR_IGNORE = '' # Default filter for our self._log() method
def __init__(self, youtubedl_path, data_hook=None, logger=None): def __init__(self, youtubedl_path, data_hook=None, logger=None):
@ -44,6 +54,7 @@ class DownloadObject(object):
self.data_hook = data_hook self.data_hook = data_hook
self.logger = logger self.logger = logger
self.files_list = [] self.files_list = []
self._return_code = 0
self._proc = None self._proc = None
self._init_data() self._init_data()
@ -61,8 +72,7 @@ class DownloadObject(object):
} }
def download(self, url, options): def download(self, url, options):
''' Download url using given options list. '''
error = False
self._return_code = self.OK
cmd = self._get_cmd(url, options) cmd = self._get_cmd(url, options)
cmd = self._encode_cmd(cmd) cmd = self._encode_cmd(cmd)
@ -77,7 +87,7 @@ class DownloadObject(object):
synced = self._sync_data(data) synced = self._sync_data(data)
if stderr != '': if stderr != '':
error = True
self._return_code = self.ERROR
if self.logger is not None: if self.logger is not None:
self._log(stderr) self._log(stderr)
@ -85,10 +95,9 @@ class DownloadObject(object):
if self.data_hook is not None and synced: if self.data_hook is not None and synced:
self._hook_data() self._hook_data()
return (not error)
return self._return_code
def stop(self): def stop(self):
''' Stop download process. '''
if self._proc is not None: if self._proc is not None:
self._proc.kill() self._proc.kill()
@ -104,13 +113,17 @@ class DownloadObject(object):
# Keep only the filename not the path on data['filename'] # Keep only the filename not the path on data['filename']
data['filename'] = get_filename(data['filename']) data['filename'] = get_filename(data['filename'])
if key == 'status':
# Set self._return_code to already downloaded
if data[key] == 'already_downloaded':
self._return_code = self.ALREADY
self._data[key] = data[key] self._data[key] = data[key]
synced = True synced = True
return synced return synced
def _add_on_files_list(self, filename): def _add_on_files_list(self, filename):
''' Add filename on files_list. '''
self.files_list.append(filename) self.files_list.append(filename)
def _log(self, data): def _log(self, data):
@ -137,7 +150,6 @@ class DownloadObject(object):
return stdout, stderr return stdout, stderr
def _read_stdout(self): def _read_stdout(self):
''' Read subprocess stdout. '''
if self._proc is None: if self._proc is None:
return '' return ''
@ -145,7 +157,6 @@ class DownloadObject(object):
return stdout.rstrip() return stdout.rstrip()
def _read_stderr(self): def _read_stderr(self):
''' Read subprocess stderr. '''
if self._proc is None: if self._proc is None:
return '' return ''
@ -199,9 +210,11 @@ def extract_data(stdout):
if header == 'download': if header == 'download':
data_dictionary['status'] = 'download' data_dictionary['status'] = 'download'
# Get filename
if stdout[0] == 'Destination:': if stdout[0] == 'Destination:':
data_dictionary['filename'] = ' '.join(stdout[1:]) data_dictionary['filename'] = ' '.join(stdout[1:])
# Get progress info
elif '%' in stdout[0]: elif '%' in stdout[0]:
if stdout[0] == '100%': if stdout[0] == '100%':
data_dictionary['speed'] = '' data_dictionary['speed'] = ''
@ -211,10 +224,15 @@ def extract_data(stdout):
data_dictionary['speed'] = stdout[4] data_dictionary['speed'] = stdout[4]
data_dictionary['eta'] = stdout[6] data_dictionary['eta'] = stdout[6]
# Get playlist info
elif stdout[0] == 'Downloading' and stdout[1] == 'video': elif stdout[0] == 'Downloading' and stdout[1] == 'video':
data_dictionary['playlist_index'] = stdout[2] data_dictionary['playlist_index'] = stdout[2]
data_dictionary['playlist_size'] = stdout[4] data_dictionary['playlist_size'] = stdout[4]
# Get file already downloaded status
elif stdout[-1] == 'downloaded':
data_dictionary['status'] = 'already_downloaded'
if header == 'ffmpeg': if header == 'ffmpeg':
data_dictionary['status'] = 'post_process' data_dictionary['status'] = 'post_process'

162
youtube_dl_gui/DownloadThread.py

@ -7,7 +7,7 @@ from wx import CallAfter
from wx.lib.pubsub import setuparg1 from wx.lib.pubsub import setuparg1
from wx.lib.pubsub import pub as Publisher from wx.lib.pubsub import pub as Publisher
from .YoutubeDLInterpreter import YoutubeDLInterpreter
from .YDLOptionsParser import OptionsParser
from .DownloadObject import DownloadObject from .DownloadObject import DownloadObject
from .Utils import ( from .Utils import (
@ -23,104 +23,100 @@ class DownloadManager(Thread):
PUBLISHER_TOPIC = 'download_manager' PUBLISHER_TOPIC = 'download_manager'
MAX_DOWNLOAD_THREADS = 3 MAX_DOWNLOAD_THREADS = 3
def __init__(self, downloadlist, opt_manager, logmanager=None):
def __init__(self, download_list, opt_manager, log_manager=None):
super(DownloadManager, self).__init__() super(DownloadManager, self).__init__()
self.downloadlist = downloadlist
self.download_list = download_list
self.opt_manager = opt_manager self.opt_manager = opt_manager
self.logmanager = logmanager
self.running = True
self.kill = False
self.procList = []
self.procNo = 0
self.log_manager = log_manager
self._threads_lst = []
self._running = True
self._kill = False
self.start() self.start()
def run(self): def run(self):
while self.running:
if self.downloadlist:
# Extract url, index from data
url, index = self.extract_data()
# Wait for your turn if there are not more positions in 'queue'
while self.procNo >= self.MAX_DOWNLOAD_THREADS:
proc = self.check_queue()
if proc != None:
self.procList.remove(proc)
self.procNo -= 1
sleep(1)
# If we still running create new ProcessWrapper thread
if self.running:
self.procList.append(
DownloadThread(
url,
index,
self.opt_manager,
self.logmanager
)
)
self.procNo += 1
while self._running:
# If download list is not empty
if self.download_list:
url, index = self._extract_data()
self._check_download_queue()
if self._running:
self._download(url, index)
else: else:
# Return True if at least one process is alive else return False
if not self.downloading(): if not self.downloading():
self.running = False
self._running = False
else: else:
sleep(0.1) sleep(0.1)
# If we reach here close down all child threads
self.terminate_all()
if not self.kill:
self._terminate_all()
if not self._kill:
self._callafter('finish') self._callafter('finish')
def downloading(self): def downloading(self):
for proc in self.procList:
if proc.isAlive():
''' Return True if at least one download thread is alive '''
for thread in self._threads_lst:
if thread.is_alive():
return True return True
return False return False
def _add_download_item(self, downloadItem):
self.downloadlist.append(downloadItem)
def add_download_item(self, item):
''' Add download item on download list '''
self.download_list.append(item)
def close(self, kill=False):
self._callafter('close')
self._running = False
self._kill = kill
def _download(self, url, index):
''' Download given url '''
dl_thread = DownloadThread(url, index, self.opt_manager, self.log_manager)
self._threads_lst.append(dl_thread)
def extract_data(self):
data = self.downloadlist.pop(0)
def _extract_data(self):
''' Extract url, index from download list '''
data = self.download_list.pop(0)
url = data['url'] url = data['url']
index = data['index'] index = data['index']
return url, index return url, index
def terminate_all(self):
for proc in self.procList:
if proc.isAlive():
proc.close()
proc.join()
def check_queue(self):
for proc in self.procList:
if not self.running: break
if not proc.isAlive():
return proc
return None
def _terminate_all(self):
''' Close down all download threads '''
for thread in self._threads_lst:
if thread.is_alive():
thread.close()
thread.join()
def _check_download_queue(self):
while len(self._threads_lst) >= self.MAX_DOWNLOAD_THREADS:
sleep(1)
for thread in self._threads_lst:
if not self._running:
return
if not thread.is_alive():
self._threads_lst.remove(thread)
def _callafter(self, data): def _callafter(self, data):
CallAfter(Publisher.sendMessage, self.PUBLISHER_TOPIC, data) CallAfter(Publisher.sendMessage, self.PUBLISHER_TOPIC, data)
def close(self, kill=False):
self.kill = kill
self.procNo = 0
self.running = False
self._callafter('close')
class DownloadThread(Thread): class DownloadThread(Thread):
''' '''
Params Params
url: URL to download. url: URL to download.
index: ListCtrl index for the current DownloadThread. index: ListCtrl index for the current DownloadThread.
opt_manager: OptionsHandler.OptionsHandler object. opt_manager: OptionsHandler.OptionsHandler object.
log_manager: Any logger which implements log(). log_manager: Any logger which implements log().
Accessible Methods Accessible Methods
close() close()
Params: None Params: None
''' '''
PUBLISHER_TOPIC = 'download_thread' PUBLISHER_TOPIC = 'download_thread'
def __init__(self, url, index, opt_manager, log_manager=None): def __init__(self, url, index, opt_manager, log_manager=None):
super(DownloadThread, self).__init__() super(DownloadThread, self).__init__()
self.log_manager = log_manager self.log_manager = log_manager
@ -129,28 +125,32 @@ class DownloadThread(Thread):
self.url = url self.url = url
self._dl_object = None self._dl_object = None
self.start() self.start()
def run(self): def run(self):
youtubedl_path = self._get_youtubedl_path() youtubedl_path = self._get_youtubedl_path()
options = YoutubeDLInterpreter(self.opt_manager).get_options()
options = OptionsParser(self.opt_manager).parse()
self._dl_object = DownloadObject(youtubedl_path, self._data_hook, self.log_manager) self._dl_object = DownloadObject(youtubedl_path, self._data_hook, self.log_manager)
success = self._dl_object.download(self.url, options)
return_code = self._dl_object.download(self.url, options)
if self.opt_manager.options['clear_dash_files']: if self.opt_manager.options['clear_dash_files']:
self._clear_dash() self._clear_dash()
if success:
if return_code == self._dl_object.OK:
self._callafter(self._get_status_pack('Finished')) self._callafter(self._get_status_pack('Finished'))
else:
elif return_code == self._dl_object.ERROR:
self._callafter(self._get_status_pack('Error')) self._callafter(self._get_status_pack('Error'))
elif return_code == self._dl_object.ALREADY:
self._callafter(self._get_status_pack('Already-Downloaded'))
def close(self): def close(self):
if self._dl_object is not None: if self._dl_object is not None:
self._callafter(self._get_status_pack('Stopping')) self._callafter(self._get_status_pack('Stopping'))
self._dl_object.stop() self._dl_object.stop()
def _clear_dash(self): def _clear_dash(self):
''' Clear DASH files after ffmpeg mux ''' ''' Clear DASH files after ffmpeg mux '''
for fl in self._dl_object.files_list: for fl in self._dl_object.files_list:
@ -165,28 +165,30 @@ class DownloadThread(Thread):
def _callafter(self, data): def _callafter(self, data):
CallAfter(Publisher.sendMessage, self.PUBLISHER_TOPIC, data) CallAfter(Publisher.sendMessage, self.PUBLISHER_TOPIC, data)
def _get_status_pack(self, message): def _get_status_pack(self, message):
''' Return simple status pack ''' ''' Return simple status pack '''
data = {'index': self.index, 'status': message} data = {'index': self.index, 'status': message}
return data return data
def _get_status(self, data): def _get_status(self, data):
''' Return download process status ''' ''' Return download process status '''
if data['playlist_index'] is not None: if data['playlist_index'] is not None:
playlist_info = '%s/%s' % (data['playlist_index'], data['playlist_size']) playlist_info = '%s/%s' % (data['playlist_index'], data['playlist_size'])
else: else:
playlist_info = '' playlist_info = ''
if data['status'] == 'pre_process': if data['status'] == 'pre_process':
msg = 'Pre-Processing %s' % playlist_info msg = 'Pre-Processing %s' % playlist_info
elif data['status'] == 'download': elif data['status'] == 'download':
msg = 'Downloading %s' % playlist_info msg = 'Downloading %s' % playlist_info
elif data['status'] == 'post_process': elif data['status'] == 'post_process':
msg = 'Post-Processing %s' % playlist_info msg = 'Post-Processing %s' % playlist_info
else:
msg = ''
return msg return msg
def _get_youtubedl_path(self): def _get_youtubedl_path(self):
''' Retrieve youtube-dl path ''' ''' Retrieve youtube-dl path '''
path = self.opt_manager.options['youtubedl_path'] path = self.opt_manager.options['youtubedl_path']

70
youtube_dl_gui/LogManager.py

@ -4,59 +4,65 @@ import wx
from time import strftime from time import strftime
from .Utils import ( from .Utils import (
fix_path,
get_filesize,
check_path,
file_exist, file_exist,
get_filesize
fix_path
) )
LOG_FILENAME = 'log'
LOG_FILESIZE = 524288 # 524288B = 512kB
class LogManager():
class LogManager(object):
def __init__(self, path, add_time=False):
self.path = fix_path(path) + LOG_FILENAME
self.add_time = add_time
self.init_log()
self.auto_clear_log()
def auto_clear_log(self):
if self.size() > LOG_FILESIZE:
self.clear()
LOG_FILENAME = 'log'
MAX_FILESIZE = 524288 # 524288B = 512kB
def init_log(self):
if not file_exist(self.path):
self.clear()
def __init__(self, config_path, add_time=False):
self.config_path = config_path
self.add_time = add_time
self.log_file = self._get_log_file()
self._auto_clear_log()
def size(self): def size(self):
return get_filesize(self.path)
if not file_exist(self.log_file):
return 0
return get_filesize(self.log_file)
def clear(self): def clear(self):
with open(self.path, 'w') as fl:
fl.write('')
self._write('', 'w')
def log(self, data): def log(self, data):
self.write(data)
def write(self, data):
with open(self.path, 'a') as fl:
self._write(data, 'a')
def _write(self, data, mode):
check_path(self.config_path)
with open(self.log_file, mode) as fl:
if self.add_time: if self.add_time:
t = '[%s] ' % strftime('%c') t = '[%s] ' % strftime('%c')
fl.write(t) fl.write(t)
fl.write(data)
fl.write('\n')
fl.write(data + '\n')
def _auto_clear_log(self):
if self.size() > self.MAX_FILESIZE:
self.clear()
def _get_log_file(self):
return fix_path(self.config_path) + self.LOG_FILENAME
class LogGUI(wx.Frame): class LogGUI(wx.Frame):
title = 'Log Viewer'
TITLE = 'Log Viewer'
def __init__(self, path, parent=None, id=-1):
wx.Frame.__init__(self, parent, id, self.title, size=(650, 200))
def __init__(self, log_file, parent=None, id=-1):
wx.Frame.__init__(self, parent, id, self.TITLE, size=(650, 200))
panel = wx.Panel(self) panel = wx.Panel(self)
textArea = wx.TextCtrl(panel, -1, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL)
text_area = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL)
sizer = wx.BoxSizer() sizer = wx.BoxSizer()
sizer.Add(textArea, 1, wx.EXPAND)
sizer.Add(text_area, 1, wx.EXPAND)
panel.SetSizerAndFit(sizer) panel.SetSizerAndFit(sizer)
textArea.LoadFile(path)
text_area.LoadFile(log_file)

174
youtube_dl_gui/OptionsHandler.py

@ -2,110 +2,112 @@
import json import json
from data import __appname__
from .Utils import ( from .Utils import (
get_user_config_path,
get_HOME,
check_path,
file_exist, file_exist,
fix_path,
check_path
get_home,
fix_path
) )
SETTINGS_FILENAME = 'settings.json'
CONFIG_PATH = fix_path(get_user_config_path()) + __appname__.lower()
class OptionsHandler():
settings_abs_path = ''
sensitive_keys = ('sudo_password', 'password', 'video_password')
class OptionsHandler(object):
def __init__(self):
self.set_settings_path()
self.load_settings()
SETTINGS_FILENAME = 'settings.json'
SENSITIVE_KEYS = ('sudo_password', 'password', 'video_password')
def load_settings(self):
def __init__(self, config_path):
self.config_path = config_path
self.settings_file = self._get_settings_file()
self.load_default() self.load_default()
check_path(self.get_config_path())
if file_exist(self.settings_abs_path):
self.load_from_file()
self.load_from_file()
def load_default(self): def load_default(self):
self.options = { self.options = {
'save_path': get_HOME(),
'video_format': 'default',
'dash_audio_format': 'none',
'clear_dash_files': False,
'to_audio': False,
'keep_video': False,
'audio_format': 'mp3',
'audio_quality': 'mid',
'restrict_filenames': False,
'output_format': 'title',
'output_template': '%(uploader)s/%(title)s.%(ext)s',
'playlist_start': 1,
'playlist_end': 0,
'max_downloads': 0,
'min_filesize': '0',
'max_filesize': '0',
'write_subs': False,
'write_all_subs': False,
'write_auto_subs': False,
'embed_subs': False,
'subs_lang': 'English',
'ignore_errors': True,
'open_dl_dir': False,
'write_description': False,
'write_info': False,
'write_thumbnail': False,
'retries': 10,
'user_agent': '',
'referer': '',
'proxy': '',
'shutdown': False,
'sudo_password': '',
'username': '',
'password': '',
'video_password': '',
'youtubedl_path': self.get_config_path(),
'cmd_args': '',
'enable_log': True,
'log_time': True
'save_path': get_home(),
'video_format': 'default',
'dash_audio_format': 'none',
'clear_dash_files': False,
'to_audio': False,
'keep_video': False,
'audio_format': 'mp3',
'audio_quality': 'mid',
'restrict_filenames': False,
'output_format': 'title',
'output_template': '%(uploader)s/%(title)s.%(ext)s',
'playlist_start': 1,
'playlist_end': 0,
'max_downloads': 0,
'min_filesize': '0',
'max_filesize': '0',
'write_subs': False,
'write_all_subs': False,
'write_auto_subs': False,
'embed_subs': False,
'subs_lang': 'English',
'ignore_errors': True,
'open_dl_dir': True,
'write_description': False,
'write_info': False,
'write_thumbnail': False,
'retries': 10,
'user_agent': '',
'referer': '',
'proxy': '',
'shutdown': False,
'sudo_password': '',
'username': '',
'password': '',
'video_password': '',
'youtubedl_path': self.config_path,
'cmd_args': '',
'enable_log': True,
'log_time': False
} }
def get_config_path(self):
return CONFIG_PATH
def set_settings_path(self):
self.settings_abs_path = fix_path(self.get_config_path()) + SETTINGS_FILENAME
def settings_are_valid(self, settings_dictionary):
''' Check settings.json dictionary '''
for key in self.options:
if key not in settings_dictionary:
return False
if len(settings_dictionary) != len(self.options):
return False
return True
def remove_sensitive_data(self):
for key in self.sensitive_keys:
self.options[key] = ''
def load_from_file(self): def load_from_file(self):
with open(self.settings_abs_path, 'rb') as f:
if not file_exist(self.settings_file):
self.load_default()
return
with open(self.settings_file, 'rb') as f:
try: try:
options = json.load(f) options = json.load(f)
if self.settings_are_valid(options):
self.options = options
else:
self.load_default()
# Raise WrongSettings Exception if NOT
self._settings_are_valid(options)
self.options = options
except: except:
self.load_default() self.load_default()
def save_to_file(self): def save_to_file(self):
check_path(self.get_config_path())
with open(self.settings_abs_path, 'wb') as f:
self.remove_sensitive_data()
check_path(self.config_path)
with open(self.settings_file, 'wb') as f:
self._remove_sensitive_data()
json.dump(self.options, f, indent=4, separators=(',', ': ')) json.dump(self.options, f, indent=4, separators=(',', ': '))
def _settings_are_valid(self, settings_dictionary):
''' Check settings.json dictionary and raise WrongSettings Exception '''
if len(settings_dictionary) != len(self.options):
raise WrongSettings()
for key in self.options:
if key not in settings_dictionary:
raise WrongSettings()
def _remove_sensitive_data(self):
''' Remove sensitive data from self.options (passwords, etc) '''
for key in self.SENSITIVE_KEYS:
self.options[key] = ''
def _get_settings_file(self):
''' Return abs path to settings file '''
return fix_path(self.config_path) + self.SETTINGS_FILENAME
class WrongSettings(Exception):
''' Wrong settings exception.
This exception will be raised if settings dictionary is not valid.
'''
pass

47
youtube_dl_gui/UpdateThread.py

@ -8,37 +8,44 @@ from threading import Thread
from urllib2 import urlopen, URLError, HTTPError from urllib2 import urlopen, URLError, HTTPError
from .Utils import ( from .Utils import (
fix_path,
file_exist,
makedir
get_youtubedl_filename,
check_path,
fix_path
) )
LATEST_YOUTUBE_DL = 'https://yt-dl.org/latest/'
PUBLISHER_TOPIC = 'update'
DOWNLOAD_TIMEOUT = 20
class UpdateThread(Thread): class UpdateThread(Thread):
def __init__(self, updatePath, youtubeDLFile):
LATEST_YOUTUBE_DL = 'https://yt-dl.org/latest/'
PUBLISHER_TOPIC = 'update'
DOWNLOAD_TIMEOUT = 20
def __init__(self, download_path):
super(UpdateThread, self).__init__() super(UpdateThread, self).__init__()
self.youtubeDLFile = youtubeDLFile
self.updatePath = fix_path(updatePath)
self.url = LATEST_YOUTUBE_DL + youtubeDLFile
self.check_path()
self.download_path = fix_path(download_path)
self._youtubedl_file = get_youtubedl_filename()
self.start() self.start()
def run(self): def run(self):
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, "Downloading latest youtube-dl. Please wait...")
self._callafter("Downloading latest youtube-dl. Please wait...")
dl_url = self.LATEST_YOUTUBE_DL + self._youtubedl_file
dst_file = self.download_path + self._youtubedl_file
check_path(self.download_path)
try: try:
f = urlopen(self.url, timeout=DOWNLOAD_TIMEOUT)
with open(self.updatePath + self.youtubeDLFile, 'wb') as lf:
lf.write(f.read())
f = urlopen(dl_url, timeout=self.DOWNLOAD_TIMEOUT)
with open(dst_file, 'wb') as bf:
bf.write(f.read())
msg = 'Youtube-dl downloaded correctly' msg = 'Youtube-dl downloaded correctly'
except (HTTPError, URLError, IOError) as e: except (HTTPError, URLError, IOError) as e:
msg = 'Youtube-dl download failed ' + str(e) msg = 'Youtube-dl download failed ' + str(e)
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, msg)
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, 'finish')
def check_path(self):
if not file_exist(self.updatePath):
makedir(self.updatePath)
self._callafter(msg)
self._callafter('finish')
def _callafter(self, data):
CallAfter(Publisher.sendMessage, self.PUBLISHER_TOPIC, data)

105
youtube_dl_gui/Utils.py

@ -12,68 +12,74 @@ from os import (
) )
from os.path import ( from os.path import (
exists as file_exist,
getsize as get_filesize
getsize as get_filesize,
exists as file_exist
) )
def remove_empty_items(array):
return [x for x in array if x != '']
def remove_spaces(string):
return string.replace(' ', '')
def string_to_array(string, char=' '):
return string.split(char)
def preferredencoding():
try:
pref = locale.getpreferredencoding()
u'TEST'.encode(pref)
except:
pref = 'UTF-8'
return pref
def get_encoding(): def get_encoding():
if sys.version_info >= (3, 0): if sys.version_info >= (3, 0):
return None return None
if sys.platform == 'win32': if sys.platform == 'win32':
return preferredencoding()
try:
enc = locale.getpreferredencoding()
u'TEST'.encode(enc)
except:
enc = 'UTF-8'
return enc
return None return None
def encode_list(data_list, encoding):
return [x.encode(encoding, 'ignore') for x in data_list]
def encode_list(lst, encoding):
return [item.encode(encoding, 'ignore') for item in lst]
def video_is_dash(video): def video_is_dash(video):
return "DASH" in video return "DASH" in video
def have_dash_audio(audio):
def audio_is_dash(audio):
return audio != "none" return audio != "none"
def get_path_seperator():
def path_seperator():
''' Return path seperator for current OS '''
return '\\' if os_type == 'nt' else '/' return '\\' if os_type == 'nt' else '/'
def fix_path(path): def fix_path(path):
if path != '' and path[-1:] != get_path_seperator():
path += get_path_seperator()
path_list = path.split(get_path_seperator())
for i in range(len(path_list)):
if path_list[i] == '~':
path_list[i] = get_HOME()
return get_path_seperator().join(path_list)
def get_HOME():
''' Add path seperator at the end of the path
if not exist and replace ~ with user $HOME '''
if path == '':
return path
if path[-1:] != path_seperator():
path += path_seperator()
path_list = path.split(path_seperator())
for index, item in enumerate(path_list):
if item == '~':
path_list[index] = get_home()
path = path_seperator().join(path_list)
return path
def get_home():
return os.path.expanduser("~") return os.path.expanduser("~")
def add_PATH(path):
os.environ["PATH"] += os.pathsep + path
def abs_path(filename): def abs_path(filename):
path = os.path.abspath(filename).split(get_path_seperator())
path = os.path.realpath(os.path.abspath(filename))
path = path.split(path_seperator())
path.pop() path.pop()
return get_path_seperator().join(path)
return path_seperator().join(path)
def get_filename(path): def get_filename(path):
return path.split(get_path_seperator())[-1]
return path.split(path_seperator())[-1]
def open_dir(path): def open_dir(path):
if os_type == 'nt': if os_type == 'nt':
@ -81,23 +87,27 @@ def open_dir(path):
else: else:
subprocess.call(('xdg-open', path)) subprocess.call(('xdg-open', path))
def check_path(path): def check_path(path):
if not file_exist(path): if not file_exist(path):
makedir(path) makedir(path)
def get_youtubedl_filename(): def get_youtubedl_filename():
youtubedl_fl = 'youtube-dl' youtubedl_fl = 'youtube-dl'
if os_type == 'nt': if os_type == 'nt':
youtubedl_fl += '.exe' youtubedl_fl += '.exe'
return youtubedl_fl return youtubedl_fl
def get_user_config_path(): def get_user_config_path():
if os_type == 'nt': if os_type == 'nt':
path = os.getenv('APPDATA') path = os.getenv('APPDATA')
else: else:
path = fix_path(get_HOME()) + '.config'
path = fix_path(get_home()) + '.config'
return path return path
def shutdown_sys(password=''): def shutdown_sys(password=''):
if os_type == 'nt': if os_type == 'nt':
subprocess.call(['shutdown', '/s', '/t', '1']) subprocess.call(['shutdown', '/s', '/t', '1'])
@ -105,7 +115,8 @@ def shutdown_sys(password=''):
if password == '': if password == '':
subprocess.call(['/sbin/shutdown', '-h', 'now']) subprocess.call(['/sbin/shutdown', '-h', 'now'])
else: else:
p = subprocess.Popen(['sudo', '-S', '/sbin/shutdown', '-h', 'now'],
stdin=subprocess.PIPE)
p.communicate(password+'\n')
p = subprocess.Popen(
['sudo', '-S', '/sbin/shutdown', '-h', 'now'],
stdin=subprocess.PIPE
)
p.communicate(password + '\n')

175
youtube_dl_gui/YDLOptionsParser.py

@ -0,0 +1,175 @@
#! /usr/bin/env python
from .Utils import (
video_is_dash,
fix_path
)
SUBS_LANG = {
"English": "en",
"Greek": "gr",
"Portuguese": "pt",
"French": "fr",
"Italian": "it",
"Russian": "ru",
"Spanish": "es",
"German": "de"
}
VIDEO_FORMATS = {
"default": "0",
"mp4 [1280x720]": "22",
"mp4 [640x360]": "18",
"webm [640x360]": "43",
"flv [400x240]": "5",
"3gp [320x240]": "36",
"mp4 1080p(DASH)": "137",
"mp4 720p(DASH)": "136",
"mp4 480p(DASH)": "135",
"mp4 360p(DASH)": "134"
}
DASH_AUDIO_FORMATS = {
"none": "none",
"DASH m4a audio 128k": "140",
"DASH webm audio 48k": "171"
}
AUDIO_QUALITY = {
"high": "0",
"mid": "5",
"low": "9"
}
class OptionsParser():
''' Parse OptionsHandler object into youtube-dl options list '''
def __init__(self, opt_manager):
self._options = opt_manager.options
self.opts = []
def parse(self):
self._set_progress_opts()
self._set_output_opts()
self._set_auth_opts()
self._set_connection_opts()
self._set_video_opts()
self._set_playlist_opts()
self._set_filesystem_opts()
self._set_subtitles_opts()
self._set_audio_opts()
self._set_other_opts()
return self.opts
def _set_progress_opts(self):
''' Do NOT change this option '''
self.opts.append('--newline')
def _set_playlist_opts(self):
if self._options['playlist_start'] != 1:
self.opts.append('--playlist-start')
self.opts.append(str(self._options['playlist_start']))
if self._options['playlist_end'] != 0:
self.opts.append('--playlist-end')
self.opts.append(str(self._options['playlist_end']))
if self._options['max_downloads'] != 0:
self.opts.append('--max-downloads')
self.opts.append(str(self._options['max_downloads']))
if self._options['min_filesize'] != '0':
self.opts.append('--min-filesize')
self.opts.append(self._options['min_filesize'])
if self._options['max_filesize'] != '0':
self.opts.append('--max-filesize')
self.opts.append(self._options['max_filesize'])
def _set_auth_opts(self):
if self._options['username'] != '':
self.opts.append('-u')
self.opts.append(self._options['username'])
if self._options['password'] != '':
self.opts.append('-p')
self.opts.append(self._options['password'])
if self._options['video_password'] != '':
self.opts.append('--video-password')
self.opts.append(self._options['video_password'])
def _set_connection_opts(self):
if self._options['retries'] != 10:
self.opts.append('-R')
self.opts.append(str(self._options['retries']))
if self._options['proxy'] != '':
self.opts.append('--proxy')
self.opts.append(self._options['proxy'])
if self._options['user_agent'] != '':
self.opts.append('--user-agent')
self.opts.append(self._options['user_agent'])
if self._options['referer'] != '':
self.opts.append('--referer')
self.opts.append(self._options['referer'])
def _set_video_opts(self):
if self._options['video_format'] != 'default':
self.opts.append('-f')
if video_is_dash(self._options['video_format']):
vf = VIDEO_FORMATS[self._options['video_format']]
af = DASH_AUDIO_FORMATS[self._options['dash_audio_format']]
if af != 'none':
self.opts.append(vf + '+' + af)
else:
self.opts.append(vf)
else:
self.opts.append(VIDEO_FORMATS[self._options['video_format']])
def _set_filesystem_opts(self):
if self._options['ignore_errors']:
self.opts.append('-i')
if self._options['write_description']:
self.opts.append('--write-description')
if self._options['write_info']:
self.opts.append('--write-info-json')
if self._options['write_thumbnail']:
self.opts.append('--write-thumbnail')
def _set_subtitles_opts(self):
if self._options['write_all_subs']:
self.opts.append('--all-subs')
if self._options['write_auto_subs']:
self.opts.append('--write-auto-sub')
if self._options['write_subs']:
self.opts.append('--write-sub')
if self._options['subs_lang'] != 'English':
self.opts.append('--sub-lang')
self.opts.append(SUBS_LANG[self._options['subs_lang']])
if self._options['embed_subs']:
self.opts.append('--embed-subs')
def _set_output_opts(self):
path = fix_path(self._options['save_path'])
self.opts.append('-o')
if self._options['output_format'] == 'id':
self.opts.append(path + '%(id)s.%(ext)s')
elif self._options['output_format'] == 'title':
self.opts.append(path + '%(title)s.%(ext)s')
elif self._options['output_format'] == 'custom':
self.opts.append(path + self._options['output_template'])
if self._options['restrict_filenames']:
self.opts.append('--restrict-filenames')
def _set_audio_opts(self):
if self._options['to_audio']:
self.opts.append('-x')
self.opts.append('--audio-format')
self.opts.append(self._options['audio_format'])
if self._options['audio_quality'] != 'mid':
self.opts.append('--audio-quality')
self.opts.append(AUDIO_QUALITY[self._options['audio_quality']])
if self._options['keep_video']:
self.opts.append('-k')
def _set_other_opts(self):
if self._options['cmd_args'] != '':
for option in self._options['cmd_args'].split():
self.opts.append(option)

1653
youtube_dl_gui/YoutubeDLGUI.py
File diff suppressed because it is too large
View File

163
youtube_dl_gui/YoutubeDLInterpreter.py

@ -1,163 +0,0 @@
#! /usr/bin/env python
from .Utils import (
video_is_dash,
fix_path
)
LANGUAGES = {"English":"en",
"Greek":"gr",
"Portuguese":"pt",
"French":"fr",
"Italian":"it",
"Russian":"ru",
"Spanish":"es",
"German":"de"}
VIDEOFORMATS = {"default":"0",
"mp4 [1280x720]":"22",
"mp4 [640x360]":"18",
"webm [640x360]":"43",
"flv [400x240]":"5",
"3gp [320x240]":"36",
"mp4 1080p(DASH)":"137",
"mp4 720p(DASH)":"136",
"mp4 480p(DASH)":"135",
"mp4 360p(DASH)":"134"}
DASH_AUDIO_FORMATS = {"none":"none",
"DASH m4a audio 128k":"140",
"DASH webm audio 48k":"171"}
AUDIO_Q = {"high":"0",
"mid":"5",
"low":"9"}
class YoutubeDLInterpreter():
def __init__(self, optManager):
self.optManager = optManager
self.opts = []
self.set_progress_opts()
self.set_output_opts()
self.set_auth_opts()
self.set_connection_opts()
self.set_video_opts()
self.set_playlist_opts()
self.set_filesystem_opts()
self.set_subtitles_opts()
self.set_audio_opts()
self.set_other_opts()
def get_options(self):
return self.opts
def set_progress_opts(self):
''' Do NOT change this option '''
self.opts.append('--newline')
def set_playlist_opts(self):
if self.optManager.options['playlist_start'] != 1:
self.opts.append('--playlist-start')
self.opts.append(str(self.optManager.options['playlist_start']))
if self.optManager.options['playlist_end'] != 0:
self.opts.append('--playlist-end')
self.opts.append(str(self.optManager.options['playlist_end']))
if self.optManager.options['max_downloads'] != 0:
self.opts.append('--max-downloads')
self.opts.append(str(self.optManager.options['max_downloads']))
if self.optManager.options['min_filesize'] != '0':
self.opts.append('--min-filesize')
self.opts.append(self.optManager.options['min_filesize'])
if self.optManager.options['max_filesize'] != '0':
self.opts.append('--max-filesize')
self.opts.append(self.optManager.options['max_filesize'])
def set_auth_opts(self):
if self.optManager.options['username'] != '':
self.opts.append('-u')
self.opts.append(self.optManager.options['username'])
if self.optManager.options['password'] != '':
self.opts.append('-p')
self.opts.append(self.optManager.options['password'])
if self.optManager.options['video_password'] != '':
self.opts.append('--video-password')
self.opts.append(self.optManager.options['video_password'])
def set_connection_opts(self):
if self.optManager.options['retries'] != 10:
self.opts.append('-R')
self.opts.append(str(self.optManager.options['retries']))
if self.optManager.options['proxy'] != '':
self.opts.append('--proxy')
self.opts.append(self.optManager.options['proxy'])
if self.optManager.options['user_agent'] != '':
self.opts.append('--user-agent')
self.opts.append(self.optManager.options['user_agent'])
if self.optManager.options['referer'] != '':
self.opts.append('--referer')
self.opts.append(self.optManager.options['referer'])
def set_video_opts(self):
if self.optManager.options['video_format'] != 'default':
self.opts.append('-f')
if video_is_dash(self.optManager.options['video_format']):
vf = VIDEOFORMATS[self.optManager.options['video_format']]
af = DASH_AUDIO_FORMATS[self.optManager.options['dash_audio_format']]
if af != 'none':
self.opts.append(vf+'+'+af)
else:
self.opts.append(vf)
else:
self.opts.append(VIDEOFORMATS[self.optManager.options['video_format']])
def set_filesystem_opts(self):
if self.optManager.options['ignore_errors']:
self.opts.append('-i')
if self.optManager.options['write_description']:
self.opts.append('--write-description')
if self.optManager.options['write_info']:
self.opts.append('--write-info-json')
if self.optManager.options['write_thumbnail']:
self.opts.append('--write-thumbnail')
def set_subtitles_opts(self):
if self.optManager.options['write_all_subs']:
self.opts.append('--all-subs')
if self.optManager.options['write_auto_subs']:
self.opts.append('--write-auto-sub')
if self.optManager.options['write_subs']:
self.opts.append('--write-sub')
if self.optManager.options['subs_lang'] != 'English':
self.opts.append('--sub-lang')
self.opts.append(LANGUAGES[self.optManager.options['subs_lang']])
if self.optManager.options['embed_subs']:
self.opts.append('--embed-subs')
def set_output_opts(self):
path = fix_path(self.optManager.options['save_path'])
self.opts.append('-o')
if self.optManager.options['output_format'] == 'id':
self.opts.append(path + '%(id)s.%(ext)s')
elif self.optManager.options['output_format'] == 'title':
self.opts.append(path + '%(title)s.%(ext)s')
elif self.optManager.options['output_format'] == 'custom':
self.opts.append(path + self.optManager.options['output_template'])
if self.optManager.options['restrict_filenames']:
self.opts.append('--restrict-filenames')
def set_audio_opts(self):
if self.optManager.options['to_audio']:
self.opts.append('-x')
self.opts.append('--audio-format')
self.opts.append(self.optManager.options['audio_format'])
if self.optManager.options['audio_quality'] != 'mid':
self.opts.append('--audio-quality')
self.opts.append(AUDIO_Q[self.optManager.options['audio_quality']])
if self.optManager.options['keep_video']:
self.opts.append('-k')
def set_other_opts(self):
if self.optManager.options['cmd_args'] != '':
for option in self.optManager.options['cmd_args'].split():
self.opts.append(option)

6
youtube_dl_gui/__init__.py

@ -4,9 +4,8 @@ from sys import exit
try: try:
import wx import wx
except ImportError, e:
print '[ERROR]', e
print 'Please install latest wx.Python'
except ImportError as e:
print e
exit(1) exit(1)
from .YoutubeDLGUI import MainFrame from .YoutubeDLGUI import MainFrame
@ -22,6 +21,7 @@ from .data import (
__licensefull__ __licensefull__
) )
def main(): def main():
app = wx.App() app = wx.App()
frame = MainFrame() frame = MainFrame()

1
youtube_dl_gui/data.py

@ -1,6 +1,7 @@
__author__ = 'Sotiris Papadopoulos' __author__ = 'Sotiris Papadopoulos'
__contact__ = 'ytubedlg@gmail.com' __contact__ = 'ytubedlg@gmail.com'
__projecturl__ = 'http://mrs0m30n3.github.io/youtube-dl-gui/' __projecturl__ = 'http://mrs0m30n3.github.io/youtube-dl-gui/'
__appname__ = 'Youtube-DLG' __appname__ = 'Youtube-DLG'
__license__ = 'UNLICENSE' __license__ = 'UNLICENSE'

Loading…
Cancel
Save