Browse Source

Version 0.3.3

Refactor code
Fix DASH to Audio bug
	When "Clear DASH audio/video files"
	option was enable. Youtube-dlG would
	also remove audio output files
	*.mp3, *.wav, etc..

SignalHandler.py
	Add DataPack class
	Add OutputHandler class

DownloadThread.py
	Now OutputHandler handles the stdout

Reduce user input errors:
 OptionsHandler.py
	Change to int
     --> startTrack, endTrack, maxDownloads, retries

 YoutubeDLInterpreter.py
	Remove empty string check
     --> startTrack, endTrack, maxDownloads, retries

 YoutubeDLGUI.py
	Change wx.TextCtrl to wx.SpinCtrl
     --> startTrack, endTrack, maxDownloads, retries

	Add check_input function for
     --> maxFileSize, minFileSize
doc-issue-template
MrS0m30n3 11 years ago
parent
commit
365e1a49a3
10 changed files with 290 additions and 228 deletions
  1. 73
      SIGNALS.txt
  2. 135
      youtube_dl_gui/DownloadThread.py
  3. 8
      youtube_dl_gui/LogManager.py
  4. 20
      youtube_dl_gui/OptionsHandler.py
  5. 137
      youtube_dl_gui/SignalHandler.py
  6. 5
      youtube_dl_gui/UpdateThread.py
  7. 16
      youtube_dl_gui/Utils.py
  8. 102
      youtube_dl_gui/YoutubeDLGUI.py
  9. 20
      youtube_dl_gui/YoutubeDLInterpreter.py
  10. 2
      youtube_dl_gui/version.py

73
SIGNALS.txt

@ -1,5 +1,6 @@
Signals list, that DownloadManager thread sends (DownloadThread.py)
Signals list that DownloadManager, ProcessWrapper
threads send (DownloadThread.py)
HANDLER HANDLER
======= =======
@ -12,39 +13,39 @@ SignalHandler.py
. .
. .
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]
['[download]', '0.1%', 'of', '4.42MiB', 'at', '63.81KiB/s', 'ETA', '01:10', 0]
HEADER
======
[
'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 DASH extra 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

135
youtube_dl_gui/DownloadThread.py

@ -8,13 +8,17 @@ 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 .SignalHandler import (
DataPack,
OutputHandler
)
from .Utils import ( from .Utils import (
remove_spaces,
string_to_array,
get_encoding, get_encoding,
encode_list, encode_list,
remove_file, remove_file,
get_os_type
get_os_type,
file_exist
) )
MAX_DOWNLOAD_THREADS = 3 MAX_DOWNLOAD_THREADS = 3
@ -62,12 +66,17 @@ class DownloadManager(Thread):
self.running = False self.running = False
else: else:
sleep(1) sleep(1)
# If we reach here close down all child threads # If we reach here close down all child threads
self.terminate_all() self.terminate_all()
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['finish', -1])
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('finish'))
def downloading(self):
for proc in self.procList:
if proc.isAlive():
return True
return False
def add_download_item(self, downloadItem):
def _add_download_item(self, downloadItem):
self.downloadlist.append(downloadItem) self.downloadlist.append(downloadItem)
def extract_data(self): def extract_data(self):
@ -82,12 +91,6 @@ class DownloadManager(Thread):
proc.close() proc.close()
proc.join() 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, t=1):
for proc in self.procList: for proc in self.procList:
if not self.running: break if not self.running: break
@ -99,7 +102,7 @@ class DownloadManager(Thread):
def close(self): def close(self):
self.procNo = 0 self.procNo = 0
self.running = False self.running = False
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['close', -1])
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, DataPack('close'))
class ProcessWrapper(Thread): class ProcessWrapper(Thread):
@ -111,89 +114,69 @@ class ProcessWrapper(Thread):
self.log = log self.log = log
self.url = url self.url = url
self.filenames = [] self.filenames = []
self.proc = None
self.stopped = False self.stopped = False
self.err = False
self.error = False
self.proc = None
self.start() self.start()
def run(self): 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 subprocess is alive and NOT the current thread
while self.proc_is_alive(): while self.proc_is_alive():
# read output
output = self.read()
if output != '':
if self.err:
self.write_to_log(output)
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['error', self.index])
else:
# process output
data = self.proc_output(output)
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])
# read stdout, stderr from proc
stdout, stderr = self.read()
if stdout != '':
# pass stdout to output handler
data = OutputHandler(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): def write_to_log(self, data):
if self.log != None: if self.log != None:
self.log.write(data) self.log.write(data)
def extract_filename(self, data):
data_list = data.split(':', 1)
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:
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) remove_file(f)
def close(self): def close(self):
self.proc.kill() self.proc.kill()
self.stopped = True 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): def proc_is_alive(self):
return self.proc.poll() == None return self.proc.poll() == None
def read(self): 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): def get_cmd(self):
enc = get_encoding() enc = get_encoding()
if enc != None: if enc != None:
@ -202,7 +185,7 @@ class ProcessWrapper(Thread):
cmd = self.options + [self.url] cmd = self.options + [self.url]
return cmd return cmd
def set_process_info(self):
def get_process_info(self):
if get_os_type() == 'nt': if get_os_type() == 'nt':
info = subprocess.STARTUPINFO() info = subprocess.STARTUPINFO()
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW info.dwFlags |= subprocess.STARTF_USESHOWWINDOW

