diff -Nru youtube-dlg-0.3+git20140327/debian/changelog youtube-dlg-0.3.5/debian/changelog --- youtube-dlg-0.3+git20140327/debian/changelog 2014-03-27 16:05:46.000000000 +0000 +++ youtube-dlg-0.3.5/debian/changelog 2014-04-07 14:19:28.000000000 +0000 @@ -1,4 +1,11 @@ -youtube-dlg (0.3+git20140327-1~webupd8~precise) precise; urgency=medium +youtube-dlg (0.3.5-1~webupd8~precise1) precise; urgency=medium + + * new upstream release + * refreshed 00_remove_icon.patch + + -- Alin Andrei Mon, 07 Apr 2014 15:58:04 +0200 + +youtube-dlg (0.3+git20140327-1~webupd8~trusty) trusty; urgency=medium * git snapshot diff -Nru youtube-dlg-0.3+git20140327/debian/patches/00_remove_icon.patch youtube-dlg-0.3.5/debian/patches/00_remove_icon.patch --- youtube-dlg-0.3+git20140327/debian/patches/00_remove_icon.patch 2014-03-24 11:11:33.000000000 +0000 +++ youtube-dlg-0.3.5/debian/patches/00_remove_icon.patch 2014-04-07 13:13:14.000000000 +0000 @@ -3,22 +3,23 @@ =================================================================== --- youtube-dl-gui-0.2.2.orig/youtube_dl_gui/YoutubeDLGUI.py +++ youtube-dl-gui-0.2.2/youtube_dl_gui/YoutubeDLGUI.py -@@ -83,10 +83,6 @@ +@@ -91,10 +91,6 @@ self.Bind(wx.EVT_BUTTON, self.OnOptions, self.optionsButton) self.Bind(wx.EVT_TEXT, self.OnTrackListChange, self.trackList) self.Bind(wx.EVT_CLOSE, self.OnClose) - - # set app icon -- icon = wx.Icon('../icons/ytube.png', wx.BITMAP_TYPE_ICO) +- icon = wx.Icon(ICON, wx.BITMAP_TYPE_ICO) - self.SetIcon(icon) # set publisher for update thread Publisher.subscribe(self.update_handler, "update") -@@ -538,7 +534,6 @@ +@@ -1153,7 +1149,6 @@ info = wx.AboutDialogInfo() -- info.SetIcon(wx.Icon('../icons/ytube.png', wx.BITMAP_TYPE_ICO)) +- info.SetIcon(wx.Icon(ICON, wx.BITMAP_TYPE_ICO)) info.SetName(TITLE) info.SetVersion(__version__) info.SetDescription(description) + diff -Nru youtube-dlg-0.3+git20140327/README.md youtube-dlg-0.3.5/README.md --- youtube-dlg-0.3+git20140327/README.md 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/README.md 2014-04-07 12:59:32.000000000 +0000 @@ -6,9 +6,9 @@ written in wxPython. ## SCREENSHOT -![Youtube-dl-gui screenshot in slackware](http://i.imgur.com/JiT6eZW.png) +![Youtube-dl-gui main window](http://i.imgur.com/I4oXPWs.png) -![Options window](http://i.imgur.com/992tVdK.png) +![Options window](http://i.imgur.com/eShdoLD.png) ## WINDOWS_SETUP http://code.google.com/p/youtube-dlg/ @@ -29,8 +29,11 @@ ## AUTHOR -Sotiris Papadopoulos -https://twitter.com/MrS0m30n3 +Sotiris Papadopoulos + +email: + +twitter: https://twitter.com/MrS0m30n3 ## THANKS diff -Nru youtube-dlg-0.3+git20140327/SIGNALS.txt youtube-dlg-0.3.5/SIGNALS.txt --- youtube-dlg-0.3+git20140327/SIGNALS.txt 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/SIGNALS.txt 2014-04-07 12:59:32.000000000 +0000 @@ -1,5 +1,6 @@ -Signals list, that DownloadManager thread sends (DownloadThread.py) +Signals list that DownloadManager, ProcessWrapper +threads send (DownloadThread.py) HANDLER ======= @@ -12,39 +13,39 @@ . . -SIGNALS -======= - -['finish', -1]: All downloads have finished - -['finish', index]: Download of url in index has finished - -['close', -1]: Closing down DownloadManager thread - -['close', index]: Closing down url in index - -['error', index]: Error occured url, index - -['playlist', data, index]: Playlist data for url, index - -['[youtube]', index]: Pre-Processing for url, index - -['[download]', data, index]: Downloading url, index - -['[ffmpeg]', index]: Post-Processing for url, index - -['ignore', index]: Do nothing - -['remove', index]: Removing dash audio/videos files, index - -EXAMPLES -======== - -['[youtube]', 'Setting', 'language', 0] - -['[youtube]', 'RBumgq5yVrA:', 'Downloading', 'webpage', 0] - -['[download]', '0.0%', 'of', '4.42MiB', 'at', '24.48KiB/s', 'ETA', '03:04', 0] +HEADER +====== -['[download]', '0.1%', 'of', '4.42MiB', 'at', '63.81KiB/s', 'ETA', '01:10', 0] +[ +'finish', Download thread finished +'close', Download thread stopped by the user +'error', Error occured while downloading +'playlist', Playlist current download no/from +'youtube', Pre-Processing +'download', Download stuff [size, percent, eta, speed] +'ffmpeg', Post-Processing +'ignore', Ignore this header +'remove', Removing extra DASH files +'filename', Extract filenames +] + + +INDEX +===== + +index == -1: GLOBAL +index != -1: URL IN COLUMN + +DATA +==== + +e.g. +['57.3%', '20.63MiB', '542.44KiB/s', '00:16'] + +DATA-PACK +========= + +DataPack.header = header +DataPack.index = index +DataPack.data = data diff -Nru youtube-dlg-0.3+git20140327/ToDo youtube-dlg-0.3.5/ToDo --- youtube-dlg-0.3+git20140327/ToDo 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/ToDo 2014-04-07 12:59:32.000000000 +0000 @@ -1,3 +1,2 @@ -* Change Options Frame * Use import instead of subprocess - +* New layout for MainFrame diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/DownloadThread.py youtube-dlg-0.3.5/youtube_dl_gui/DownloadThread.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/DownloadThread.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/DownloadThread.py 2014-04-07 12:59:32.000000000 +0000 @@ -2,18 +2,23 @@ import subprocess from time import sleep -from wx import CallAfter from threading import Thread + +from wx import CallAfter from wx.lib.pubsub import setuparg1 from wx.lib.pubsub import pub as Publisher +from .OutputHandler import ( + DataPack, + OutputFormatter +) + from .Utils import ( - remove_spaces, - string_to_array, get_encoding, encode_list, remove_file, - get_os_type + get_os_type, + file_exist ) MAX_DOWNLOAD_THREADS = 3 @@ -21,10 +26,11 @@ class DownloadManager(Thread): - def __init__(self, options, downloadlist, clear_dash_files): + def __init__(self, options, downloadlist, clear_dash_files, logmanager=None): super(DownloadManager, self).__init__() self.clear_dash_files = clear_dash_files self.downloadlist = downloadlist + self.logmanager = logmanager self.options = options self.running = True self.procList = [] @@ -38,10 +44,11 @@ url, index = self.extract_data() # Wait for your turn if there are not more positions in 'queue' while self.procNo >= MAX_DOWNLOAD_THREADS: - proc = self.check_queue(0.5) + 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( @@ -49,7 +56,8 @@ self.options, url, index, - self.clear_dash_files + self.clear_dash_files, + self.logmanager ) ) self.procNo += 1 @@ -58,13 +66,18 @@ if not self.downloading(): self.running = False else: - sleep(1) - + sleep(0.1) # If we reach here close down all child threads self.terminate_all() - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['finish', -1]) + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('finish')) - def add_download_item(self, downloadItem): + def downloading(self): + for proc in self.procList: + if proc.isAlive(): + return True + return False + + def _add_download_item(self, downloadItem): self.downloadlist.append(downloadItem) def extract_data(self): @@ -79,124 +92,104 @@ proc.close() proc.join() - def downloading(self): - for proc in self.procList: - if proc.isAlive(): - return True - return False - - def check_queue(self, t=1): + def check_queue(self): for proc in self.procList: if not self.running: break if not proc.isAlive(): return proc - sleep(t) return None def close(self): self.procNo = 0 self.running = False - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['close', -1]) + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('close')) class ProcessWrapper(Thread): - def __init__(self, options, url, index, clear_dash_files): + def __init__(self, options, url, index, clear_dash_files, log=None): super(ProcessWrapper, self).__init__() self.clear_dash_files = clear_dash_files self.options = options self.index = index + self.log = log self.url = url self.filenames = [] - self.proc = None self.stopped = False - self.err = False + self.error = False + self.proc = None self.start() def run(self): - self.proc = subprocess.Popen(self.get_cmd(), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - startupinfo=self.set_process_info()) + self.proc = self.create_process(self.get_cmd(), self.get_process_info()) # while subprocess is alive and NOT the current thread while self.proc_is_alive(): - # read output - output = self.read() - if output != '': - # process output - data = self.proc_output(output) - if self.err: - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['error', self.index]) - else: - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, data) - if not self.err and not self.stopped: - if self.clear_dash_files: - self.cleardash() - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['finish', self.index]) - - def extract_filename(self, data): - data_list = data.split(':') - if 'Destination' in data_list[0].split(): - self.filenames.append(data_list[1].lstrip()) - - def cleardash(self): - if self.filenames: - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['remove', self.index]) - for f in self.filenames: + # read stdout, stderr from proc + stdout, stderr = self.read() + if stdout != '': + # pass stdout to output formatter + data = OutputFormatter(stdout).get_data() + if self.clear_dash_files: self.add_file(data) + # add index to data pack + data.index = self.index + # send data back to caller + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, data) + if stderr != '': + self.error = True + self.write_to_log(stderr) + if not self.stopped: + if self.clear_dash_files: + self.clear_dash() + if not self.error: + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('finish', self.index)) + else: + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('error', self.index)) + + def add_file(self, dataPack): + if dataPack.header == 'filename': + self.filenames.append(dataPack.data) + + def write_to_log(self, data): + if self.log != None: + self.log.write(data) + + def clear_dash(self): + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('remove', self.index)) + for f in self.filenames: + if file_exist(f): remove_file(f) def close(self): self.proc.kill() self.stopped = True - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['close', self.index]) + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('close', self.index)) def proc_is_alive(self): return self.proc.poll() == None def read(self): - output = self.proc.stdout.readline() - if output == '': - output = self.proc.stderr.readline() - if output != '': - self.err = True - return output.rstrip() - - def proc_output(self, output): - if self.clear_dash_files: - self.extract_filename(output) - data = remove_spaces(string_to_array(output)) - data.append(self.index) - data = self.filter_data(data) - return data - - def filter_data(self, data): - ''' Filters data for output exceptions ''' - filter_list = ['Destination:', '100%', 'Resuming'] - if len(data) > 3: - if data[0] == '[download]': - if data[1] in filter_list or len(data[1]) > 11: - return ['ignore', self.index] - if data[1] == 'Downloading': - if data[2] == 'video': - return ['playlist', data[3], data[5], self.index] - else: - return ['ignore', self.index] - else: - if data[1] == 'UnicodeWarning:': - self.err = False - return ['ignore', self.index] - return data - + stdout = '' + stderr = '' + stdout = self.proc.stdout.readline() + if stdout == '': + stderr = self.proc.stderr.readline() + return stdout.rstrip(), stderr.rstrip() + + def create_process(self, cmd, info): + return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=info) + def get_cmd(self): enc = get_encoding() if enc != None: - data = encode_list(self.options + [self.url], enc) - return self.options + [self.url] + cmd = encode_list(self.options + [self.url], enc) + else: + cmd = self.options + [self.url] + return cmd - def set_process_info(self): + def get_process_info(self): if get_os_type() == 'nt': info = subprocess.STARTUPINFO() info.dwFlags |= subprocess.STARTF_USESHOWWINDOW return info else: return None - \ No newline at end of file + diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/LogManager.py youtube-dlg-0.3.5/youtube_dl_gui/LogManager.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/LogManager.py 1970-01-01 00:00:00.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/LogManager.py 2014-04-07 12:59:32.000000000 +0000 @@ -0,0 +1,60 @@ +#! /usr/bin/env python + +import wx + +from time import strftime +from .Utils import ( + fix_path, + file_exist, + get_filesize +) + +LOG_FILENAME = 'log' +LOG_FILESIZE = 524288 # 524288B = 512kB + +class LogManager(): + + 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() + + def init_log(self): + if not file_exist(self.path): + self.clear() + + def size(self): + return get_filesize(self.path) + + def clear(self): + with open(self.path, 'w') as fl: + fl.write('') + + def write(self, data): + with open(self.path, 'a') as fl: + if self.add_time: + t = '[%s] ' % strftime('%c') + fl.write(t) + fl.write(data) + fl.write('\n') + +class LogGUI(wx.Frame): + + title = 'Log Viewer' + + def __init__(self, path, parent=None, id=-1): + wx.Frame.__init__(self, parent, id, self.title, size=(650, 200)) + + panel = wx.Panel(self) + textArea = wx.TextCtrl(panel, -1, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL) + sizer = wx.BoxSizer() + sizer.Add(textArea, 1, wx.EXPAND) + panel.SetSizerAndFit(sizer) + + textArea.LoadFile(path) + diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/OptionsHandler.py youtube-dlg-0.3.5/youtube_dl_gui/OptionsHandler.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/OptionsHandler.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/OptionsHandler.py 2014-04-07 12:59:32.000000000 +0000 @@ -1,5 +1,6 @@ #! /usr/bin/env python +from .version import __version__ from .Utils import ( get_HOME, file_exist, @@ -19,6 +20,7 @@ def __init__(self, statusBarWrite): self.statusBarWrite = statusBarWrite self.set_settings_path() + self.load_default() self.load_settings() def load_settings(self): @@ -26,41 +28,45 @@ makedir(self.get_config_path()) if file_exist(self.settings_abs_path): self.load_from_file() - else: - self.load_default() def load_default(self): - self.ignoreErrors = True - self.idAsName = False + self.savePath = get_HOME() + self.videoFormat = "default" + self.dashAudioFormat = "NO SOUND" + self.clearDashFiles = False self.toAudio = False - self.audioFormat = "mp3" self.keepVideo = False - self.audioQuality = 5 - self.proxy = "" - self.savePath = get_HOME() - self.autoUpdate = False - self.videoFormat = "highest available" - self.userAgent = "" - self.referer = "" - self.username = "" - self.startTrack = "1" - self.endTrack = "0" - self.maxDownloads = "0" - self.rateLimit = "0" - self.retries = "10" - self.writeDescription = False - self.writeInfo = False - self.writeThumbnail = False + self.audioFormat = "mp3" + self.audioQuality = "mid" + self.outputFormat = "title" + self.outputTemplate = '%(uploader)s/%(title)s.%(ext)s' + self.startTrack = 1 + self.endTrack = 0 + self.maxDownloads = 0 self.minFileSize = "0" self.maxFileSize = "0" self.writeSubs = False self.writeAllSubs = False - self.subsLang = "English" self.writeAutoSubs = False - self.cmdArgs = "" - self.dashAudioFormat = "NO SOUND" - self.clearDashFiles = False + self.embedSubs = False + self.subsLang = "English" + self.openDownloadDir = False + self.ignoreErrors = True + self.writeDescription = False + self.writeInfo = False + self.writeThumbnail = False + self.retries = 10 + self.userAgent = "" + self.referer = "" + self.proxy = "" + self.username = "" + self.password = "" + self.videoPass = "" self.updatePath = self.get_config_path() + self.autoUpdate = False + self.cmdArgs = "" + self.enableLog = True + self.writeTimeToLog = True def get_config_path(self): if get_os_type() == 'nt': @@ -87,76 +93,99 @@ opts.append(opt[1].rstrip('\n')) return opts + def check_settings_file_version(self, options): + data = options.pop(0).rstrip('\n') + name, version = data.split('=') + if name == 'Version' and version == __version__: + return True + else: + return False + def load_from_file(self): - opts = self.extract_options(self.read_from_file()) - try: - self.ignoreErrors = opts[0] in ['True'] - self.idAsName = opts[1] in ['True'] - self.toAudio = opts[2] in ['True'] - self.audioFormat = opts[3] - self.keepVideo = opts[4] in ['True'] - self.audioQuality = int(opts[5]) - self.proxy = opts[6] - self.savePath = opts[7].decode('utf8') - self.autoUpdate = opts[8] in ['True'] - self.videoFormat = opts[9] - self.userAgent = opts[10] - self.referer = opts[11] - self.username = opts[12] - self.startTrack = opts[13] - self.endTrack = opts[14] - self.maxDownloads = opts[15] - self.rateLimit = opts[16] - self.retries = opts[17] - self.writeDescription = opts[18] in ['True'] - self.writeInfo = opts[19] in ['True'] - self.writeThumbnail = opts[20] in ['True'] - self.minFileSize = opts[21] - self.maxFileSize = opts[22] - self.writeSubs = opts[23] in ['True'] - self.writeAllSubs = opts[24] in ['True'] - self.subsLang = opts[25] - self.writeAutoSubs = opts[26] in ['True'] - self.cmdArgs = opts[27] - self.dashAudioFormat = opts[28] - self.clearDashFiles = opts[29] in ['True'] - self.updatePath = opts[30] - except: - self.statusBarWrite('Error while loading settings file') + options = self.read_from_file() + if self.check_settings_file_version(options): + opts = self.extract_options(options) + try: + self.savePath = opts[0].decode('utf8') + self.videoFormat = opts[1] + self.dashAudioFormat = opts[2] + self.clearDashFiles = opts[3] in ['True'] + self.toAudio = opts[4] in ['True'] + self.keepVideo = opts[5] in ['True'] + self.audioFormat = opts[6] + self.audioQuality = opts[7] + self.outputFormat = opts[8] + self.outputTemplate = opts[9] + self.startTrack = int(opts[10]) + self.endTrack = int(opts[11]) + self.maxDownloads = int(opts[12]) + self.minFileSize = opts[13] + self.maxFileSize = opts[14] + self.writeSubs = opts[15] in ['True'] + self.writeAllSubs = opts[16] in ['True'] + self.writeAutoSubs = opts[17] in ['True'] + self.embedSubs = opts[18] in ['True'] + self.subsLang = opts[19] + self.openDownloadDir = opts[20] in ['True'] + self.ignoreErrors = opts[21] in ['True'] + self.writeDescription = opts[22] in ['True'] + self.writeInfo = opts[23] in ['True'] + self.writeThumbnail = opts[24] in ['True'] + self.retries = int(opts[25]) + self.userAgent = opts[26] + self.referer = opts[27] + self.proxy = opts[28] + self.username = opts[29] + self.updatePath = opts[30].decode('utf8') + self.autoUpdate = opts[31] in ['True'] + self.cmdArgs = opts[32] + self.enableLog = opts[33] in ['True'] + self.writeTimeToLog = opts[34] in ['True'] + except: + self.statusBarWrite('Error while loading settings file. Loading default settings.') + self.load_default() + else: + self.statusBarWrite('Settings file version problem. Loading default settings.') self.load_default() def save_to_file(self): f = open(self.settings_abs_path, 'w') - f.write('IgnoreErrors='+str(self.ignoreErrors)+'\n') - f.write('IdAsName='+str(self.idAsName)+'\n') + f.write('Version='+__version__+'\n') + f.write('SavePath='+self.savePath.encode('utf-8')+'\n') + f.write('VideoFormat='+str(self.videoFormat)+'\n') + f.write('DashAudioFormat='+str(self.dashAudioFormat)+'\n') + f.write('ClearDashFiles='+str(self.clearDashFiles)+'\n') f.write('ToAudio='+str(self.toAudio)+'\n') - f.write('AudioFormat='+str(self.audioFormat)+'\n') f.write('KeepVideo='+str(self.keepVideo)+'\n') + f.write('AudioFormat='+str(self.audioFormat)+'\n') f.write('AudioQuality='+str(self.audioQuality)+'\n') - f.write('Proxy='+str(self.proxy)+'\n') - f.write('SavePath='+self.savePath.encode('utf-8')+'\n') - f.write('AutoUpdate='+str(self.autoUpdate)+'\n') - f.write('VideoFormat='+str(self.videoFormat)+'\n') - f.write('UserAgent='+str(self.userAgent)+'\n') - f.write('Referer='+str(self.referer)+'\n') - f.write('Username='+str(self.username)+'\n') + f.write('OutputFormat='+str(self.outputFormat)+'\n') + f.write('OutputTemplate='+str(self.outputTemplate)+'\n') f.write('StartTrack='+str(self.startTrack)+'\n') f.write('EndTrack='+str(self.endTrack)+'\n') f.write('MaxDownloads='+str(self.maxDownloads)+'\n') - f.write('RateLimit='+str(self.rateLimit)+'\n') - f.write('Retries='+str(self.retries)+'\n') - f.write('WriteDescription='+str(self.writeDescription)+'\n') - f.write('WriteInfo='+str(self.writeInfo)+'\n') - f.write('WriteThumbnail='+str(self.writeThumbnail)+'\n') f.write('MinFileSize='+str(self.minFileSize)+'\n') f.write('MaxFileSize='+str(self.maxFileSize)+'\n') f.write('WriteSubtitles='+str(self.writeSubs)+'\n') f.write('WriteAllSubtitles='+str(self.writeAllSubs)+'\n') - f.write('SubtitlesLanguage='+str(self.subsLang)+'\n') f.write('WriteAutoSubtitles='+str(self.writeAutoSubs)+'\n') + f.write('EmbedSubs='+str(self.embedSubs)+'\n') + f.write('SubtitlesLanguage='+str(self.subsLang)+'\n') + f.write('OpenDownloadDirectory='+str(self.openDownloadDir)+'\n') + f.write('IgnoreErrors='+str(self.ignoreErrors)+'\n') + f.write('WriteDescription='+str(self.writeDescription)+'\n') + f.write('WriteInfo='+str(self.writeInfo)+'\n') + f.write('WriteThumbnail='+str(self.writeThumbnail)+'\n') + f.write('Retries='+str(self.retries)+'\n') + f.write('UserAgent='+str(self.userAgent)+'\n') + f.write('Referer='+str(self.referer)+'\n') + f.write('Proxy='+str(self.proxy)+'\n') + f.write('Username='+str(self.username)+'\n') + # We dont store password & videoPass for security reasons + f.write('UpdatePath='+self.updatePath.encode('utf-8')+'\n') + f.write('AutoUpdate='+str(self.autoUpdate)+'\n') f.write('CmdArgs='+str(self.cmdArgs)+'\n') - f.write('DashAudioFormat='+str(self.dashAudioFormat)+'\n') - f.write('ClearDashFiles='+str(self.clearDashFiles)+'\n') - f.write('UpdatePath='+str(self.updatePath)+'\n') + f.write('EnableLog='+str(self.enableLog)+'\n') + f.write('WriteTimeToLog='+str(self.writeTimeToLog)+'\n') f.close() - \ No newline at end of file + diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/OutputHandler.py youtube-dlg-0.3.5/youtube_dl_gui/OutputHandler.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/OutputHandler.py 1970-01-01 00:00:00.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/OutputHandler.py 2014-04-07 12:59:32.000000000 +0000 @@ -0,0 +1,192 @@ +#! /usr/bin/env python + +from .Utils import ( + remove_empty_items, + string_to_array, + get_filename +) + +class DownloadHandler(): + + def __init__(self, ListCtrl): + self.ListCtrl = ListCtrl + self.finished = False + self.closed = False + self.error = False + self.init_handlers() + + def init_handlers(self): + ''' Initialise handlers ''' + self.handlers = [None for i in range(self.ListCtrl.ListIndex)] + + def _has_closed(self): + return self.closed + + def _has_finished(self): + return self.finished + + def _has_error(self): + return self.error + + def _add_empty_handler(self): + self.handlers.append(None) + + def handle(self, msg): + ''' Handles msg base to Signals.txt ''' + pack = msg.data + if pack.index == -1: + self.global_handler(pack) + else: + self.index_handler(pack) + + def global_handler(self, pack): + ''' Manage global index = -1 ''' + if pack.header == 'close': + self.closed = True + elif pack.header == 'finish': + self.finished = True + + def index_handler(self, pack): + ''' Manage handlers base on index ''' + if self.handlers[pack.index] == None: + self.handlers[pack.index] = IndexDownloadHandler(self.ListCtrl, pack.index) + self.handlers[pack.index].handle(pack) + if self.handlers[pack.index].has_error(): + self.error = True + +class IndexDownloadHandler(): + + def __init__(self, ListCtrl, index): + self.ListCtrl = ListCtrl + self.index = index + self.err = False + self.info = '' + + def has_error(self): + return self.err + + def handle(self, pack): + ''' Handle its pack for current index ''' + if pack.header == 'finish': + self.finish() + elif pack.header == 'close': + self.close() + elif pack.header == 'error': + self.error() + elif pack.header == 'playlist': + self.playlist(pack.data) + elif pack.header == 'youtube': + self.pre_proc() + elif pack.header == 'download': + self.download(pack.data) + elif pack.header == 'ffmpeg': + self.post_proc() + elif pack.header == 'remove': + self.remove() + elif pack.header == 'filename': + self.filename(pack.data) + + def finish(self): + self.ListCtrl._write_data(self.index, 4, '') + self.ListCtrl._write_data(self.index, 5, 'Finished') + + def close(self): + self.ListCtrl._write_data(self.index, 3, '') + self.ListCtrl._write_data(self.index, 4, '') + self.ListCtrl._write_data(self.index, 5, 'Stopped') + + def error(self): + self.err = True + self.ListCtrl._write_data(self.index, 3, '') + self.ListCtrl._write_data(self.index, 4, '') + self.ListCtrl._write_data(self.index, 5, 'Error') + + def pre_proc(self): + self.ListCtrl._write_data(self.index, 5, 'Pre-Processing %s' % self.info) + + def post_proc(self): + self.ListCtrl._write_data(self.index, 4, '') + self.ListCtrl._write_data(self.index, 5, 'Post-Processing %s' % self.info) + + def download(self, data): + self.ListCtrl._write_data(self.index, 1, data[0]) + self.ListCtrl._write_data(self.index, 2, data[1]) + self.ListCtrl._write_data(self.index, 3, data[2]) + self.ListCtrl._write_data(self.index, 4, data[3]) + self.ListCtrl._write_data(self.index, 5, 'Downloading %s' % self.info) + + def playlist(self, data): + self.ListCtrl._write_data(self.index, 1, '') + self.ListCtrl._write_data(self.index, 2, '') + self.ListCtrl._write_data(self.index, 3, '') + self.ListCtrl._write_data(self.index, 4, '') + self.info = '%s/%s' % (data[0], data[1]) + + def remove(self): + self.ListCtrl._write_data(self.index, 5, 'Removing DASH Files') + + def filename(self, fl): + self.ListCtrl._write_data(self.index, 0, get_filename(fl)) + +class DataPack(): + + def __init__(self, header, index=-1, data=None): + self.header = header + self.index = index + self.data = data + +class OutputFormatter(): + + def __init__(self, stdout): + dataPack = None + + self.stdout = remove_empty_items(string_to_array(stdout)) + # extract header from stdout + header = self.extract_header() + # extract special headers filename, playlist + header = self.set_filename_header(header) + header = self.set_playlist_header(header) + # extract data base on header + data = self.extract_data(header) + # extract special ignore header base on header, data + header = self.set_ignore_header(header, data) + # create data pack + self.dataPack = DataPack(header, data=data) + + def extract_header(self): + header = self.stdout.pop(0).replace('[', '').replace(']', '') + return header + + def extract_data(self, header): + ''' Extract data base on header ''' + if header == 'download': + if '%' in self.stdout[0]: + if self.stdout[0] != '100%': + ''' size, percent, eta, speed ''' + return [self.stdout[2], self.stdout[0], self.stdout[6], self.stdout[4]] + if header == 'playlist': + return [self.stdout[2], self.stdout[4]] + if header == 'filename': + return ' '.join(self.stdout[1:]) + return None + + def set_filename_header(self, header): + if header != 'ffmpeg': + if self.stdout[0] == 'Destination:': + return 'filename' + return header + + def set_playlist_header(self, header): + if header == 'download': + if self.stdout[0] == 'Downloading' and self.stdout[1] == 'video': + return 'playlist' + return header + + def set_ignore_header(self, header, data): + if header == 'download' and data == None: + return 'ignore' + return header + + def get_data(self): + return self.dataPack + diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/SignalHandler.py youtube-dlg-0.3.5/youtube_dl_gui/SignalHandler.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/SignalHandler.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/SignalHandler.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -#! /usr/bin/env python - -class DownloadHandler(): - - def __init__(self, ListCtrl): - self.ListCtrl = ListCtrl - self.finished = False - self.close = False - self.handlers = [] - - def _has_closed(self): - return self.close - - def _has_finished(self): - return self.finished - - def handle(self, msg): - ''' Handles msg base to Signals.txt ''' - data = msg.data - index = self.get_index(data) - if index == -1: - if data[0] == 'finish': - self.finished = True - elif data[0] == 'close': - self.close = True - else: - ''' Manage handlers for its index ''' - if index == len(self.handlers): - ''' Create new IndexDownloadHandler and add it to handlers ''' - self.handlers.append(IndexDownloadHandler(self.ListCtrl, index)) - ''' Let IndexDownloadHandler handle message data for current index ''' - self.handlers[index].handle(data) - - def get_index(self, data): - return data.pop() - -class IndexDownloadHandler(): - - def __init__(self, ListCtrl, index): - self.ListCtrl = ListCtrl - self.index = index - self.info = '' - - def handle(self, data): - ''' Handle its data message for current index ''' - if data[0] == 'finish': - self.finish() - elif data[0] == 'close': - self.close() - elif data[0] == 'error': - self.error() - elif data[0] == 'playlist': - self.playlist(data) - elif data[0] == '[youtube]': - self.pre_proc() - elif data[0] == '[download]': - self.download(data) - elif data[0] == '[ffmpeg]': - self.post_proc() - elif data[0] == 'remove': - self.remove() - - def finish(self): - self.ListCtrl._write_data(self.index, 4, '') - self.ListCtrl._write_data(self.index, 5, 'Finished') - - def close(self): - self.ListCtrl._write_data(self.index, 3, '') - self.ListCtrl._write_data(self.index, 4, '') - self.ListCtrl._write_data(self.index, 5, 'Stopped') - - def error(self): - self.ListCtrl._write_data(self.index, 3, '') - self.ListCtrl._write_data(self.index, 4, '') - self.ListCtrl._write_data(self.index, 5, 'Error') - - def pre_proc(self): - self.ListCtrl._write_data(self.index, 5, 'Pre-Processing %s' % self.info) - - def post_proc(self): - self.ListCtrl._write_data(self.index, 4, '') - self.ListCtrl._write_data(self.index, 5, 'Post-Processing %s' % self.info) - - def download(self, data): - self.ListCtrl._write_data(self.index, 1, data[3]) - self.ListCtrl._write_data(self.index, 2, data[1]) - self.ListCtrl._write_data(self.index, 3, data[7]) - self.ListCtrl._write_data(self.index, 4, data[5]) - self.ListCtrl._write_data(self.index, 5, 'Downloading %s' % self.info) - - def playlist(self, data): - self.ListCtrl._write_data(self.index, 1, '') - self.ListCtrl._write_data(self.index, 2, '') - self.ListCtrl._write_data(self.index, 3, '') - self.ListCtrl._write_data(self.index, 4, '') - self.info = '%s/%s' % (data[1], data[2]) - - def remove(self): - self.ListCtrl._write_data(self.index, 5, 'Removing DASH %s' % self.info) - diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/UpdateThread.py youtube-dlg-0.3.5/youtube_dl_gui/UpdateThread.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/UpdateThread.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/UpdateThread.py 2014-04-07 12:59:32.000000000 +0000 @@ -1,9 +1,10 @@ #! /usr/bin/env python -from threading import Thread from wx import CallAfter from wx.lib.pubsub import setuparg1 from wx.lib.pubsub import pub as Publisher + +from threading import Thread from urllib2 import urlopen, URLError, HTTPError from .Utils import ( @@ -18,7 +19,7 @@ class UpdateThread(Thread): - def __init__(self, youtubeDLFile, updatePath): + def __init__(self, updatePath, youtubeDLFile): super(UpdateThread, self).__init__() self.youtubeDLFile = youtubeDLFile self.updatePath = fix_path(updatePath) @@ -27,7 +28,7 @@ self.start() def run(self): - CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, "Downloading latest youtube-dl...") + CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, "Downloading latest youtube-dl. Please wait...") try: f = urlopen(self.url, timeout=DOWNLOAD_TIMEOUT) with open(self.updatePath + self.youtubeDLFile, 'wb') as lf: diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/Utils.py youtube-dlg-0.3.5/youtube_dl_gui/Utils.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/Utils.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/Utils.py 2014-04-07 12:59:32.000000000 +0000 @@ -2,21 +2,33 @@ import os import sys +import locale +import subprocess -def remove_spaces(array): +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(): if sys.version_info >= (3, 0): return None - if sys.platform == 'win32': - return sys.getfilesystemencoding() - else: - return None + # Refer to http://stackoverflow.com/a/9951851/35070 + return preferredencoding() + return None def encode_list(data_list, encoding): return [x.encode(encoding, 'ignore') for x in data_list] @@ -31,14 +43,10 @@ os.remove(filename) def get_path_seperator(): - if os.name == 'nt': - return '\\' - else: - return '/' + return '\\' if os.name == 'nt' else '/' def fix_path(path): - if path != '': - if path[-1:] != get_path_seperator(): + if path != '' and path[-1:] != get_path_seperator(): path += get_path_seperator() return path @@ -48,21 +56,38 @@ def add_PATH(path): os.environ["PATH"] += os.pathsep + path -def get_abs_path(path): - sep = get_path_seperator() - path_list = path.split(sep) +def abs_path(path): + path_list = path.split(get_path_seperator()) for i in range(len(path_list)): if path_list[i] == '~': path_list[i] = get_HOME() - path = sep.join(path_list) - return path + return get_path_seperator().join(path_list) def file_exist(filename): return os.path.exists(filename) def get_os_type(): return os.name - + +def get_filesize(path): + return os.path.getsize(path) + def makedir(path): os.makedirs(path) +def icon_path(icon_path, file_path): + L = len(icon_path) + file_path = os.path.abspath(file_path).split(get_path_seperator()) + for index, item in reversed(list(enumerate(icon_path))): + file_path[index - L] = item + return get_path_seperator().join(file_path) + +def get_filename(path): + return path.split(get_path_seperator())[-1] + +def open_dir(path): + if os.name == 'nt': + os.startfile(path) + else: + subprocess.call(('xdg-open', path)) + diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/version.py youtube-dlg-0.3.5/youtube_dl_gui/version.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/version.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/version.py 2014-04-07 12:59:32.000000000 +0000 @@ -1 +1 @@ -__version__ = '0.3' +__version__ = '0.3.5' diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/YoutubeDLGUI.py youtube-dlg-0.3.5/youtube_dl_gui/YoutubeDLGUI.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/YoutubeDLGUI.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/YoutubeDLGUI.py 2014-04-07 12:59:32.000000000 +0000 @@ -4,36 +4,42 @@ This file contains all gui classes MainFrame Custom wx.ListCtrl class -UpdateDialog OptionsFrame - ConnectionPanel + GeneralPanel AudioPanel - videoPanel - DownloadPanel + ConnectionPanel + VideoPanel + FilesystemPanel SubtitlesPanel - GeneralPanel OtherPanel + UpdatePanel + AuthenticationPanel + PlaylistPanel ''' import wx from wx.lib.pubsub import setuparg1 from wx.lib.pubsub import pub as Publisher +from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin + from .version import __version__ from .UpdateThread import UpdateThread from .DownloadThread import DownloadManager from .OptionsHandler import OptionsHandler from .YoutubeDLInterpreter import YoutubeDLInterpreter -from .SignalHandler import DownloadHandler +from .OutputHandler import DownloadHandler +from .LogManager import LogManager, LogGUI from .Utils import ( video_is_dash, have_dash_audio, - add_PATH, - get_HOME, get_os_type, file_exist, fix_path, - get_abs_path + abs_path, + icon_path, + open_dir, + remove_spaces ) if get_os_type() == 'nt': @@ -45,7 +51,7 @@ AUDIOFORMATS = ["mp3", "wav", "aac", "m4a"] -VIDEOFORMATS = ["highest available", +VIDEOFORMATS = ["default", "mp4 [1280x720]", "mp4 [640x360]", "webm [640x360]", @@ -68,31 +74,16 @@ "Russian", "Spanish", "German"] - + +ICON = icon_path(['icons', 'ytube.png'], __file__) + class MainFrame(wx.Frame): def __init__(self, parent=None, id=-1): - wx.Frame.__init__(self, parent, id, TITLE+' '+__version__, size=(600, 410), style = wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) + wx.Frame.__init__(self, parent, id, TITLE+' '+__version__, size=(600, 420)) - # set sizers for status box (Windows & Linux) - if get_os_type() == 'nt': - statusListSizer = (580, 165) - statusBarSizer = (15, 365) - else: - statusListSizer = (580, 195) - statusBarSizer = (15, 390) - - # create panel, trackList, statusBox using global statusBoxSizer - self.panel = wx.Panel(self) - wx.StaticText(self.panel, -1, "URLs", (15, 10)) - self.trackList = wx.TextCtrl(self.panel, -1, pos=(10, 25), size=(580, 110), style = wx.TE_MULTILINE | wx.TE_DONTWRAP) - self.statusList = ListCtrl(self.panel, -1, pos=(10, 190), size=statusListSizer, style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES) - self.statusBar = wx.StaticText(self.panel, -1, 'Author: Sotiris Papadopoulos', pos=statusBarSizer) - - # create buttons - self.downloadButton = wx.Button(self.panel, label="Download", pos=(100, 145), size=(-1, 30)) - self.updateButton = wx.Button(self.panel, label="Update", pos=(250, 145), size=(-1, 30)) - self.optionsButton = wx.Button(self.panel, label="Options", pos=(390, 145), size=(-1, 30)) + # init gui + self.InitGUI() # bind events self.Bind(wx.EVT_BUTTON, self.OnDownload, self.downloadButton) @@ -102,7 +93,7 @@ self.Bind(wx.EVT_CLOSE, self.OnClose) # set app icon - icon = wx.Icon('../icons/ytube.png', wx.BITMAP_TYPE_ICO) + icon = wx.Icon(ICON, wx.BITMAP_TYPE_ICO) self.SetIcon(icon) # set publisher for update thread @@ -115,6 +106,14 @@ self.optionsList = OptionsHandler(self.status_bar_write) self.downloadHandler = None + # init log manager + self.logManager = None + if self.optionsList.enableLog: + self.logManager = LogManager( + self.optionsList.get_config_path(), + self.optionsList.writeTimeToLog + ) + # init some thread variables self.downloadThread = None self.updateThread = None @@ -128,6 +127,40 @@ self.status_bar_write("Auto update enable") self.update_youtube_dl() + def InitGUI(self): + self.panel = wx.Panel(self) + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) + + urlTextBox = wx.BoxSizer(wx.HORIZONTAL) + urlTextBox.Add(wx.StaticText(self.panel, label='URLs'), flag = wx.TOP, border=10) + mainBoxSizer.Add(urlTextBox, flag = wx.LEFT, border=20) + + trckListBox = wx.BoxSizer(wx.HORIZONTAL) + self.trackList = wx.TextCtrl(self.panel, size=(-1, 120), style = wx.TE_MULTILINE | wx.TE_DONTWRAP) + trckListBox.Add(self.trackList, 1) + mainBoxSizer.Add(trckListBox, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=20) + + buttonsBox = wx.BoxSizer(wx.HORIZONTAL) + self.downloadButton = wx.Button(self.panel, label='Download', size=(90, 30)) + buttonsBox.Add(self.downloadButton) + self.updateButton = wx.Button(self.panel, label='Update', size=(90, 30)) + buttonsBox.Add(self.updateButton, flag = wx.LEFT | wx.RIGHT, border=80) + self.optionsButton = wx.Button(self.panel, label='Options', size=(90, 30)) + buttonsBox.Add(self.optionsButton) + mainBoxSizer.Add(buttonsBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP, border=10) + + stListBox = wx.BoxSizer(wx.HORIZONTAL) + self.statusList = ListCtrl(self.panel, style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES) + stListBox.Add(self.statusList, 1, flag = wx.EXPAND) + mainBoxSizer.Add(stListBox, 1, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=20) + + stBarBox = wx.BoxSizer(wx.HORIZONTAL) + self.statusBar = wx.StaticText(self.panel, label='Author: Sotiris Papadopoulos') + stBarBox.Add(self.statusBar, flag = wx.TOP | wx.BOTTOM, border=5) + mainBoxSizer.Add(stBarBox, flag = wx.LEFT, border=20) + + self.panel.SetSizer(mainBoxSizer) + def check_if_youtube_dl_exist(self): path = fix_path(self.optionsList.updatePath)+YOUTUBE_DL_FILENAME if not file_exist(path): @@ -136,52 +169,71 @@ def update_youtube_dl(self): self.downloadButton.Disable() - self.updateThread = UpdateThread(YOUTUBE_DL_FILENAME, self.optionsList.updatePath) + self.updateThread = UpdateThread(self.optionsList.updatePath, YOUTUBE_DL_FILENAME) def status_bar_write(self, msg): self.statusBar.SetLabel(msg) + def fin_task(self, msg): + self.status_bar_write(msg) + self.downloadButton.SetLabel('Download') + self.updateButton.Enable() + self.downloadThread.join() + self.downloadThread = None + self.downloadHandler = None + self.urlList = [] + self.finished_popup() + self.open_destination_dir() + + def open_destination_dir(self): + if self.optionsList.openDownloadDir: + open_dir(self.optionsList.savePath) + def download_handler(self, msg): self.downloadHandler.handle(msg) if self.downloadHandler._has_closed(): self.status_bar_write('Stoping downloads') if self.downloadHandler._has_finished(): - self.finished_popup() - self.status_bar_write('Done') - self.downloadButton.SetLabel('Download') - self.updateButton.Enable() - self.downloadThread = None - self.urlList = [] - self.downloadHandler = None + if self.downloadHandler._has_error(): + if self.logManager != None: + msg = 'An error occured while downloading. See Options>Log.' + else: + msg = 'An error occured while downloading' + else: + msg = 'Done' + self.fin_task(msg) def update_handler(self, msg): if msg.data == 'finish': self.downloadButton.Enable() + self.updateThread.join() self.updateThread = None else: self.status_bar_write(msg.data) def stop_download(self): self.downloadThread.close() - self.downloadThread.join() - self.downloadThread = None - self.urlList = [] - def start_download(self, trackList): - self.statusList._clear_list() + def load_tracklist(self, trackList): for url in trackList: + url = remove_spaces(url) if url != '': self.urlList.append(url) self.statusList._add_item(url) + + def start_download(self): + self.statusList._clear_list() + self.load_tracklist(self.trackList.GetValue().split('\n')) if not self.statusList._is_empty(): options = YoutubeDLInterpreter(self.optionsList, YOUTUBE_DL_FILENAME).get_options() - self.status_bar_write('Download started') self.downloadThread = DownloadManager( options, self.statusList._get_items(), - self.optionsList.clearDashFiles + self.optionsList.clearDashFiles, + self.logManager ) self.downloadHandler = DownloadHandler(self.statusList) + self.status_bar_write('Download started') self.downloadButton.SetLabel('Stop') self.updateButton.Disable() else: @@ -202,53 +254,57 @@ curList = self.trackList.GetValue().split('\n') ''' For each url in current url list ''' for url in curList: + ''' Remove spaces from url ''' + url = remove_spaces(url) ''' If url is not in self.urlList (original downloads list) and url is not empty ''' if url not in self.urlList and url != '': ''' Add url into original download list ''' self.urlList.append(url) + ''' Add handler for url ''' + self.downloadHandler._add_empty_handler() ''' Add url into statusList ''' - index = self.statusList._add_item(url) + self.statusList._add_item(url) ''' Retrieve last item as {url:url, index:indexNo} ''' item = self.statusList._get_last_item() ''' Pass that item into downloadThread ''' - self.downloadThread.add_download_item(item) + self.downloadThread._add_download_item(item) def OnDownload(self, event): if self.downloadThread != None: self.stop_download() else: - self.start_download(self.trackList.GetValue().split('\n')) + self.start_download() def OnUpdate(self, event): if (self.downloadThread == None and self.updateThread == None): - self.status_bar_write("Updating youtube-dl...") self.update_youtube_dl() def OnOptions(self, event): - optionsFrame = OptionsFrame(self.optionsList, self) + optionsFrame = OptionsFrame(self.optionsList, parent=self, logger=self.logManager) optionsFrame.Show() def OnClose(self, event): self.save_options() self.Destroy() - -class ListCtrl(wx.ListCtrl): + +class ListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin): ''' Custom ListCtrl class ''' - def __init__(self, parent, id, pos, size, style): + def __init__(self, parent=None, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, id, pos, size, style) - self.InsertColumn(0, 'URL', width=150) - self.InsertColumn(1, 'Size', width=90) - self.InsertColumn(2, 'Percent', width=80) - self.InsertColumn(3, 'ETA', width=50) + ListCtrlAutoWidthMixin.__init__(self) + self.InsertColumn(0, 'Video', width=150) + self.InsertColumn(1, 'Size', width=80) + self.InsertColumn(2, 'Percent', width=65) + self.InsertColumn(3, 'ETA', width=45) self.InsertColumn(4, 'Speed', width=90) - self.InsertColumn(5, 'Status', width=150) + self.InsertColumn(5, 'Status', width=160) + self.setResizeColumn(0) self.ListIndex = 0 ''' Add single item on list ''' def _add_item(self, item): self.InsertStringItem(self.ListIndex, item) self.ListIndex += 1 - return self.ListIndex ''' Write data on index, column ''' def _write_data(self, index, column, data): @@ -261,10 +317,7 @@ ''' Return True if list is empty ''' def _is_empty(self): - if self.ListIndex == 0: - return True - else: - return False + return self.ListIndex == 0 ''' Get last item inserted, Returns dictionary ''' def _get_last_item(self): @@ -285,117 +338,407 @@ items.append(data) return items +class LogPanel(wx.Panel): + + size = '' + path = '' + win_box_border = 0 + + def __init__(self, parent, optList, log): + wx.Panel.__init__(self, parent) + + self.SetBoxBorder() + self.optList = optList + self.log = log + self.set_data() + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) + + enLogBox = wx.BoxSizer(wx.HORIZONTAL) + self.enableLogChk = wx.CheckBox(self, label='Enable Log') + enLogBox.Add(self.enableLogChk) + mainBoxSizer.Add(enLogBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=20+self.win_box_border) + + wrTimeBox = wx.BoxSizer(wx.HORIZONTAL) + self.enableTimeChk = wx.CheckBox(self, label='Write Time') + wrTimeBox.Add(self.enableTimeChk) + mainBoxSizer.Add(wrTimeBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=5+self.win_box_border) + + butBox = wx.BoxSizer(wx.HORIZONTAL) + self.clearLogButton = wx.Button(self, label='Clear Log') + butBox.Add(self.clearLogButton) + self.viewLogButton = wx.Button(self, label='View Log') + butBox.Add(self.viewLogButton, flag = wx.LEFT, border=20) + mainBoxSizer.Add(butBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=15) + + if self.optList.enableLog: + self.SetDataSizers(mainBoxSizer) + + self.SetSizer(mainBoxSizer) + + self.Bind(wx.EVT_CHECKBOX, self.OnEnable, self.enableLogChk) + self.Bind(wx.EVT_CHECKBOX, self.OnTime, self.enableTimeChk) + self.Bind(wx.EVT_BUTTON, self.OnClear, self.clearLogButton) + self.Bind(wx.EVT_BUTTON, self.OnView, self.viewLogButton) + + def SetBoxBorder(self): + ''' Set border for windows ''' + if get_os_type() == 'nt': + self.win_box_border = 10 + + def SetDataSizers(self, box): + logPathText = wx.BoxSizer(wx.HORIZONTAL) + logPathText.Add(wx.StaticText(self, label='Path: ' + self.path)) + box.Add(logPathText, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=20) + logSizeText = wx.BoxSizer(wx.HORIZONTAL) + self.sizeText = wx.StaticText(self, label='Log Size: ' + self.size) + logSizeText.Add(self.sizeText) + box.Add(logSizeText, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=10) + + def set_data(self): + if self.log != None: + self.size = str(self.log.size()) + ' Bytes' + self.path = self.log.path + + def OnTime(self, event): + if self.log != None: + self.log.add_time = self.enableTimeChk.GetValue() + + def OnEnable(self, event): + if self.enableLogChk.GetValue(): + self.enableTimeChk.Enable() + else: + self.enableTimeChk.Disable() + self.restart_popup() + + def OnClear(self, event): + if self.log != None: + self.log.clear() + self.sizeText.SetLabel('Log Size: 0 Bytes') + + def OnView(self, event): + if self.log != None: + log_gui = LogGUI(self.path, parent=self) + log_gui.Show() + + def load_options(self): + self.enableLogChk.SetValue(self.optList.enableLog) + self.enableTimeChk.SetValue(self.optList.writeTimeToLog) + if self.optList.enableLog == False: + self.enableTimeChk.Disable() + if self.log == None: + self.clearLogButton.Disable() + self.viewLogButton.Disable() + + def save_options(self): + self.optList.enableLog = self.enableLogChk.GetValue() + self.optList.writeTimeToLog = self.enableTimeChk.GetValue() + + def restart_popup(self): + wx.MessageBox('Please restart ' + TITLE, 'Restart', wx.OK | wx.ICON_INFORMATION) + class UpdatePanel(wx.Panel): - def __init__(self, parent, optionsList): - self.optionsList = optionsList + def __init__(self, parent, optList): + wx.Panel.__init__(self, parent) + + self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) + + text = '''Enter the path where youtube-dlG should +download the latest youtube-dl.''' - text = '''Enter the path where youtube-dlG should store -settings file and the latest youtube-dl.''' + helpText = wx.BoxSizer(wx.HORIZONTAL) + helpText.Add(wx.StaticText(self, label=text), flag = wx.TOP, border=10) + mainBoxSizer.Add(helpText, flag = wx.LEFT, border=80) + + pathText = wx.BoxSizer(wx.HORIZONTAL) + pathText.Add(wx.StaticText(self, label='Path'), flag = wx.TOP, border=20) + mainBoxSizer.Add(pathText, flag = wx.LEFT, border=95) + + upPathBox = wx.BoxSizer(wx.HORIZONTAL) + self.updatePathBox = wx.TextCtrl(self) + upPathBox.Add(self.updatePathBox, 1, flag = wx.TOP, border=5) + mainBoxSizer.Add(upPathBox, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=90) + + autoUpBox = wx.BoxSizer(wx.HORIZONTAL) + self.autoUpdateChk = wx.CheckBox(self, label='Auto Update youtube-dl') + autoUpBox.Add(self.autoUpdateChk, flag = wx.TOP, border=30) + mainBoxSizer.Add(autoUpBox, flag = wx.LEFT, border=100) + + self.SetSizer(mainBoxSizer) + + def load_options(self): + self.updatePathBox.SetValue(self.optList.updatePath) + self.autoUpdateChk.SetValue(self.optList.autoUpdate) + + def save_options(self): + self.optList.updatePath = abs_path(self.updatePathBox.GetValue()) + self.optList.autoUpdate = self.autoUpdateChk.GetValue() +class PlaylistPanel(wx.Panel): + + def __init__(self, parent, optList): wx.Panel.__init__(self, parent) - wx.StaticText(self, -1, text, (15, 10)) - wx.StaticText(self, -1, 'Path', (25, 60)) - self.updatePathBox = wx.TextCtrl(self, -1, pos=(20, 80), size=(450, -1)) - self.autoUpdateChk = wx.CheckBox(self, -1, 'Auto Update', (25, 120)) + self.optList = optList + mainBoxSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Playlist Options'), wx.VERTICAL) + + mainBoxSizer.Add((-1, 10)) + + startBox = wx.BoxSizer(wx.HORIZONTAL) + startBox.Add(wx.StaticText(self, label='Start'), flag = wx.RIGHT, border=32) + self.startSpnr = wx.SpinCtrl(self, size=(60, -1)) + self.startSpnr.SetRange(1, 999) + startBox.Add(self.startSpnr) + mainBoxSizer.Add(startBox, flag = wx.TOP | wx.LEFT, border=20) + + stopBox = wx.BoxSizer(wx.HORIZONTAL) + stopBox.Add(wx.StaticText(self, label='Stop'), flag = wx.RIGHT, border=34) + self.stopSpnr = wx.SpinCtrl(self, size=(60, -1)) + self.stopSpnr.SetRange(0, 999) + stopBox.Add(self.stopSpnr) + mainBoxSizer.Add(stopBox, flag = wx.TOP | wx.LEFT, border=20) + + maxBox = wx.BoxSizer(wx.HORIZONTAL) + maxBox.Add(wx.StaticText(self, label='Max DLs'), flag = wx.RIGHT, border=self.get_border()) + self.maxSpnr = wx.SpinCtrl(self, size=(60, -1)) + self.maxSpnr.SetRange(0, 999) + maxBox.Add(self.maxSpnr) + mainBoxSizer.Add(maxBox, flag = wx.TOP | wx.LEFT, border=20) + + self.SetSizer(mainBoxSizer) + + def get_border(self): + if get_os_type() == 'nt': + return 16 + return 10 + def load_options(self): - self.updatePathBox.SetValue(self.optionsList.updatePath) - self.autoUpdateChk.SetValue(self.optionsList.autoUpdate) + self.startSpnr.SetValue(self.optList.startTrack) + self.stopSpnr.SetValue(self.optList.endTrack) + self.maxSpnr.SetValue(self.optList.maxDownloads) def save_options(self): - self.optionsList.updatePath = self.updatePathBox.GetValue() - self.optionsList.autoUpdate = self.autoUpdateChk.GetValue() + self.optList.startTrack = self.startSpnr.GetValue() + self.optList.endTrack = self.stopSpnr.GetValue() + self.optList.maxDownloads = self.maxSpnr.GetValue() class ConnectionPanel(wx.Panel): def __init__(self, parent, optList): + wx.Panel.__init__(self, parent) + self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) + + retBox = wx.BoxSizer(wx.HORIZONTAL) + retBox.Add(wx.StaticText(self, label='Retries'), flag = wx.RIGHT, border=8) + self.retriesSpnr = wx.SpinCtrl(self, size=(50, -1)) + self.retriesSpnr.SetRange(1, 99) + retBox.Add(self.retriesSpnr) + mainBoxSizer.Add(retBox, flag = wx.LEFT | wx.TOP, border=10) + + uaText = wx.BoxSizer(wx.HORIZONTAL) + uaText.Add(wx.StaticText(self, label='User Agent'), flag = wx.LEFT, border=15) + mainBoxSizer.Add(uaText, flag = wx.TOP, border=10) + + mainBoxSizer.Add((-1, 5)) + + uaBox = wx.BoxSizer(wx.HORIZONTAL) + self.userAgentBox = wx.TextCtrl(self) + uaBox.Add(self.userAgentBox, 1, flag = wx.LEFT, border=10) + mainBoxSizer.Add(uaBox, flag = wx.EXPAND | wx.RIGHT, border=200) + + refText = wx.BoxSizer(wx.HORIZONTAL) + refText.Add(wx.StaticText(self, label='Referer'), flag = wx.LEFT, border=15) + mainBoxSizer.Add(refText, flag = wx.TOP, border=10) + + mainBoxSizer.Add((-1, 5)) + + refBox = wx.BoxSizer(wx.HORIZONTAL) + self.refererBox = wx.TextCtrl(self) + refBox.Add(self.refererBox, 1, flag = wx.LEFT, border=10) + mainBoxSizer.Add(refBox, flag = wx.EXPAND | wx.RIGHT, border=200) + + prxyText = wx.BoxSizer(wx.HORIZONTAL) + prxyText.Add(wx.StaticText(self, label='Proxy'), flag = wx.LEFT, border=15) + mainBoxSizer.Add(prxyText, flag = wx.TOP, border=10) + + mainBoxSizer.Add((-1, 5)) + + prxyBox = wx.BoxSizer(wx.HORIZONTAL) + self.proxyBox = wx.TextCtrl(self) + prxyBox.Add(self.proxyBox, 1, flag = wx.LEFT, border=10) + mainBoxSizer.Add(prxyBox, flag = wx.EXPAND | wx.RIGHT, border=100) + + self.SetSizer(mainBoxSizer) - wx.Panel.__init__(self, parent) - wx.StaticText(self, -1, 'User Agent', (15, 10)) - self.userAgentBox = wx.TextCtrl(self, -1, pos=(10, 30), size=(230, -1)) - wx.StaticText(self, -1, 'Referer', (270, 10)) - self.refererBox = wx.TextCtrl(self, -1, pos=(265, 30), size=(230, -1)) - wx.StaticText(self, -1, 'Username', (15, 60)) - self.usernameBox = wx.TextCtrl(self, -1, pos=(10, 80), size=(230, -1)) - wx.StaticText(self, -1, 'Password', (270, 60)) - self.passwordBox = wx.TextCtrl(self, -1, pos=(265, 80), size=(230, -1), style = wx.TE_PASSWORD) - wx.StaticText(self, -1, 'Proxy', (15, 110)) - self.proxyBox = wx.TextCtrl(self, -1, pos=(10, 130), size=(350, -1)) - def load_options(self): self.userAgentBox.SetValue(self.optList.userAgent) self.refererBox.SetValue(self.optList.referer) - self.usernameBox.SetValue(self.optList.username) self.proxyBox.SetValue(self.optList.proxy) + self.retriesSpnr.SetValue(self.optList.retries) def save_options(self): self.optList.userAgent = self.userAgentBox.GetValue() self.optList.referer = self.refererBox.GetValue() - self.optList.username = self.usernameBox.GetValue() self.optList.proxy = self.proxyBox.GetValue() + self.optList.retries = self.retriesSpnr.GetValue() + +class AuthenticationPanel(wx.Panel): + + def __init__(self, parent, optList): + wx.Panel.__init__(self,parent) + + self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) + usrTextBox = wx.BoxSizer(wx.HORIZONTAL) + usrTextBox.Add(wx.StaticText(self, label='Username')) + mainBoxSizer.Add(usrTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=15) + + usrBox = wx.BoxSizer(wx.HORIZONTAL) + self.usernameBox = wx.TextCtrl(self, size=(-1, 25)) + usrBox.Add(self.usernameBox, 1, flag = wx.TOP, border=5) + mainBoxSizer.Add(usrBox, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=160) + + passTextBox = wx.BoxSizer(wx.HORIZONTAL) + passTextBox.Add(wx.StaticText(self, label='Password')) + mainBoxSizer.Add(passTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=15) + + passBox = wx.BoxSizer(wx.HORIZONTAL) + self.passwordBox = wx.TextCtrl(self, size=(-1, 25), style = wx.TE_PASSWORD) + passBox.Add(self.passwordBox, 1, flag = wx.TOP, border=5) + mainBoxSizer.Add(passBox, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=160) + + vPassTextBox = wx.BoxSizer(wx.HORIZONTAL) + vPassTextBox.Add(wx.StaticText(self, label='Video Password (vimeo, smotri)')) + mainBoxSizer.Add(vPassTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=15) + + vPassBox = wx.BoxSizer(wx.HORIZONTAL) + self.videopassBox = wx.TextCtrl(self, size=(-1, 25), style = wx.TE_PASSWORD) + vPassBox.Add(self.videopassBox, 1, flag = wx.TOP, border=5) + mainBoxSizer.Add(vPassBox, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=160) + + self.SetSizer(mainBoxSizer) + + def load_options(self): + self.usernameBox.SetValue(self.optList.username) + self.passwordBox.SetValue(self.optList.password) + self.videopassBox.SetValue(self.optList.videoPass) + + def save_options(self): + self.optList.username = self.usernameBox.GetValue() + self.optList.password = self.passwordBox.GetValue() + self.optList.videoPass = self.videopassBox.GetValue() + class AudioPanel(wx.Panel): + win_box_border = 0 + quality = ['high', 'mid', 'low'] + def __init__(self, parent, optList): + wx.Panel.__init__(self, parent) + + self.SetBoxBorder() self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) - wx.Panel.__init__(self, parent) - self.toAudioChk = wx.CheckBox(self, -1, 'Convert to Audio', (10, 10)) - self.keepVideoChk = wx.CheckBox(self, -1, 'Keep Video', (30, 40)) - wx.StaticText(self, -1, 'Audio Format', (35, 80)) - self.audioFormatCombo = wx.ComboBox(self, choices=AUDIOFORMATS, pos=(30, 100), size=(95, -1)) - wx.StaticText(self, -1, "Audio Quality 0 (best) 9 (worst)", (35, 130)) - self.audioQualitySpnr = wx.SpinCtrl(self, -1, "", (30, 150)) - self.audioQualitySpnr.SetRange(0, 9) + toaBox = wx.BoxSizer(wx.HORIZONTAL) + self.toAudioChk = wx.CheckBox(self, label='Convert to Audio') + toaBox.Add(self.toAudioChk) + mainBoxSizer.Add(toaBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=15+self.win_box_border) + + keepVBox = wx.BoxSizer(wx.HORIZONTAL) + self.keepVideoChk = wx.CheckBox(self, label='Keep Video') + keepVBox.Add(self.keepVideoChk) + mainBoxSizer.Add(keepVBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=5+self.win_box_border) + + afTextBox = wx.BoxSizer(wx.HORIZONTAL) + afTextBox.Add(wx.StaticText(self, label='Audio Format')) + mainBoxSizer.Add(afTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=10) + + afComboBox = wx.BoxSizer(wx.HORIZONTAL) + self.audioFormatCombo = wx.ComboBox(self, choices=AUDIOFORMATS, size=(160, 30)) + afComboBox.Add(self.audioFormatCombo) + mainBoxSizer.Add(afComboBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=5) + + aqTextBox = wx.BoxSizer(wx.HORIZONTAL) + aqTextBox.Add(wx.StaticText(self, label='Audio Quality')) + mainBoxSizer.Add(aqTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=10) + + aqComboBox = wx.BoxSizer(wx.HORIZONTAL) + self.audioQualityCombo = wx.ComboBox(self, choices=self.quality, size=(80, 25)) + aqComboBox.Add(self.audioQualityCombo) + mainBoxSizer.Add(aqComboBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=5) + + self.SetSizer(mainBoxSizer) self.Bind(wx.EVT_CHECKBOX, self.OnAudioCheck, self.toAudioChk) + def SetBoxBorder(self): + ''' Set border for windows ''' + if get_os_type() == 'nt': + self.win_box_border = 5 + def OnAudioCheck(self, event): - if (self.toAudioChk.GetValue()): + if self.toAudioChk.GetValue(): self.keepVideoChk.Enable() self.audioFormatCombo.Enable() - self.audioQualitySpnr.Enable() + self.audioQualityCombo.Enable() else: self.keepVideoChk.Disable() self.audioFormatCombo.Disable() - self.audioQualitySpnr.Disable() + self.audioQualityCombo.Disable() def load_options(self): self.toAudioChk.SetValue(self.optList.toAudio) self.keepVideoChk.SetValue(self.optList.keepVideo) self.audioFormatCombo.SetValue(self.optList.audioFormat) - self.audioQualitySpnr.SetValue(self.optList.audioQuality) - if (self.optList.toAudio == False): + self.audioQualityCombo.SetValue(self.optList.audioQuality) + if self.optList.toAudio == False: self.keepVideoChk.Disable() self.audioFormatCombo.Disable() - self.audioQualitySpnr.Disable() + self.audioQualityCombo.Disable() def save_options(self): self.optList.toAudio = self.toAudioChk.GetValue() self.optList.keepVideo = self.keepVideoChk.GetValue() self.optList.audioFormat = self.audioFormatCombo.GetValue() - self.optList.audioQuality = self.audioQualitySpnr.GetValue() + self.optList.audioQuality = self.audioQualityCombo.GetValue() class VideoPanel(wx.Panel): def __init__(self, parent, optList): + wx.Panel.__init__(self, parent) + self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) - wx.Panel.__init__(self, parent) - wx.StaticText(self, -1, 'Video Format', (15, 10)) - self.videoFormatCombo = wx.ComboBox(self, choices=VIDEOFORMATS, pos=(10, 30), size=(160, 30)) - wx.StaticText(self, -1, 'DASH Audio', (15, 100)) - self.dashAudioFormatCombo = wx.ComboBox(self, choices=DASH_AUDIO_FORMATS, pos=(10, 120), size=(160, 30)) - self.clearDashFilesChk = wx.CheckBox(self, -1, 'Clear DASH audio/video files', (10, 160)) - wx.StaticText(self, -1, 'Playlist Options', (350, 30)) - wx.StaticText(self, -1, 'Start', (300, 60)) - self.startBox = wx.TextCtrl(self, -1, pos=(370, 55), size=(50, -1)) - wx.StaticText(self, -1, 'Stop', (300, 100)) - self.stopBox = wx.TextCtrl(self, -1, pos=(370, 95), size=(50, -1)) - wx.StaticText(self, -1, 'Max DLs', (300, 140)) - self.maxBox = wx.TextCtrl(self, -1, pos=(370, 135), size=(50, -1)) + vfTextBox = wx.BoxSizer(wx.HORIZONTAL) + vfTextBox.Add(wx.StaticText(self, label='Video Format')) + mainBoxSizer.Add(vfTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=20) + + vfComboBox = wx.BoxSizer(wx.HORIZONTAL) + self.videoFormatCombo = wx.ComboBox(self, choices=VIDEOFORMATS, size=(160, 30)) + vfComboBox.Add(self.videoFormatCombo) + mainBoxSizer.Add(vfComboBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=5) + + daTextBox = wx.BoxSizer(wx.HORIZONTAL) + daTextBox.Add(wx.StaticText(self, label='DASH Audio')) + mainBoxSizer.Add(daTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=10) + + daComboBox = wx.BoxSizer(wx.HORIZONTAL) + self.dashAudioFormatCombo = wx.ComboBox(self, choices=DASH_AUDIO_FORMATS, size=(160, 30)) + daComboBox.Add(self.dashAudioFormatCombo) + mainBoxSizer.Add(daComboBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=5) + + clrDashBox = wx.BoxSizer(wx.HORIZONTAL) + self.clearDashFilesChk = wx.CheckBox(self, label='Clear DASH audio/video files') + clrDashBox.Add(self.clearDashFilesChk) + mainBoxSizer.Add(clrDashBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=20) + + self.SetSizer(mainBoxSizer) self.Bind(wx.EVT_COMBOBOX, self.OnVideoFormatPick, self.videoFormatCombo) self.Bind(wx.EVT_COMBOBOX, self.OnAudioFormatPick, self.dashAudioFormatCombo) @@ -419,9 +762,6 @@ def load_options(self): self.videoFormatCombo.SetValue(self.optList.videoFormat) - self.startBox.SetValue(self.optList.startTrack) - self.stopBox.SetValue(self.optList.endTrack) - self.maxBox.SetValue(self.optList.maxDownloads) self.dashAudioFormatCombo.SetValue(self.optList.dashAudioFormat) self.clearDashFilesChk.SetValue(self.optList.clearDashFiles) if not video_is_dash(self.optList.videoFormat): @@ -432,87 +772,268 @@ def save_options(self): self.optList.videoFormat = self.videoFormatCombo.GetValue() - self.optList.startTrack = self.startBox.GetValue() - self.optList.endTrack = self.stopBox.GetValue() - self.optList.maxDownloads = self.maxBox.GetValue() self.optList.dashAudioFormat = self.dashAudioFormatCombo.GetValue() self.optList.clearDashFiles = self.clearDashFilesChk.GetValue() - -class DownloadPanel(wx.Panel): + +class OutputPanel(wx.Panel): + + win_box_border = 0 def __init__(self, parent, optList): + wx.Panel.__init__(self, parent) + + self.SetBoxBorder() self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) + + idBox = wx.BoxSizer(wx.HORIZONTAL) + self.idAsNameChk = wx.CheckBox(self, label='ID as Name') + idBox.Add(self.idAsNameChk, flag = wx.LEFT, border=5) + mainBoxSizer.Add(idBox, flag = wx.TOP, border=15) + + titleBox = wx.BoxSizer(wx.HORIZONTAL) + self.titleBoxChk = wx.CheckBox(self, label='Title as Name') + titleBox.Add(self.titleBoxChk, flag = wx.LEFT, border=5) + mainBoxSizer.Add(titleBox, flag = wx.TOP, border=5+self.win_box_border) + + customChkBox = wx.BoxSizer(wx.HORIZONTAL) + self.customTitleChk = wx.CheckBox(self, label='Custom Template (youtube-dl)') + customChkBox.Add(self.customTitleChk, flag = wx.LEFT, border=5) + mainBoxSizer.Add(customChkBox, flag = wx.TOP, border=5+self.win_box_border) + + mainBoxSizer.Add((-1, 10)) + + customBox = wx.BoxSizer(wx.HORIZONTAL) + self.customTitleBox = wx.TextCtrl(self) + customBox.Add(self.customTitleBox, 1, flag = wx.RIGHT, border=300) + mainBoxSizer.Add(customBox, flag = wx.EXPAND | wx.LEFT, border=5) + + self.SetSizer(mainBoxSizer) + + self.Bind(wx.EVT_CHECKBOX, self.OnId, self.idAsNameChk) + self.Bind(wx.EVT_CHECKBOX, self.OnTitle, self.titleBoxChk) + self.Bind(wx.EVT_CHECKBOX, self.OnCustom, self.customTitleChk) + + def OnId(self, event): + self.group_load('id') + + def OnTitle(self, event): + self.group_load('title') + + def OnCustom(self, event): + self.group_load('custom') + def SetBoxBorder(self): + ''' Set border for windows ''' + if get_os_type() == 'nt': + self.win_box_border = 10 + + def group_load(self, oformat): + if oformat == 'id': + self.idAsNameChk.SetValue(True) + self.titleBoxChk.SetValue(False) + self.customTitleChk.SetValue(False) + self.customTitleBox.Disable() + elif oformat == 'title': + self.idAsNameChk.SetValue(False) + self.titleBoxChk.SetValue(True) + self.customTitleChk.SetValue(False) + self.customTitleBox.Disable() + elif oformat == 'custom': + self.idAsNameChk.SetValue(False) + self.titleBoxChk.SetValue(False) + self.customTitleChk.SetValue(True) + self.customTitleBox.Enable() + + def get_output_format(self): + if self.idAsNameChk.GetValue(): + return 'id' + elif self.titleBoxChk.GetValue(): + return 'title' + elif self.customTitleChk.GetValue(): + return 'custom' + + def load_options(self): + self.group_load(self.optList.outputFormat) + self.customTitleBox.SetValue(self.optList.outputTemplate) + + def save_options(self): + self.optList.outputTemplate = self.customTitleBox.GetValue() + self.optList.outputFormat = self.get_output_format() + +class FilesystemPanel(wx.Panel): + + win_box_border = 0 + + def __init__(self, parent, optList): wx.Panel.__init__(self, parent) - wx.StaticText(self, -1, 'Rate Limit (e.g. 50k or 44.6m)', (250, 15)) - self.limitBox = wx.TextCtrl(self, -1, pos=(245, 35), size=(80, -1)) - wx.StaticText(self, -1, 'Retries', (15, 15)) - self.retriesBox = wx.TextCtrl(self, -1, pos=(10, 35), size=(50, -1)) - self.writeDescriptionChk = wx.CheckBox(self, -1, 'Write description to file', (10, 60)) - self.writeInfoChk = wx.CheckBox(self, -1, 'Write info to (.json) file', (10, 85)) - self.writeThumbnailChk = wx.CheckBox(self, -1, 'Write thumbnail to disk', (10, 110)) - self.ignoreErrorsChk = wx.CheckBox(self, -1, 'Ignore Errors', (10, 135)) - wx.StaticText(self, -1, 'Min Filesize (e.g. 50k or 44.6m)', (250, 65)) - self.minFilesizeBox = wx.TextCtrl(self, -1, pos=(245, 85), size=(80, -1)) - wx.StaticText(self, -1, 'Max Filesize (e.g. 50k or 44.6m)', (250, 115)) - self.maxFilesizeBox = wx.TextCtrl(self, -1, pos=(245, 135), size=(80, -1)) + self.SetBoxBorder() + self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.HORIZONTAL) + leftBoxSizer = wx.BoxSizer(wx.VERTICAL) + rightBoxSizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Filesize (e.g. 50k or 44.6m)'), wx.VERTICAL) + + self.SetLeftBox(leftBoxSizer) + self.SetRightBox(rightBoxSizer) + + mainBoxSizer.Add(leftBoxSizer, 1, flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=10) + mainBoxSizer.Add(rightBoxSizer, 1, flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=10) + + self.SetSizer(mainBoxSizer) + + def SetBoxBorder(self): + ''' Set border for windows ''' + if get_os_type() == 'nt': + self.win_box_border = 15 + + def SetLeftBox(self, box): + ignrBox = wx.BoxSizer(wx.HORIZONTAL) + self.ignoreErrorsChk = wx.CheckBox(self, label='Ignore Errors') + ignrBox.Add(self.ignoreErrorsChk, flag = wx.LEFT, border=5) + box.Add(ignrBox, flag = wx.TOP, border=20) + + wrtDescBox = wx.BoxSizer(wx.HORIZONTAL) + self.writeDescriptionChk = wx.CheckBox(self, label='Write description to file') + wrtDescBox.Add(self.writeDescriptionChk, flag = wx.LEFT, border=5) + box.Add(wrtDescBox, flag = wx.TOP, border=5+self.win_box_border) + + wrtInfoBox = wx.BoxSizer(wx.HORIZONTAL) + self.writeInfoChk = wx.CheckBox(self, label='Write info to (.json) file') + wrtInfoBox.Add(self.writeInfoChk, flag = wx.LEFT, border=5) + box.Add(wrtInfoBox, flag = wx.TOP, border=5+self.win_box_border) + + wrtThumBox = wx.BoxSizer(wx.HORIZONTAL) + self.writeThumbnailChk = wx.CheckBox(self, label='Write thumbnail to disk') + wrtThumBox.Add(self.writeThumbnailChk, flag = wx.LEFT, border=5) + box.Add(wrtThumBox, flag = wx.TOP, border=5+self.win_box_border) + + openDirBox = wx.BoxSizer(wx.HORIZONTAL) + self.openDirChk = wx.CheckBox(self, label='Open destination folder when done') + openDirBox.Add(self.openDirChk, flag = wx.LEFT, border=5) + box.Add(openDirBox, flag = wx.TOP, border=5+self.win_box_border) + + def SetRightBox(self, box): + minBox = wx.BoxSizer(wx.HORIZONTAL) + minBox.Add(wx.StaticText(self, label='Min'), flag = wx.RIGHT, border=12) + self.minFilesizeBox = wx.TextCtrl(self, size=(80, -1)) + minBox.Add(self.minFilesizeBox) + box.Add(minBox, flag = wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, border=10) + + maxBox = wx.BoxSizer(wx.HORIZONTAL) + maxBox.Add(wx.StaticText(self, label='Max'), flag = wx.RIGHT, border=8) + self.maxFilesizeBox = wx.TextCtrl(self, size=(80, -1)) + maxBox.Add(self.maxFilesizeBox) + box.Add(maxBox, flag = wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, border=10) + def load_options(self): - self.limitBox.SetValue(self.optList.rateLimit) - self.retriesBox.SetValue(self.optList.retries) self.writeDescriptionChk.SetValue(self.optList.writeDescription) self.writeInfoChk.SetValue(self.optList.writeInfo) self.writeThumbnailChk.SetValue(self.optList.writeThumbnail) self.ignoreErrorsChk.SetValue(self.optList.ignoreErrors) + self.openDirChk.SetValue(self.optList.openDownloadDir) self.minFilesizeBox.SetValue(self.optList.minFileSize) self.maxFilesizeBox.SetValue(self.optList.maxFileSize) - + def save_options(self): - self.optList.rateLimit = self.limitBox.GetValue() - self.optList.retries = self.retriesBox.GetValue() self.optList.writeDescription = self.writeDescriptionChk.GetValue() self.optList.writeInfo = self.writeInfoChk.GetValue() self.optList.writeThumbnail = self.writeThumbnailChk.GetValue() self.optList.ignoreErrors = self.ignoreErrorsChk.GetValue() + self.optList.openDownloadDir = self.openDirChk.GetValue() self.optList.minFileSize = self.minFilesizeBox.GetValue() self.optList.maxFileSize = self.maxFilesizeBox.GetValue() + self.check_input() + + def check_input(self): + self.optList.minFileSize.replace('-', '') + self.optList.maxFileSize.replace('-', '') + if self.optList.minFileSize == '': + self.optList.minFileSize = '0' + if self.optList.maxFileSize == '': + self.optList.maxFileSize = '0' class SubtitlesPanel(wx.Panel): + win_box_border = 0 + def __init__(self, parent, optList): + wx.Panel.__init__(self, parent) + + self.SetBoxBorder() self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) - wx.Panel.__init__(self, parent) - self.writeSubsChk = wx.CheckBox(self, -1, 'Write subtitle file', (10, 10)) - self.writeAllSubsChk = wx.CheckBox(self, -1, 'Download all available subtitles', (10, 40)) - self.writeAutoSubsChk = wx.CheckBox(self, -1, 'Write automatic subtitle file (YOUTUBE ONLY)', (10, 70)) - wx.StaticText(self, -1, 'Subtitles Language', (15, 105)) - self.subsLangCombo = wx.ComboBox(self, choices=LANGUAGES, pos=(10, 125), size=(140, 30)) + dlSubsBox = wx.BoxSizer(wx.HORIZONTAL) + self.writeSubsChk = wx.CheckBox(self, label='Download subtitle file by language') + dlSubsBox.Add(self.writeSubsChk, flag = wx.LEFT, border=10) + mainBoxSizer.Add(dlSubsBox, flag = wx.TOP, border=15) + + dlAllSubBox = wx.BoxSizer(wx.HORIZONTAL) + self.writeAllSubsChk = wx.CheckBox(self, label='Download all available subtitles') + dlAllSubBox.Add(self.writeAllSubsChk, flag = wx.LEFT, border=10) + mainBoxSizer.Add(dlAllSubBox, flag = wx.TOP, border=5+self.win_box_border) + + dlAutoSubBox = wx.BoxSizer(wx.HORIZONTAL) + self.writeAutoSubsChk = wx.CheckBox(self, label='Download automatic subtitle file (YOUTUBE ONLY)') + dlAutoSubBox.Add(self.writeAutoSubsChk, flag = wx.LEFT, border=10) + mainBoxSizer.Add(dlAutoSubBox, flag = wx.TOP, border=5+self.win_box_border) + + embSubBox = wx.BoxSizer(wx.HORIZONTAL) + self.embedSubsChk = wx.CheckBox(self, label='Embed subtitles in the video (only for mp4 videos)') + self.embedSubsChk.Disable() + embSubBox.Add(self.embedSubsChk, flag = wx.LEFT, border=10) + mainBoxSizer.Add(embSubBox, flag = wx.TOP, border=5+self.win_box_border) + + slangTextBox = wx.BoxSizer(wx.HORIZONTAL) + slangTextBox.Add(wx.StaticText(self, label='Subtitles Language'), flag = wx.LEFT, border=15) + mainBoxSizer.Add(slangTextBox, flag = wx.TOP, border=10+self.win_box_border) + + slangBox = wx.BoxSizer(wx.HORIZONTAL) + self.subsLangCombo = wx.ComboBox(self, choices=LANGUAGES, size=(140, 30)) + slangBox.Add(self.subsLangCombo, flag = wx.LEFT, border=10) + mainBoxSizer.Add(slangBox, flag = wx.TOP, border=5) + + self.SetSizer(mainBoxSizer) self.Bind(wx.EVT_CHECKBOX, self.OnWriteSubsChk, self.writeSubsChk) self.Bind(wx.EVT_CHECKBOX, self.OnWriteAllSubsChk, self.writeAllSubsChk) self.Bind(wx.EVT_CHECKBOX, self.OnWriteAutoSubsChk, self.writeAutoSubsChk) - + + def SetBoxBorder(self): + ''' Set border for windows ''' + if get_os_type() == 'nt': + self.win_box_border = 10 + + def subs_are_on(self): + return self.writeAutoSubsChk.GetValue() or self.writeSubsChk.GetValue() + def OnWriteAutoSubsChk(self, event): - if (self.writeAutoSubsChk.GetValue()): + if self.writeAutoSubsChk.GetValue(): self.writeAllSubsChk.Disable() self.writeSubsChk.Disable() self.subsLangCombo.Disable() + self.embedSubsChk.Enable() else: self.writeAllSubsChk.Enable() self.writeSubsChk.Enable() self.subsLangCombo.Enable() + self.embedSubsChk.Disable() + self.embedSubsChk.SetValue(False) def OnWriteSubsChk(self, event): - if (self.writeSubsChk.GetValue()): + if self.writeSubsChk.GetValue(): self.writeAllSubsChk.Disable() self.writeAutoSubsChk.Disable() + self.embedSubsChk.Enable() else: self.writeAllSubsChk.Enable() self.writeAutoSubsChk.Enable() + self.embedSubsChk.Disable() + self.embedSubsChk.SetValue(False) def OnWriteAllSubsChk(self, event): - if (self.writeAllSubsChk.GetValue()): + if self.writeAllSubsChk.GetValue(): self.writeSubsChk.Disable() self.subsLangCombo.Disable() self.writeAutoSubsChk.Disable() @@ -530,51 +1051,70 @@ self.writeAllSubsChk.SetValue(self.optList.writeAllSubs) self.subsLangCombo.SetValue(self.optList.subsLang) self.writeAutoSubsChk.SetValue(self.optList.writeAutoSubs) - if (self.writeSubsChk.GetValue()): + self.embedSubsChk.SetValue(self.optList.embedSubs) + if self.optList.writeSubs: self.writeAllSubsChk.Disable() - self.writeAllSubsChk.SetValue(False) self.writeAutoSubsChk.Disable() - self.writeAutoSubsChk.SetValue(False) - if (self.writeAllSubsChk.GetValue()): + self.embedSubsChk.Enable() + if self.optList.writeAllSubs: self.writeSubsChk.Disable() - self.writeSubsChk.SetValue(False) self.subsLangCombo.Disable() self.writeAutoSubsChk.Disable() - self.writeAutoSubsChk.SetValue(False) - if (self.writeAutoSubsChk.GetValue()): + if self.optList.writeAutoSubs: self.writeAllSubsChk.Disable() - self.writeAllSubsChk.SetValue(False) self.writeSubsChk.Disable() - self.writeSubsChk.SetValue(False) self.subsLangCombo.Disable() + self.embedSubsChk.Enable() + if not self.subs_are_on(): + self.embedSubsChk.Disable() def save_options(self): self.optList.writeSubs = self.writeSubsChk.GetValue() self.optList.writeAllSubs = self.writeAllSubsChk.GetValue() self.optList.subsLang = self.subsLangCombo.GetValue() self.optList.writeAutoSubs = self.writeAutoSubsChk.GetValue() + self.optList.embedSubs = self.embedSubsChk.GetValue() class GeneralPanel(wx.Panel): - def __init__(self, parent, optList, controlParent): + def __init__(self, parent, optList, resetHandler): + wx.Panel.__init__(self, parent) + self.optList = optList - self.parent = controlParent + self.resetHandler = resetHandler + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) - wx.Panel.__init__(self, parent) - wx.StaticText(self, -1, "Save Path", (15, 10)) - self.savePathBox = wx.TextCtrl(self, -1, pos=(10, 30), size=(350, -1)) - self.idAsNameChk = wx.CheckBox(self, -1, 'ID as Name', (10, 70)) - self.aboutButton = wx.Button(self, label="About", pos=(380, 80), size=(100, 40)) - self.openButton = wx.Button(self, label="Open", pos=(380, 20), size=(100, 40)) - self.resetButton = wx.Button(self, label="Reset", pos=(380, 140), size=(100, 40)) - wx.StaticText(self, -1, "Settings: " + self.optList.settings_abs_path, (20, 155)) + svTextBox = wx.BoxSizer(wx.HORIZONTAL) + svTextBox.Add(wx.StaticText(self, label='Save Path')) + mainBoxSizer.Add(svTextBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP, border=20) + + svPathBox = wx.BoxSizer(wx.HORIZONTAL) + self.savePathBox = wx.TextCtrl(self) + svPathBox.Add(self.savePathBox, 1, flag = wx.TOP, border=10) + mainBoxSizer.Add(svPathBox, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=40) + + buttonsBox = wx.BoxSizer(wx.HORIZONTAL) + self.aboutButton = wx.Button(self, label='About', size=(110, 40)) + buttonsBox.Add(self.aboutButton) + self.openButton = wx.Button(self, label='Open', size=(110, 40)) + buttonsBox.Add(self.openButton, flag = wx.LEFT | wx.RIGHT, border=50) + self.resetButton = wx.Button(self, label='Reset Options', size=(110, 40)) + buttonsBox.Add(self.resetButton) + mainBoxSizer.Add(buttonsBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP, border=20) + + setngsBox = wx.BoxSizer(wx.HORIZONTAL) + text = 'Settings: ' + self.optList.settings_abs_path + setngsBox.Add(wx.StaticText(self, label=text), flag = wx.TOP, border=20) + mainBoxSizer.Add(setngsBox, flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM, border=10) + + self.SetSizer(mainBoxSizer) self.Bind(wx.EVT_BUTTON, self.OnAbout, self.aboutButton) self.Bind(wx.EVT_BUTTON, self.OnOpen, self.openButton) self.Bind(wx.EVT_BUTTON, self.OnReset, self.resetButton) def OnReset(self, event): - self.parent.reset() + self.resetHandler() def OnOpen(self, event): dlg = wx.DirDialog(None, "Choose directory") @@ -613,31 +1153,39 @@ info = wx.AboutDialogInfo() - info.SetIcon(wx.Icon('../icons/ytube.png', wx.BITMAP_TYPE_ICO)) + info.SetIcon(wx.Icon(ICON, wx.BITMAP_TYPE_ICO)) info.SetName(TITLE) info.SetVersion(__version__) info.SetDescription(description) - info.SetWebSite('https://github.com/MrS0m30n3/youtube-dl-gui') + info.SetWebSite('http://mrs0m30n3.github.io/youtube-dl-gui/') info.SetLicense(license) info.AddDeveloper('Sotiris Papadopoulos') wx.AboutBox(info) def load_options(self): self.savePathBox.SetValue(self.optList.savePath) - self.idAsNameChk.SetValue(self.optList.idAsName) def save_options(self): - self.optList.savePath = self.savePathBox.GetValue() - self.optList.idAsName = self.idAsNameChk.GetValue() + self.optList.savePath = abs_path(self.savePathBox.GetValue()) class OtherPanel(wx.Panel): def __init__(self, parent, optList): + wx.Panel.__init__(self, parent) + self.optList = optList + mainBoxSizer = wx.BoxSizer(wx.VERTICAL) - wx.Panel.__init__(self, parent) - wx.StaticText(self, -1, 'Command line arguments (e.g. --help)', (25, 20)) - self.cmdArgsBox = wx.TextCtrl(self, -1, pos=(20, 40), size=(450, -1)) + textBox = wx.BoxSizer(wx.HORIZONTAL) + textBox.Add(wx.StaticText(self, label='Command line arguments (e.g. --help)'), flag = wx.TOP, border=30) + mainBoxSizer.Add(textBox, flag = wx.LEFT, border=50) + + inputBox = wx.BoxSizer(wx.HORIZONTAL) + self.cmdArgsBox = wx.TextCtrl(self) + inputBox.Add(self.cmdArgsBox, 1, flag = wx.TOP, border=10) + mainBoxSizer.Add(inputBox, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border=50) + + self.SetSizer(mainBoxSizer) def load_options(self): self.cmdArgsBox.SetValue(self.optList.cmdArgs) @@ -647,30 +1195,38 @@ class OptionsFrame(wx.Frame): - def __init__(self, optionsList, parent=None, id=-1): - wx.Frame.__init__(self, parent, id, "Options", size=(540, 250)) + def __init__(self, optionsList, parent=None, id=-1, logger=None): + wx.Frame.__init__(self, parent, id, "Options", size=(580, 250)) self.optionsList = optionsList panel = wx.Panel(self) notebook = wx.Notebook(panel) - self.generalTab = GeneralPanel(notebook, self.optionsList, self) + self.generalTab = GeneralPanel(notebook, self.optionsList, self.reset) self.audioTab = AudioPanel(notebook, self.optionsList) self.connectionTab = ConnectionPanel(notebook, self.optionsList) self.videoTab = VideoPanel(notebook, self.optionsList) - self.downloadTab = DownloadPanel(notebook, self.optionsList) + self.filesysTab = FilesystemPanel(notebook, self.optionsList) self.subtitlesTab = SubtitlesPanel(notebook, self.optionsList) self.otherTab = OtherPanel(notebook, self.optionsList) self.updateTab = UpdatePanel(notebook, self.optionsList) + self.authTab = AuthenticationPanel(notebook, self.optionsList) + self.videoselTab = PlaylistPanel(notebook, self.optionsList) + self.logTab = LogPanel(notebook, self.optionsList, logger) + self.outputTab = OutputPanel(notebook, self.optionsList) notebook.AddPage(self.generalTab, "General") - notebook.AddPage(self.audioTab, "Audio") notebook.AddPage(self.videoTab, "Video") + notebook.AddPage(self.audioTab, "Audio") + notebook.AddPage(self.outputTab, "Output") + notebook.AddPage(self.videoselTab, "Playlist") notebook.AddPage(self.subtitlesTab, "Subtitles") - notebook.AddPage(self.downloadTab, "Download") + notebook.AddPage(self.filesysTab, "Filesystem") notebook.AddPage(self.connectionTab, "Connection") + notebook.AddPage(self.authTab, "Authentication") notebook.AddPage(self.updateTab, "Update") + notebook.AddPage(self.logTab, "Log") notebook.AddPage(self.otherTab, "Commands") sizer = wx.BoxSizer() @@ -683,8 +1239,18 @@ def OnClose(self, event): self.save_all_options() + if not file_exist(fix_path(self.optionsList.updatePath)+YOUTUBE_DL_FILENAME): + self.wrong_youtubedl_path() self.Destroy() + def wrong_youtubedl_path(self): + text = '''The path under Options>Update is invalid +please do one of the following: + *) restart youtube-dlG + *) click the update button + *) change the path to point where youtube-dl is''' + wx.MessageBox(text, 'Error', wx.OK | wx.ICON_EXCLAMATION) + def reset(self): self.optionsList.load_default() self.load_all_options() @@ -694,18 +1260,26 @@ self.audioTab.load_options() self.connectionTab.load_options() self.videoTab.load_options() - self.downloadTab.load_options() + self.filesysTab.load_options() self.subtitlesTab.load_options() self.otherTab.load_options() self.updateTab.load_options() + self.authTab.load_options() + self.videoselTab.load_options() + self.logTab.load_options() + self.outputTab.load_options() def save_all_options(self): self.generalTab.save_options() self.audioTab.save_options() self.connectionTab.save_options() self.videoTab.save_options() - self.downloadTab.save_options() + self.filesysTab.save_options() self.subtitlesTab.save_options() self.otherTab.save_options() self.updateTab.save_options() + self.authTab.save_options() + self.videoselTab.save_options() + self.logTab.save_options() + self.outputTab.save_options() diff -Nru youtube-dlg-0.3+git20140327/youtube_dl_gui/YoutubeDLInterpreter.py youtube-dlg-0.3.5/youtube_dl_gui/YoutubeDLInterpreter.py --- youtube-dlg-0.3+git20140327/youtube_dl_gui/YoutubeDLInterpreter.py 2014-03-27 16:04:53.000000000 +0000 +++ youtube-dlg-0.3.5/youtube_dl_gui/YoutubeDLInterpreter.py 2014-04-07 12:59:32.000000000 +0000 @@ -11,7 +11,8 @@ from .Utils import ( video_is_dash, get_os_type, - fix_path + fix_path, + add_PATH ) LANGUAGES = {"English":"en", @@ -23,7 +24,7 @@ "Spanish":"es", "German":"de"} -VIDEOFORMATS = {"highest available":"auto", +VIDEOFORMATS = {"default":"0", "mp4 [1280x720]":"22", "mp4 [640x360]":"18", "webm [640x360]":"43", @@ -38,6 +39,10 @@ "DASH m4a audio 128k":"140", "DASH webm audio 48k":"171"} +AUDIO_Q = {"high":"0", + "mid":"5", + "low":"9"} + class YoutubeDLInterpreter(): def __init__(self, optionsList, youtubeDLFile): @@ -46,12 +51,13 @@ self.opts = [] self.set_os() self.set_progress_opts() - self.set_download_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_output_opts() self.set_audio_opts() self.set_other_opts() @@ -61,40 +67,50 @@ def set_os(self): if get_os_type() == 'nt': self.opts = [self.youtubeDLFile] + add_PATH(self.optionsList.updatePath) else: path = fix_path(self.optionsList.updatePath) self.opts = ['python', path + self.youtubeDLFile] - def set_download_opts(self): - if (self.optionsList.rateLimit != '0' and self.optionsList.rateLimit != ''): - self.opts.append('-r') - self.opts.append(self.optionsList.rateLimit) - if (self.optionsList.retries != '10' and self.optionsList.retries != ''): - self.opts.append('-R') - self.opts.append(self.optionsList.retries) - if (self.optionsList.minFileSize != '0' and self.optionsList.minFileSize != ''): + def set_progress_opts(self): + ''' Do NOT change this option ''' + self.opts.append('--newline') + + def set_playlist_opts(self): + if self.optionsList.startTrack != 1: + self.opts.append('--playlist-start') + self.opts.append(str(self.optionsList.startTrack)) + if self.optionsList.endTrack != 0: + self.opts.append('--playlist-end') + self.opts.append(str(self.optionsList.endTrack)) + if self.optionsList.maxDownloads != 0: + self.opts.append('--max-downloads') + self.opts.append(str(self.optionsList.maxDownloads)) + if self.optionsList.minFileSize != '0': self.opts.append('--min-filesize') self.opts.append(self.optionsList.minFileSize) - if (self.optionsList.maxFileSize != '0' and self.optionsList.maxFileSize != ''): + if self.optionsList.maxFileSize != '0': self.opts.append('--max-filesize') self.opts.append(self.optionsList.maxFileSize) - if self.optionsList.writeDescription: - self.opts.append('--write-description') - if self.optionsList.writeInfo: - self.opts.append('--write-info-json') - if self.optionsList.writeThumbnail: - self.opts.append('--write-thumbnail') - def set_progress_opts(self): - self.opts.append('--newline') + def set_auth_opts(self): + if self.optionsList.username != '': + self.opts.append('-u') + self.opts.append(self.optionsList.username) + if self.optionsList.password != '': + self.opts.append('-p') + self.opts.append(self.optionsList.password) + if self.optionsList.videoPass != '': + self.opts.append('--video-password') + self.opts.append(self.optionsList.videoPass) def set_connection_opts(self): + if self.optionsList.retries != 10: + self.opts.append('-R') + self.opts.append(str(self.optionsList.retries)) if self.optionsList.proxy != '': self.opts.append('--proxy') self.opts.append(self.optionsList.proxy) - if self.optionsList.username != '': - self.opts.append('--username') - self.opts.append(self.optionsList.username) if self.optionsList.userAgent != '': self.opts.append('--user-agent') self.opts.append(self.optionsList.userAgent) @@ -103,7 +119,7 @@ self.opts.append(self.optionsList.referer) def set_video_opts(self): - if self.optionsList.videoFormat != 'highest available': + if self.optionsList.videoFormat != 'default': self.opts.append('-f') if video_is_dash(self.optionsList.videoFormat): vf = VIDEOFORMATS[self.optionsList.videoFormat] @@ -115,16 +131,15 @@ else: self.opts.append(VIDEOFORMATS[self.optionsList.videoFormat]) - def set_playlist_opts(self): - if (self.optionsList.startTrack != '1' and self.optionsList.startTrack != ''): - self.opts.append('--playlist-start') - self.opts.append(self.optionsList.startTrack) - if (self.optionsList.endTrack != '0' and self.optionsList.endTrack != ''): - self.opts.append('--playlist-end') - self.opts.append(self.optionsList.endTrack) - if (self.optionsList.maxDownloads != '0' and self.optionsList.maxDownloads != ''): - self.opts.append('--max-downloads') - self.opts.append(self.optionsList.maxDownloads) + def set_filesystem_opts(self): + if self.optionsList.ignoreErrors: + self.opts.append('-i') + if self.optionsList.writeDescription: + self.opts.append('--write-description') + if self.optionsList.writeInfo: + self.opts.append('--write-info-json') + if self.optionsList.writeThumbnail: + self.opts.append('--write-thumbnail') def set_subtitles_opts(self): if self.optionsList.writeAllSubs: @@ -132,35 +147,36 @@ if (self.optionsList.writeAutoSubs): self.opts.append('--write-auto-sub') if self.optionsList.writeSubs: - self.opts.append('--write-subs') + self.opts.append('--write-sub') if self.optionsList.subsLang != 'English': self.opts.append('--sub-lang') self.opts.append(LANGUAGES[self.optionsList.subsLang]) + if self.optionsList.embedSubs: + self.opts.append('--embed-subs') def set_output_opts(self): path = fix_path(self.optionsList.savePath) self.opts.append('-o') - if self.optionsList.idAsName: + if self.optionsList.outputFormat == 'id': self.opts.append(path + '%(id)s.%(ext)s') - else: + elif self.optionsList.outputFormat == 'title': self.opts.append(path + '%(title)s.%(ext)s') + elif self.optionsList.outputFormat == 'custom': + self.opts.append(path + self.optionsList.outputTemplate) def set_audio_opts(self): if self.optionsList.toAudio: self.opts.append('-x') self.opts.append('--audio-format') self.opts.append(self.optionsList.audioFormat) - if self.optionsList.audioQuality != 5: + if self.optionsList.audioQuality != 'mid': self.opts.append('--audio-quality') - self.opts.append(str(self.optionsList.audioQuality)) + self.opts.append(AUDIO_Q[self.optionsList.audioQuality]) if self.optionsList.keepVideo: self.opts.append('-k') def set_other_opts(self): - if self.optionsList.ignoreErrors: - self.opts.append('-i') if self.optionsList.cmdArgs != '': for option in self.optionsList.cmdArgs.split(): self.opts.append(option) - \ No newline at end of file