8
youtube_dl_gui/LogManager.py

@ -18,17 +18,17 @@ class LogManager():
self.path = fix_path(path) + LOG_FILENAME self.path = fix_path(path) + LOG_FILENAME
self.add_time = add_time self.add_time = add_time
self.init_log() self.init_log()
self.check_log()
self.auto_clear_log()
def check_log(self):
if self.log_size() > LOG_FILESIZE:
def auto_clear_log(self):
if self.size() > LOG_FILESIZE:
self.clear() self.clear()
def init_log(self): def init_log(self):
if not file_exist(self.path): if not file_exist(self.path):
self.clear() self.clear()
def log_size(self):
def size(self):
return get_filesize(self.path) return get_filesize(self.path)
def clear(self): def clear(self):

20
youtube_dl_gui/OptionsHandler.py

@ -38,9 +38,9 @@ class OptionsHandler():
self.keepVideo = False self.keepVideo = False
self.audioFormat = "mp3" self.audioFormat = "mp3"
self.audioQuality = 5 self.audioQuality = 5
self.startTrack = "1"
self.endTrack = "0"
self.maxDownloads = "0"
self.startTrack = 1
self.endTrack = 0
self.maxDownloads = 0
self.minFileSize = "0" self.minFileSize = "0"
self.maxFileSize = "0" self.maxFileSize = "0"
self.writeSubs = False self.writeSubs = False
@ -53,7 +53,7 @@ class OptionsHandler():
self.writeDescription = False self.writeDescription = False
self.writeInfo = False self.writeInfo = False
self.writeThumbnail = False self.writeThumbnail = False
self.retries = "10"
self.retries = 10
self.userAgent = "" self.userAgent = ""
self.referer = "" self.referer = ""
self.proxy = "" self.proxy = ""
@ -112,9 +112,9 @@ class OptionsHandler():
self.keepVideo = opts[5] in ['True'] self.keepVideo = opts[5] in ['True']
self.audioFormat = opts[6] self.audioFormat = opts[6]
self.audioQuality = int(opts[7]) self.audioQuality = int(opts[7])
self.startTrack = opts[8]
self.endTrack = opts[9]
self.maxDownloads = opts[10]
self.startTrack = int(opts[8])
self.endTrack = int(opts[9])
self.maxDownloads = int(opts[10])
self.minFileSize = opts[11] self.minFileSize = opts[11]
self.maxFileSize = opts[12] self.maxFileSize = opts[12]
self.writeSubs = opts[13] in ['True'] self.writeSubs = opts[13] in ['True']
@ -127,7 +127,7 @@ class OptionsHandler():
self.writeDescription = opts[20] in ['True'] self.writeDescription = opts[20] in ['True']
self.writeInfo = opts[21] in ['True'] self.writeInfo = opts[21] in ['True']
self.writeThumbnail = opts[22] in ['True'] self.writeThumbnail = opts[22] in ['True']
self.retries = opts[23]
self.retries = int(opts[23])
self.userAgent = opts[24] self.userAgent = opts[24]
self.referer = opts[25] self.referer = opts[25]
self.proxy = opts[26] self.proxy = opts[26]
@ -138,10 +138,10 @@ class OptionsHandler():
self.enableLog = opts[31] in ['True'] self.enableLog = opts[31] in ['True']
self.writeTimeToLog = opts[32] in ['True'] self.writeTimeToLog = opts[32] in ['True']
except: except:
self.statusBarWrite('Error while loading settings file')
self.statusBarWrite('Error while loading settings file. Loading default settings.')
self.load_default() self.load_default()
else: else:
self.statusBarWrite('Old settings file loading default settings')
self.statusBarWrite('Old settings file. Loading default settings.')
self.load_default() self.load_default()
def save_to_file(self): def save_to_file(self):

137
youtube_dl_gui/SignalHandler.py

@ -1,16 +1,21 @@
#! /usr/bin/env python #! /usr/bin/env python
from .Utils import (
remove_spaces,
string_to_array
)
class DownloadHandler(): class DownloadHandler():
def __init__(self, ListCtrl): def __init__(self, ListCtrl):
self.ListCtrl = ListCtrl self.ListCtrl = ListCtrl
self.finished = False self.finished = False
self.close = False
self.closed = False
self.error = False self.error = False
self.handlers = [] self.handlers = []
def _has_closed(self): def _has_closed(self):
return self.close
return self.closed
def _has_finished(self): def _has_finished(self):
return self.finished return self.finished
@ -20,53 +25,52 @@ class DownloadHandler():
def handle(self, msg): def handle(self, msg):
''' Handles msg base to Signals.txt ''' ''' Handles msg base to Signals.txt '''
data = msg.data
index = self.get_index(data)
self.check_for_error(data)
pack = msg.data
index = pack.index
''' Manage global index = -1 '''
if index == -1: if index == -1:
if data[0] == 'finish':
if pack.header == 'close':
self.closed = True
elif pack.header == 'finish':
self.finished = True self.finished = True
elif data[0] == 'close':
self.close = True
else: else:
''' Manage handlers for its index ''' ''' Manage handlers for its index '''
if index == len(self.handlers): if index == len(self.handlers):
''' Create new IndexDownloadHandler and add it to handlers ''' ''' Create new IndexDownloadHandler and add it to handlers '''
self.handlers.append(IndexDownloadHandler(self.ListCtrl, index)) self.handlers.append(IndexDownloadHandler(self.ListCtrl, index))
''' Let IndexDownloadHandler handle message data for current index ''' ''' Let IndexDownloadHandler handle message data for current index '''
self.handlers[index].handle(data)
def get_index(self, data):
return data.pop()
def check_for_error(self, data):
if data[0] == 'error':
self.error = True
self.handlers[index].handle(pack)
if self.handlers[index].has_error():
self.error = True
class IndexDownloadHandler(): class IndexDownloadHandler():
def __init__(self, ListCtrl, index): def __init__(self, ListCtrl, index):
self.ListCtrl = ListCtrl self.ListCtrl = ListCtrl
self.index = index self.index = index
self.err = False
self.info = '' self.info = ''
def handle(self, data):
''' Handle its data message for current index '''
if data[0] == 'finish':
def has_error(self):
return self.err
def handle(self, pack):
''' Handle its pack for current index '''
if pack.header == 'finish':
self.finish() self.finish()
elif data[0] == 'close':
elif pack.header == 'close':
self.close() self.close()
elif data[0] == 'error':
elif pack.header == 'error':
self.error() self.error()
elif data[0] == 'playlist':
self.playlist(data)
elif data[0] == '[youtube]':
elif pack.header == 'playlist':
self.playlist(pack.data)
elif pack.header == 'youtube':
self.pre_proc() self.pre_proc()
elif data[0] == '[download]':
self.download(data)
elif data[0] == '[ffmpeg]':
elif pack.header == 'download':
self.download(pack.data)
elif pack.header == 'ffmpeg':
self.post_proc() self.post_proc()
elif data[0] == 'remove':
elif pack.header == 'remove':
self.remove() self.remove()
def finish(self): def finish(self):
@ -79,6 +83,7 @@ class IndexDownloadHandler():
self.ListCtrl._write_data(self.index, 5, 'Stopped') self.ListCtrl._write_data(self.index, 5, 'Stopped')
def error(self): def error(self):
self.err = True
self.ListCtrl._write_data(self.index, 3, '') self.ListCtrl._write_data(self.index, 3, '')
self.ListCtrl._write_data(self.index, 4, '') self.ListCtrl._write_data(self.index, 4, '')
self.ListCtrl._write_data(self.index, 5, 'Error') self.ListCtrl._write_data(self.index, 5, 'Error')
@ -91,10 +96,10 @@ class IndexDownloadHandler():
self.ListCtrl._write_data(self.index, 5, 'Post-Processing %s' % self.info) self.ListCtrl._write_data(self.index, 5, 'Post-Processing %s' % self.info)
def download(self, data): def download(self, data):
self.ListCtrl._write_data(self.index, 1, data[3])
self.ListCtrl._write_data(self.index, 1, data[0])
self.ListCtrl._write_data(self.index, 2, data[1]) 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, 3, data[2])
self.ListCtrl._write_data(self.index, 4, data[3])
self.ListCtrl._write_data(self.index, 5, 'Downloading %s' % self.info) self.ListCtrl._write_data(self.index, 5, 'Downloading %s' % self.info)
def playlist(self, data): def playlist(self, data):
@ -102,8 +107,70 @@ class IndexDownloadHandler():
self.ListCtrl._write_data(self.index, 2, '') self.ListCtrl._write_data(self.index, 2, '')
self.ListCtrl._write_data(self.index, 3, '') self.ListCtrl._write_data(self.index, 3, '')
self.ListCtrl._write_data(self.index, 4, '') self.ListCtrl._write_data(self.index, 4, '')
self.info = '%s/%s' % (data[1], data[2])
self.info = '%s/%s' % (data[0], data[1])
def remove(self): def remove(self):
self.ListCtrl._write_data(self.index, 5, 'Removing DASH %s' % self.info)
self.ListCtrl._write_data(self.index, 5, 'Removing DASH Files')
class DataPack():
def __init__(self, header, index=-1, data=None):
self.header = header
self.index = index
self.data = data
class OutputHandler():
def __init__(self, stdout):
dataPack = None
self.stdout = remove_spaces(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

5
youtube_dl_gui/UpdateThread.py

@ -1,9 +1,10 @@
#! /usr/bin/env python #! /usr/bin/env python
from threading import Thread
from wx import CallAfter 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 threading import Thread
from urllib2 import urlopen, URLError, HTTPError from urllib2 import urlopen, URLError, HTTPError
from .Utils import ( from .Utils import (
@ -27,7 +28,7 @@ class UpdateThread(Thread):
self.start() self.start()
def run(self): def run(self):
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, "Downloading latest youtube-dl...")
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, "Downloading latest youtube-dl. Please wait...")
try: try:
f = urlopen(self.url, timeout=DOWNLOAD_TIMEOUT) f = urlopen(self.url, timeout=DOWNLOAD_TIMEOUT)
with open(self.updatePath + self.youtubeDLFile, 'wb') as lf: with open(self.updatePath + self.youtubeDLFile, 'wb') as lf:

16
youtube_dl_gui/Utils.py

@ -12,7 +12,6 @@ def string_to_array(string, char=' '):
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 sys.getfilesystemencoding() return sys.getfilesystemencoding()
else: else:
@ -28,14 +27,10 @@ def have_dash_audio(audio):
return audio != "NO SOUND" return audio != "NO SOUND"
def remove_file(filename): def remove_file(filename):
if file_exist(filename):
os.remove(filename)
os.remove(filename)
def get_path_seperator(): def get_path_seperator():
if os.name == 'nt':
return '\\'
else:
return '/'
return '\\' if os.name == 'nt' else '/'
def fix_path(path): def fix_path(path):
if path != '': if path != '':
@ -50,12 +45,11 @@ def add_PATH(path):
os.environ["PATH"] += os.pathsep + path os.environ["PATH"] += os.pathsep + path
def get_abs_path(path): def get_abs_path(path):
sep = get_path_seperator()
path_list = path.split(sep)
path_list = path.split(get_path_seperator())
for i in range(len(path_list)): for i in range(len(path_list)):
if path_list[i] == '~': if path_list[i] == '~':
path_list[i] = get_HOME() path_list[i] = get_HOME()
path = sep.join(path_list)
path = get_path_seperator().join(path_list)
return path return path
def file_exist(filename): def file_exist(filename):
@ -78,4 +72,4 @@ def get_icon_path(icon_path, file_path):
path = get_path_seperator().join(path) path = get_path_seperator().join(path)
return path return path

102
youtube_dl_gui/YoutubeDLGUI.py

@ -157,12 +157,8 @@ class MainFrame(wx.Frame):
def status_bar_write(self, msg): def status_bar_write(self, msg):
self.statusBar.SetLabel(msg) self.statusBar.SetLabel(msg)
def fin_task(self, error):
if error:
msg = 'An error occured while downloading. See Options>Log.'
self.status_bar_write(msg)
else:
self.status_bar_write('Done')
def fin_task(self, msg):
self.status_bar_write(msg)
self.downloadButton.SetLabel('Download') self.downloadButton.SetLabel('Download')
self.updateButton.Enable() self.updateButton.Enable()
self.downloadThread.join() self.downloadThread.join()
@ -176,7 +172,14 @@ class MainFrame(wx.Frame):
if self.downloadHandler._has_closed(): if self.downloadHandler._has_closed():
self.status_bar_write('Stoping downloads') self.status_bar_write('Stoping downloads')
if self.downloadHandler._has_finished(): if self.downloadHandler._has_finished():
self.fin_task(self.downloadHandler._has_error())
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): def update_handler(self, msg):
if msg.data == 'finish': if msg.data == 'finish':
@ -189,15 +192,17 @@ class MainFrame(wx.Frame):
def stop_download(self): def stop_download(self):
self.downloadThread.close() self.downloadThread.close()
def start_download(self, trackList):
self.statusList._clear_list()
def load_tracklist(self, trackList):
for url in trackList: for url in trackList:
if url != '': if url != '':
self.urlList.append(url) self.urlList.append(url)
self.statusList._add_item(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(): if not self.statusList._is_empty():
options = YoutubeDLInterpreter(self.optionsList, YOUTUBE_DL_FILENAME).get_options() options = YoutubeDLInterpreter(self.optionsList, YOUTUBE_DL_FILENAME).get_options()
self.status_bar_write('Download started')
self.downloadThread = DownloadManager( self.downloadThread = DownloadManager(
options, options,
self.statusList._get_items(), self.statusList._get_items(),
@ -205,6 +210,7 @@ class MainFrame(wx.Frame):
self.logManager self.logManager
) )
self.downloadHandler = DownloadHandler(self.statusList) self.downloadHandler = DownloadHandler(self.statusList)
self.status_bar_write('Download started')
self.downloadButton.SetLabel('Stop') self.downloadButton.SetLabel('Stop')
self.updateButton.Disable() self.updateButton.Disable()
else: else:
@ -230,25 +236,24 @@ class MainFrame(wx.Frame):
''' Add url into original download list ''' ''' Add url into original download list '''
self.urlList.append(url) self.urlList.append(url)
''' Add url into statusList ''' ''' Add url into statusList '''
index = self.statusList._add_item(url)
self.statusList._add_item(url)
''' Retrieve last item as {url:url, index:indexNo} ''' ''' Retrieve last item as {url:url, index:indexNo} '''
item = self.statusList._get_last_item() item = self.statusList._get_last_item()
''' Pass that item into downloadThread ''' ''' Pass that item into downloadThread '''
self.downloadThread.add_download_item(item)
self.downloadThread._add_download_item(item)
def OnDownload(self, event): def OnDownload(self, event):
if self.downloadThread != None: if self.downloadThread != None:
self.stop_download() self.stop_download()
else: else:
self.start_download(self.trackList.GetValue().split('\n'))
self.start_download()
def OnUpdate(self, event): def OnUpdate(self, event):
if (self.downloadThread == None and self.updateThread == None): if (self.downloadThread == None and self.updateThread == None):
self.status_bar_write("Updating youtube-dl...")
self.update_youtube_dl() self.update_youtube_dl()
def OnOptions(self, event): def OnOptions(self, event):
optionsFrame = OptionsFrame(self.optionsList, self, logger=self.logManager)
optionsFrame = OptionsFrame(self.optionsList, parent=self, logger=self.logManager)
optionsFrame.Show() optionsFrame.Show()
def OnClose(self, event): def OnClose(self, event):
@ -271,7 +276,6 @@ class ListCtrl(wx.ListCtrl):
def _add_item(self, item): def _add_item(self, item):
self.InsertStringItem(self.ListIndex, item) self.InsertStringItem(self.ListIndex, item)
self.ListIndex += 1 self.ListIndex += 1
return self.ListIndex
''' Write data on index, column ''' ''' Write data on index, column '''
def _write_data(self, index, column, data): def _write_data(self, index, column, data):
@ -307,10 +311,13 @@ class ListCtrl(wx.ListCtrl):
class LogPanel(wx.Panel): class LogPanel(wx.Panel):
size = ''
path = ''
def __init__(self, parent, optList, log): def __init__(self, parent, optList, log):
self.optList = optList self.optList = optList
self.log = log self.log = log
self.size, self.path = self.set_data()
self.set_data()
wx.Panel.__init__(self, parent) wx.Panel.__init__(self, parent)
self.enableLogChk = wx.CheckBox(self, -1, 'Enable log', (240, 20)) self.enableLogChk = wx.CheckBox(self, -1, 'Enable log', (240, 20))
@ -318,7 +325,7 @@ class LogPanel(wx.Panel):
self.clearLogButton = wx.Button(self, label="Clear Log", pos=(200, 90)) self.clearLogButton = wx.Button(self, label="Clear Log", pos=(200, 90))
self.viewLogButton = wx.Button(self, label="View Log", pos=(300, 90)) self.viewLogButton = wx.Button(self, label="View Log", pos=(300, 90))
wx.StaticText(self, -1, 'Path: ' + self.path, (180, 140)) wx.StaticText(self, -1, 'Path: ' + self.path, (180, 140))
self.sizeText = wx.StaticText(self, -1, 'Log Size: ' + self.size, (230, 170))
self.sizeText = wx.StaticText(self, -1, 'Log Size: ' + self.size, (240, 170))
self.Bind(wx.EVT_CHECKBOX, self.OnEnable, self.enableLogChk) self.Bind(wx.EVT_CHECKBOX, self.OnEnable, self.enableLogChk)
self.Bind(wx.EVT_CHECKBOX, self.OnTime, self.enableTimeChk) self.Bind(wx.EVT_CHECKBOX, self.OnTime, self.enableTimeChk)
@ -327,12 +334,8 @@ class LogPanel(wx.Panel):
def set_data(self): def set_data(self):
if self.log != None: if self.log != None:
size = str(self.log.log_size()) + ' Bytes'
path = self.log.path
else:
size = ''
path = ''
return size, path
self.size = str(self.log.size()) + ' Bytes'
self.path = self.log.path
def OnTime(self, event): def OnTime(self, event):
if self.log != None: if self.log != None:
@ -401,30 +404,42 @@ class PlaylistPanel(wx.Panel):
wx.Panel.__init__(self, parent) wx.Panel.__init__(self, parent)
wx.StaticText(self, -1, 'Playlist Options', (100, 20)) wx.StaticText(self, -1, 'Playlist Options', (100, 20))
wx.StaticText(self, -1, 'Start', (90, 53)) wx.StaticText(self, -1, 'Start', (90, 53))
self.startBox = wx.TextCtrl(self, -1, pos=(160, 50), size=(50, -1))
self.startSpnr = wx.SpinCtrl(self, -1, "", (160, 50), size=(60, -1))
wx.StaticText(self, -1, 'Stop', (90, 83)) wx.StaticText(self, -1, 'Stop', (90, 83))
self.stopBox = wx.TextCtrl(self, -1, pos=(160, 80), size=(50, -1))
self.stopSpnr = wx.SpinCtrl(self, -1, "", (160, 80), size=(60, -1))
wx.StaticText(self, -1, 'Max DLs', (90, 113)) wx.StaticText(self, -1, 'Max DLs', (90, 113))
self.maxBox = wx.TextCtrl(self, -1, pos=(160, 110), size=(50, -1))
self.maxSpnr = wx.SpinCtrl(self, -1, "", (160, 110), size=(60, -1))
wx.StaticText(self, -1, 'Filesize (e.g. 50k or 44.6m)', (330, 20)) wx.StaticText(self, -1, 'Filesize (e.g. 50k or 44.6m)', (330, 20))
wx.StaticText(self, -1, 'Min', (360, 63)) wx.StaticText(self, -1, 'Min', (360, 63))
self.minFilesizeBox = wx.TextCtrl(self, -1, pos=(400, 60), size=(70, -1)) self.minFilesizeBox = wx.TextCtrl(self, -1, pos=(400, 60), size=(70, -1))
wx.StaticText(self, -1, 'Max', (360, 93)) wx.StaticText(self, -1, 'Max', (360, 93))
self.maxFilesizeBox = wx.TextCtrl(self, -1, pos=(400, 90), size=(70, -1)) self.maxFilesizeBox = wx.TextCtrl(self, -1, pos=(400, 90), size=(70, -1))
self.startSpnr.SetRange(1, 999)
self.stopSpnr.SetRange(0, 999)
self.maxSpnr.SetRange(0, 999)
def load_options(self): def load_options(self):
self.startBox.SetValue(self.optList.startTrack)
self.stopBox.SetValue(self.optList.endTrack)
self.maxBox.SetValue(self.optList.maxDownloads)
self.startSpnr.SetValue(self.optList.startTrack)
self.stopSpnr.SetValue(self.optList.endTrack)
self.maxSpnr.SetValue(self.optList.maxDownloads)
self.minFilesizeBox.SetValue(self.optList.minFileSize) self.minFilesizeBox.SetValue(self.optList.minFileSize)
self.maxFilesizeBox.SetValue(self.optList.maxFileSize) self.maxFilesizeBox.SetValue(self.optList.maxFileSize)
def save_options(self): def save_options(self):
self.optList.startTrack = self.startBox.GetValue()
self.optList.endTrack = self.stopBox.GetValue()
self.optList.maxDownloads = self.maxBox.GetValue()
self.optList.startTrack = self.startSpnr.GetValue()
self.optList.endTrack = self.stopSpnr.GetValue()
self.optList.maxDownloads = self.maxSpnr.GetValue()
self.optList.minFileSize = self.minFilesizeBox.GetValue() self.optList.minFileSize = self.minFilesizeBox.GetValue()
self.optList.maxFileSize = self.maxFilesizeBox.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 ConnectionPanel(wx.Panel): class ConnectionPanel(wx.Panel):
@ -433,7 +448,8 @@ class ConnectionPanel(wx.Panel):
wx.Panel.__init__(self, parent) wx.Panel.__init__(self, parent)
wx.StaticText(self, -1, 'Retries', (15, 12)) wx.StaticText(self, -1, 'Retries', (15, 12))
self.retriesBox = wx.TextCtrl(self, -1, pos=(65, 10), size=(50, -1))
self.retriesSpnr = wx.SpinCtrl(self, -1, "", (65, 10), size=(50, -1))
self.retriesSpnr.SetRange(1, 99)
wx.StaticText(self, -1, 'User Agent', (15, 50)) wx.StaticText(self, -1, 'User Agent', (15, 50))
self.userAgentBox = wx.TextCtrl(self, -1, pos=(10, 70), size=(320, -1)) self.userAgentBox = wx.TextCtrl(self, -1, pos=(10, 70), size=(320, -1))
wx.StaticText(self, -1, 'Referer', (15, 100)) wx.StaticText(self, -1, 'Referer', (15, 100))
@ -445,13 +461,13 @@ class ConnectionPanel(wx.Panel):
self.userAgentBox.SetValue(self.optList.userAgent) self.userAgentBox.SetValue(self.optList.userAgent)
self.refererBox.SetValue(self.optList.referer) self.refererBox.SetValue(self.optList.referer)
self.proxyBox.SetValue(self.optList.proxy) self.proxyBox.SetValue(self.optList.proxy)
self.retriesBox.SetValue(self.optList.retries)
self.retriesSpnr.SetValue(self.optList.retries)
def save_options(self): def save_options(self):
self.optList.userAgent = self.userAgentBox.GetValue() self.optList.userAgent = self.userAgentBox.GetValue()
self.optList.referer = self.refererBox.GetValue() self.optList.referer = self.refererBox.GetValue()
self.optList.proxy = self.proxyBox.GetValue() self.optList.proxy = self.proxyBox.GetValue()
self.optList.retries = self.retriesBox.GetValue()
self.optList.retries = self.retriesSpnr.GetValue()
class AuthenticationPanel(wx.Panel): class AuthenticationPanel(wx.Panel):
@ -459,9 +475,9 @@ class AuthenticationPanel(wx.Panel):
self.optList = optList self.optList = optList
wx.Panel.__init__(self,parent) wx.Panel.__init__(self,parent)
wx.StaticText(self, -1, 'Username', (250, 10))
wx.StaticText(self, -1, 'Username', (255, 10))
self.usernameBox = wx.TextCtrl(self, -1, pos=(175, 30), size=(230, 25)) self.usernameBox = wx.TextCtrl(self, -1, pos=(175, 30), size=(230, 25))
wx.StaticText(self, -1, 'Password', (255, 70))
wx.StaticText(self, -1, 'Password', (260, 70))
self.passwordBox = wx.TextCtrl(self, -1, pos=(175, 90), size=(230, 25), style = wx.TE_PASSWORD) self.passwordBox = wx.TextCtrl(self, -1, pos=(175, 90), size=(230, 25), style = wx.TE_PASSWORD)
wx.StaticText(self, -1, 'Video Password (vimeo, smotri)', (190, 130)) wx.StaticText(self, -1, 'Video Password (vimeo, smotri)', (190, 130))
self.videopassBox = wx.TextCtrl(self, -1, pos=(175, 150), size=(230, 25), style = wx.TE_PASSWORD) self.videopassBox = wx.TextCtrl(self, -1, pos=(175, 150), size=(230, 25), style = wx.TE_PASSWORD)
@ -482,12 +498,12 @@ class AudioPanel(wx.Panel):
self.optList = optList self.optList = optList
wx.Panel.__init__(self, parent) wx.Panel.__init__(self, parent)
self.toAudioChk = wx.CheckBox(self, -1, 'Convert to Audio', (225, 10))
self.keepVideoChk = wx.CheckBox(self, -1, 'Keep Video', (245, 40))
self.toAudioChk = wx.CheckBox(self, -1, 'Convert to Audio', (220, 10))
self.keepVideoChk = wx.CheckBox(self, -1, 'Keep Video', (240, 40))
wx.StaticText(self, -1, 'Audio Format', (250, 80)) wx.StaticText(self, -1, 'Audio Format', (250, 80))
self.audioFormatCombo = wx.ComboBox(self, choices=AUDIOFORMATS, pos=(210, 100), size=(160, 30)) self.audioFormatCombo = wx.ComboBox(self, choices=AUDIOFORMATS, pos=(210, 100), size=(160, 30))
wx.StaticText(self, -1, "Audio Quality 0 (best) 9 (worst)", (200, 140)) wx.StaticText(self, -1, "Audio Quality 0 (best) 9 (worst)", (200, 140))
self.audioQualitySpnr = wx.SpinCtrl(self, -1, "", (245, 160))
self.audioQualitySpnr = wx.SpinCtrl(self, -1, "", (248, 160), size=(90, 20))
self.audioQualitySpnr.SetRange(0, 9) self.audioQualitySpnr.SetRange(0, 9)
self.Bind(wx.EVT_CHECKBOX, self.OnAudioCheck, self.toAudioChk) self.Bind(wx.EVT_CHECKBOX, self.OnAudioCheck, self.toAudioChk)
@ -686,7 +702,7 @@ class GeneralPanel(wx.Panel):
self.parent = controlParent self.parent = controlParent
wx.Panel.__init__(self, parent) wx.Panel.__init__(self, parent)
wx.StaticText(self, -1, "Save Path", (250, 20))
wx.StaticText(self, -1, "Save Path", (255, 20))
self.savePathBox = wx.TextCtrl(self, -1, pos=(60, 50), size=(450, -1)) self.savePathBox = wx.TextCtrl(self, -1, pos=(60, 50), size=(450, -1))
self.aboutButton = wx.Button(self, label="About", pos=(70, 100), size=(110, 40)) self.aboutButton = wx.Button(self, label="About", pos=(70, 100), size=(110, 40))
self.openButton = wx.Button(self, label="Open", pos=(230, 100), size=(110, 40)) self.openButton = wx.Button(self, label="Open", pos=(230, 100), size=(110, 40))
@ -741,7 +757,7 @@ For more information, please refer to <http://unlicense.org/>'''
info.SetName(TITLE) info.SetName(TITLE)
info.SetVersion(__version__) info.SetVersion(__version__)
info.SetDescription(description) 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.SetLicense(license)
info.AddDeveloper('Sotiris Papadopoulos') info.AddDeveloper('Sotiris Papadopoulos')
wx.AboutBox(info) wx.AboutBox(info)

20
youtube_dl_gui/YoutubeDLInterpreter.py

@ -73,19 +73,19 @@ class YoutubeDLInterpreter():
self.opts.append('--newline') self.opts.append('--newline')
def set_playlist_opts(self): def set_playlist_opts(self):
if (self.optionsList.startTrack != '1' and self.optionsList.startTrack != ''):
if self.optionsList.startTrack != 1:
self.opts.append('--playlist-start') self.opts.append('--playlist-start')
self.opts.append(self.optionsList.startTrack)
if (self.optionsList.endTrack != '0' and self.optionsList.endTrack != ''):
self.opts.append(str(self.optionsList.startTrack))
if self.optionsList.endTrack != 0:
self.opts.append('--playlist-end') self.opts.append('--playlist-end')
self.opts.append(self.optionsList.endTrack)
if (self.optionsList.maxDownloads != '0' and self.optionsList.maxDownloads != ''):
self.opts.append(str(self.optionsList.endTrack))
if self.optionsList.maxDownloads != 0:
self.opts.append('--max-downloads') self.opts.append('--max-downloads')
self.opts.append(self.optionsList.maxDownloads)
if (self.optionsList.minFileSize != '0' and self.optionsList.minFileSize != ''):
self.opts.append(str(self.optionsList.maxDownloads))
if self.optionsList.minFileSize != '0':
self.opts.append('--min-filesize') self.opts.append('--min-filesize')
self.opts.append(self.optionsList.minFileSize) 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('--max-filesize')
self.opts.append(self.optionsList.maxFileSize) self.opts.append(self.optionsList.maxFileSize)
@ -101,9 +101,9 @@ class YoutubeDLInterpreter():
self.opts.append(self.optionsList.videoPass) self.opts.append(self.optionsList.videoPass)
def set_connection_opts(self): def set_connection_opts(self):
if (self.optionsList.retries != '10' and self.optionsList.retries != ''):
if self.optionsList.retries != 10:
self.opts.append('-R') self.opts.append('-R')
self.opts.append(self.optionsList.retries)
self.opts.append(str(self.optionsList.retries))
if self.optionsList.proxy != '': if self.optionsList.proxy != '':
self.opts.append('--proxy') self.opts.append('--proxy')
self.opts.append(self.optionsList.proxy) self.opts.append(self.optionsList.proxy)

2
youtube_dl_gui/version.py

@ -1 +1 @@
__version__ = '0.3.2'
__version__ = '0.3.3'
Loading…
Cancel
Save