first commit

master
MrS0m30n3 2014-03-01 19:12:37 +02:00
commit 1ca60f921f
13 changed files with 1235 additions and 0 deletions

25
LICENSE Normal file
View File

@ -0,0 +1,25 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

44
README Normal file
View File

@ -0,0 +1,44 @@
NAME
====
youtube-dlG - GUI for youtube-dl
DESCRIPTION
===========
A cross platform front-end GUI of the popular youtube-dl
written in Python.
REQUIREMENTS
============
Python (version 2.7 or 3.3+)
http://www.python.org/
wxPython
http://wxpython.org
** Optional (In order to convert video files to audio-only files)
FFMPEG & FFPROBE
http://www.ffmpeg.org
PROJECT HOMEPAGE
================
Youtube-dlG: https://github.com/MrS0m30n3/youtube-dl-gui
Youtube-dl: http://rg3.github.io/youtube-dl/
AUTHOR
======
Sotiris Papadopoulos <ytubedlg@gmail.com>
THANKS
======
Thanks to youtube-dl authors for creating such and amazing tool.

40
SIGNALS.txt Normal file
View File

@ -0,0 +1,40 @@
Signals list, that DownloadManager thread sends (DownloadThread.py)
HANDLER
=======
YoutubeDLGUI.py
def download_handler(self, msg):
...
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
['[youtube]', ..., index]: Pre-Processing for url, index
['[download]', ..., index]: Downloading url, index
['[ffmpeg]', ..., index]: Post-Processing for url, 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]

BIN
icons/ytube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

11
setup.py Normal file
View File

@ -0,0 +1,11 @@
#! /usr/bin/env python
from distutils.core import setup
setup(name='Youtube-DLG',
version='0.2',
description='Youtube-dl GUI',
author='Sotiris Papadopoulos',
author_email='ytubedlg@gmail.com',
url='https://github.com/MrS0m30n3/youtube-dl-gui',
packages=['youtube_dl_gui'])

View File

@ -0,0 +1,148 @@
#! /usr/bin/env python
import subprocess
from os import name
from time import sleep
from wx import CallAfter
from threading import Thread
from wx.lib.pubsub import setuparg1
from wx.lib.pubsub import pub as Publisher
OS_TYPE = name
MAX_DOWNLOAD_THREADS = 3
PUBLISHER_TOPIC = 'download'
class DownloadManager(Thread):
def __init__(self, options, downloadlist):
super(DownloadManager, self).__init__()
self.downloadlist = downloadlist
self.options = options
self.running = True
self.procList = []
self.procNo = 0
self.start()
def run(self):
while self.running:
if self.downloadlist:
# Extract url, index from data
url, index = self.extract_data()
# Wait for your turn if there are not more positions in 'queue'
while self.procNo >= MAX_DOWNLOAD_THREADS:
proc = self.check_queue(0.5)
if proc != None:
self.procList.remove(proc)
self.procNo -= 1
# If we still running create new ProcessWrapper thread
if self.running:
self.procList.append(ProcessWrapper(self.options, url, index))
self.procNo += 1
else:
# Return True if at least one process is alive else return False
if not self.downloading():
self.running = False
else:
sleep(1)
# If we reach here close down all child threads
self.terminate_all()
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['finish', -1])
def add_download_item(self, downloadItem):
self.downloadlist.append(downloadItem)
def extract_data(self):
data = self.downloadlist.pop(0)
url = data['url']
index = data['index']
return url, index
def terminate_all(self):
for proc in self.procList:
if proc.isAlive():
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):
for proc in self.procList:
if not self.running:
break
if not proc.isAlive():
return proc
sleep(t)
return None
def close(self):
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['close', -1])
self.running = False
self.procNo = 0
class ProcessWrapper(Thread):
def __init__(self, options, url, index):
super(ProcessWrapper, self).__init__()
self.options = options
self.index = index
self.url = url
self.proc = None
self.info = None
self.err = False
self.stopped = False
self.set_process_info()
self.start()
def run(self):
self.proc = subprocess.Popen(self.options + [self.url], stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=self.info)
# while subprocess is alive and NOT the current thread
while self.proc_is_alive():
# read output
output = self.read()
if self.err:
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['error', self.index])
else:
if output != '':
data = self.proc_output(output)
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, data)
if not self.err and not self.stopped:
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['finish', self.index])
def close(self):
self.proc.kill()
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, ['close', self.index])
self.stopped = True
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):
data = self.remove_spaces(self.string_to_array(output))
data.append(self.index)
return data
def string_to_array(self, string):
return string.split(' ')
def remove_spaces(self, array):
return [x for x in array if x != '']
def set_process_info(self):
if OS_TYPE == 'nt':
self.info = subprocess.STARTUPINFO()
self.info.dwFlags |= subprocess.STARTF_USESHOWWINDOW

View File

@ -0,0 +1,129 @@
#! /usr/bin/env python
import os
OS_TYPE = os.name
SETTINGS_FILENAME = 'settings'
class OptionsHandler():
settings_abs_path = ''
def __init__(self):
self.load_default()
self.set_settings_path()
if self.settings_file_exist():
self.load_from_file()
def load_default(self):
self.ignoreErrors = True
self.idAsName = False
self.toAudio = False
self.audioFormat = "mp3"
self.keepVideo = False
self.audioQuality = 5
self.proxy = ""
self.savePath = ""
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.minFileSize = "0"
self.maxFileSize = "0"
self.writeSubs = False
self.writeAllSubs = False
self.subsLang = "English"
self.writeAutoSubs = False
self.cmdArgs = ""
def set_settings_path(self):
self.settings_abs_path = os.path.expanduser('~')
if OS_TYPE == 'nt':
self.settings_abs_path += '\\'
else:
self.settings_abs_path += '/.config/'
self.settings_abs_path += SETTINGS_FILENAME
def settings_file_exist(self):
return os.path.exists(self.settings_abs_path)
def read_from_file(self):
f = open(self.settings_abs_path, 'r')
options = f.readlines()
f.close()
return options
def load_from_file(self):
opts = []
for option in self.read_from_file():
opts.append(option.split('=')[1].rstrip('\n'))
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]
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]
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('ToAudio='+str(self.toAudio)+'\n')
f.write('AudioFormat='+str(self.audioFormat)+'\n')
f.write('KeepVideo='+str(self.keepVideo)+'\n')
f.write('AudioQuality='+str(self.audioQuality)+'\n')
f.write('Proxy='+str(self.proxy)+'\n')
f.write('SavePath='+str(self.savePath)+'\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('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('CmdArgs='+str(self.cmdArgs)+'\n')
f.close()

View File

@ -0,0 +1,32 @@
#! /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 urllib2 import urlopen, URLError, HTTPError
LATEST_YOUTUBE_DL = 'https://yt-dl.org/latest/'
PUBLISHER_TOPIC = 'update'
DOWNLOAD_TIMEOUT = 10
class UpdateThread(Thread):
def __init__(self, youtubeDLFile):
super(UpdateThread, self).__init__()
self.youtubeDLFile = youtubeDLFile
self.url = LATEST_YOUTUBE_DL + youtubeDLFile
self.start()
def run(self):
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, "Downloading latest youtube-dl...")
try:
f = urlopen(self.url, timeout=DOWNLOAD_TIMEOUT)
with open(self.youtubeDLFile, 'wb') as lf:
lf.write(f.read())
msg = 'Youtube-dl downloaded correctly'
except (HTTPError, URLError, IOError) as e:
msg = 'Youtube-dl download failed ' + str(e)
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, msg)
CallAfter(Publisher.sendMessage, PUBLISHER_TOPIC, 'finish')

View File

@ -0,0 +1,616 @@
#! /usr/bin/env python
'''
This file contains all gui classes
MainFrame
Custom wx.ListCtrl class
OptionsFrame
ConnectionPanel
AudioPanel
videoPanel
DownloadPanel
SubtitlesPanel
GeneralPanel
OtherPanel
'''
import os
import wx
from wx.lib.pubsub import setuparg1
from wx.lib.pubsub import pub as Publisher
from .version import __version__
from .UpdateThread import UpdateThread
from .DownloadThread import DownloadManager
from .OptionsHandler import OptionsHandler
from .YoutubeDLInterpreter import YoutubeDLInterpreter
if os.name == 'nt':
YOUTUBE_DL_FILENAME = 'youtube-dl.exe'
else:
YOUTUBE_DL_FILENAME = 'youtube-dl'
TITLE = 'Youtube-dlG'
AUDIOFORMATS = ["mp3", "wav", "aac", "m4a"]
VIDEOFORMATS = ["highest available",
"mp4 [1280x720]",
"mp4 [640x360]",
"webm [640x360]",
"flv [400x240]",
"3gp [320x240]",
"mp4 1080p(DASH)",
"mp4 720p(DASH)",
"mp4 480p(DASH)",
"mp4 360p(DASH)"]
LANGUAGES = ["English",
"Greek",
"Portuguese",
"French",
"Italian",
"Russian",
"Spanish",
"German"]
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)
# set sizers for status box (Windows & Linux)
if os.name == '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))
# bind events
self.Bind(wx.EVT_BUTTON, self.OnDownload, self.downloadButton)
self.Bind(wx.EVT_BUTTON, self.OnUpdate, self.updateButton)
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)
self.SetIcon(icon)
# set publisher for update thread
Publisher.subscribe(self.update_handler, "update")
# set publisher for download thread
Publisher.subscribe(self.download_handler, "download")
# init options object
self.optionsList = OptionsHandler()
# init some thread variables
self.downloadThread = None
self.updateThread = None
# init urlList for evt_text on self.trackList
self.urlList = []
# check & update libraries (youtube-dl)
self.check_if_youtube_dl_exist()
if (self.optionsList.autoUpdate):
self.status_bar_write("Auto update enable")
self.update_youtube_dl()
def check_if_youtube_dl_exist(self):
if not os.path.exists(YOUTUBE_DL_FILENAME):
self.status_bar_write("Youtube-dl is missing, trying to download it...")
self.update_youtube_dl()
def update_youtube_dl(self):
self.downloadButton.Disable()
self.updateThread = UpdateThread(YOUTUBE_DL_FILENAME)
def status_bar_write(self, msg):
self.statusBar.SetLabel(msg)
def download_handler(self, msg):
''' Handles msg base into signals see SIGNALS.txt for more info'''
if msg.data[0] == 'finish':
index = msg.data.pop()
if index == -1:
self.status_bar_write('Done')
self.downloadButton.SetLabel('Download')
self.updateButton.Enable()
self.downloadThread = None
self.urlList = []
else:
self.statusList._write_data(index, 4, '')
self.statusList._write_data(index, 5, 'Finished')
elif msg.data[0] == 'close':
index = msg.data.pop()
if index == -1:
self.status_bar_write('Stoping downloads')
else:
self.statusList._write_data(index, 3, '')
self.statusList._write_data(index, 4, '')
self.statusList._write_data(index, 5, 'Stopped')
elif msg.data[0] == 'error':
index = msg.data.pop()
self.statusList._write_data(index, 3, '')
self.statusList._write_data(index, 4, '')
self.statusList._write_data(index, 5, 'Error')
elif msg.data[0] == '[youtube]':
index = msg.data.pop()
self.statusList._write_data(index, 5, 'Pre-Processing')
elif msg.data[0] == '[download]':
index = msg.data.pop()
if (len(msg.data[1]) <= 6 and msg.data[1] != '100%'):
self.statusList._write_data(index, 1, msg.data[3])
self.statusList._write_data(index, 2, msg.data[1])
self.statusList._write_data(index, 3, msg.data[7])
self.statusList._write_data(index, 4, msg.data[5])
self.statusList._write_data(index, 5, 'Downloading')
elif msg.data[0] == '[ffmpeg]':
index = msg.data.pop()
self.statusList._write_data(index, 4, '')
self.statusList._write_data(index, 5, 'Converting to Audio')
def update_handler(self, msg):
if msg.data == 'finish':
self.downloadButton.Enable()
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()
for url in trackList:
if url != '':
self.urlList.append(url)
self.statusList._add_item(url)
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.downloadButton.SetLabel('Stop')
self.updateButton.Disable()
def save_options(self):
self.optionsList.save_to_file()
def OnTrackListChange(self, event):
if self.downloadThread != None:
''' Get current url list from trackList textCtrl '''
curList = self.trackList.GetValue().split('\n')
''' For each url in current url list '''
for url in curList:
''' 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 url into statusList '''
index = 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)
def OnDownload(self, event):
if self.downloadThread != None:
self.stop_download()
else:
self.start_download(self.trackList.GetValue().split('\n'))
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)
optionsFrame.Show()
def OnClose(self, event):
self.save_options()
self.Destroy()
class ListCtrl(wx.ListCtrl):
''' Custom ListCtrl class '''
def __init__(self, parent, id, pos, size, style):
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)
self.InsertColumn(4, 'Speed', width=90)
self.InsertColumn(5, 'Status', width=120)
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):
self.SetStringItem(index, column, data)
''' Clear list and set index to 0'''
def _clear_list(self):
self.DeleteAllItems()
self.ListIndex = 0
''' Return True if list is empty '''
def _is_empty(self):
if self.ListIndex == 0:
return True
else:
return False
''' Get last item inserted, Returns dictionary '''
def _get_last_item(self):
data = {}
last_item = self.GetItem(itemId=self.ListIndex-1, col=0)
data['url'] = last_item.GetText()
data['index'] = self.ListIndex-1
return data
''' Retrieve all items [start, self.ListIndex), Returns list '''
def _get_items(self, start=0):
items = []
for row in range(start, self.ListIndex):
item = self.GetItem(itemId=row, col=0)
data = {}
data['url'] = item.GetText()
data['index'] = row
items.append(data)
return items
class ConnectionPanel(wx.Panel):
def __init__(self, parent, optList):
self.optList = optList
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)
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()
class AudioPanel(wx.Panel):
def __init__(self, parent, optList):
self.optList = optList
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)
self.Bind(wx.EVT_CHECKBOX, self.OnAudioCheck, self.toAudioChk)
def OnAudioCheck(self, event):
if (self.toAudioChk.GetValue()):
self.keepVideoChk.Enable()
self.audioFormatCombo.Enable()
self.audioQualitySpnr.Enable()
else:
self.keepVideoChk.Disable()
self.audioFormatCombo.Disable()
self.audioQualitySpnr.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.keepVideoChk.Disable()
self.audioFormatCombo.Disable()
self.audioQualitySpnr.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()
class VideoPanel(wx.Panel):
def __init__(self, parent, optList):
self.optList = optList
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, 'Playlist Options', (300, 30))
wx.StaticText(self, -1, 'Start', (250, 60))
self.startBox = wx.TextCtrl(self, -1, pos=(320, 55), size=(50, -1))
wx.StaticText(self, -1, 'Stop', (250, 100))
self.stopBox = wx.TextCtrl(self, -1, pos=(320, 95), size=(50, -1))
wx.StaticText(self, -1, 'Max DLs', (250, 140))
self.maxBox = wx.TextCtrl(self, -1, pos=(320, 135), size=(50, -1))
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)
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()
class DownloadPanel(wx.Panel):
def __init__(self, parent, optList):
self.optList = 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))
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.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.minFileSize = self.minFilesizeBox.GetValue()
self.optList.maxFileSize = self.maxFilesizeBox.GetValue()
class SubtitlesPanel(wx.Panel):
def __init__(self, parent, optList):
self.optList = optList
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))
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 OnWriteAutoSubsChk(self, event):
if (self.writeAutoSubsChk.GetValue()):
self.writeAllSubsChk.Disable()
self.writeSubsChk.Disable()
self.subsLangCombo.Disable()
else:
self.writeAllSubsChk.Enable()
self.writeSubsChk.Enable()
self.subsLangCombo.Enable()
def OnWriteSubsChk(self, event):
if (self.writeSubsChk.GetValue()):
self.writeAllSubsChk.Disable()
self.writeAutoSubsChk.Disable()
else:
self.writeAllSubsChk.Enable()
self.writeAutoSubsChk.Enable()
def OnWriteAllSubsChk(self, event):
if (self.writeAllSubsChk.GetValue()):
self.writeSubsChk.Disable()
self.subsLangCombo.Disable()
self.writeAutoSubsChk.Disable()
else:
self.writeSubsChk.Enable()
self.subsLangCombo.Enable()
self.writeAutoSubsChk.Enable()
def load_options(self):
self.writeSubsChk.Enable()
self.subsLangCombo.Enable()
self.writeAllSubsChk.Enable()
self.writeAutoSubsChk.Enable()
self.writeSubsChk.SetValue(self.optList.writeSubs)
self.writeAllSubsChk.SetValue(self.optList.writeAllSubs)
self.subsLangCombo.SetValue(self.optList.subsLang)
self.writeAutoSubsChk.SetValue(self.optList.writeAutoSubs)
if (self.writeSubsChk.GetValue()):
self.writeAllSubsChk.Disable()
self.writeAllSubsChk.SetValue(False)
self.writeAutoSubsChk.Disable()
self.writeAutoSubsChk.SetValue(False)
if (self.writeAllSubsChk.GetValue()):
self.writeSubsChk.Disable()
self.writeSubsChk.SetValue(False)
self.subsLangCombo.Disable()
self.writeAutoSubsChk.Disable()
self.writeAutoSubsChk.SetValue(False)
if (self.writeAutoSubsChk.GetValue()):
self.writeAllSubsChk.Disable()
self.writeAllSubsChk.SetValue(False)
self.writeSubsChk.Disable()
self.writeSubsChk.SetValue(False)
self.subsLangCombo.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()
class GeneralPanel(wx.Panel):
def __init__(self, parent, optList, controlParent):
self.optList = optList
self.parent = controlParent
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.autoUpdateChk = wx.CheckBox(self, -1, 'Auto Update', (160, 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))
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()
def OnOpen(self, event):
dlg = wx.DirDialog(None, "Choose directory")
if dlg.ShowModal() == wx.ID_OK:
self.savePathBox.SetValue(dlg.GetPath())
dlg.Destroy()
def OnAbout(self, event):
msg = TITLE + ' (GUI)'+ '\nVersion: ' + __version__ + '\nCreated by: Sotiris Papadopoulos'
wx.MessageBox(msg, 'About', wx.OK | wx.ICON_INFORMATION)
def load_options(self):
self.savePathBox.SetValue(self.optList.savePath)
self.idAsNameChk.SetValue(self.optList.idAsName)
self.autoUpdateChk.SetValue(self.optList.autoUpdate)
def save_options(self):
self.optList.savePath = self.savePathBox.GetValue()
self.optList.idAsName = self.idAsNameChk.GetValue()
self.optList.autoUpdate = self.autoUpdateChk.GetValue()
class OtherPanel(wx.Panel):
def __init__(self, parent, optList):
self.optList = optList
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))
def load_options(self):
self.cmdArgsBox.SetValue(self.optList.cmdArgs)
def save_options(self):
self.optList.cmdArgs = self.cmdArgsBox.GetValue()
class OptionsFrame(wx.Frame):
def __init__(self, optionsList, parent=None, id=-1):
wx.Frame.__init__(self, parent, id, "Options", size=(540, 250))
self.optionsList = optionsList
panel = wx.Panel(self)
notebook = wx.Notebook(panel)
self.generalTab = GeneralPanel(notebook, self.optionsList, self)
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.subtitlesTab = SubtitlesPanel(notebook, self.optionsList)
self.otherTab = OtherPanel(notebook, self.optionsList)
notebook.AddPage(self.generalTab, "General")
notebook.AddPage(self.audioTab, "Audio")
notebook.AddPage(self.videoTab, "Video")
notebook.AddPage(self.subtitlesTab, "Subtitles")
notebook.AddPage(self.downloadTab, "Download")
notebook.AddPage(self.connectionTab, "Connection")
notebook.AddPage(self.otherTab, "Commands")
sizer = wx.BoxSizer()
sizer.Add(notebook, 1, wx.EXPAND)
panel.SetSizer(sizer)
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.load_all_options()
def OnClose(self, event):
self.save_all_options()
self.Destroy()
def reset(self):
self.optionsList.load_default()
self.load_all_options()
def load_all_options(self):
self.generalTab.load_options()
self.audioTab.load_options()
self.connectionTab.load_options()
self.videoTab.load_options()
self.downloadTab.load_options()
self.subtitlesTab.load_options()
self.otherTab.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.subtitlesTab.save_options()
self.otherTab.save_options()

View File

@ -0,0 +1,156 @@
#! /usr/bin/env python
'''
Parse OptionHandler object into list
and call youtube_dl.main(list) using
subprocess (we use this method to let
youtube_dl.main() handle all the hard
work)
'''
from os import name
OS_TYPE = name
LANGUAGES = {"English":"en",
"Greek":"gr",
"Portuguese":"pt",
"French":"fr",
"Italian":"it",
"Russian":"ru",
"Spanish":"es",
"German":"de"}
VIDEOFORMATS = {"highest available":"auto",
"mp4 [1280x720]":"22",
"mp4 [640x360]":"18",
"webm [640x360]":"43",
"flv [400x240]":"5",
"3gp [320x240]":"36",
"mp4 1080p(DASH)":"137",
"mp4 720p(DASH)":"136",
"mp4 480p(DASH)":"135",
"mp4 360p(DASH)":"134"}
class YoutubeDLInterpreter():
def __init__(self, optionsList, youtubeDLFile):
self.youtubeDLFile = youtubeDLFile
self.optionsList = optionsList
self.opts = []
self.set_os()
self.set_progress_opts()
self.set_download_opts()
self.set_connection_opts()
self.set_video_opts()
self.set_playlist_opts()
self.set_subtitles_opts()
self.set_output_opts()
self.set_audio_opts()
self.set_other_opts()
def get_options(self):
return self.opts
def set_os(self):
if OS_TYPE == 'nt':
self.opts = [self.youtubeDLFile]
else:
self.opts = ['python', 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 != ''):
self.opts.append('--min-filesize')
self.opts.append(self.optionsList.minFileSize)
if (self.optionsList.maxFileSize != '0' and self.optionsList.maxFileSize != ''):
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_connection_opts(self):
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)
if self.optionsList.referer != '':
self.opts.append('--referer')
self.opts.append(self.optionsList.referer)
def set_video_opts(self):
if self.optionsList.videoFormat != 'highest available':
self.opts.append('-f')
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_subtitles_opts(self):
if self.optionsList.writeAllSubs:
self.opts.append('--all-subs')
if (self.optionsList.writeAutoSubs):
self.opts.append('--write-auto-sub')
if self.optionsList.writeSubs:
self.opts.append('--write-subs')
if self.optionsList.subsLang != 'English':
self.opts.append('--sub-lang')
self.opts.append(LANGUAGES[self.optionsList.subsLang])
def set_output_opts(self):
fullP = ''
if self.optionsList.savePath != '':
if OS_TYPE == 'nt':
fullP = self.optionsList.savePath + '\\'
else:
fullP = self.optionsList.savePath + '/'
self.opts.append('-o')
if self.optionsList.idAsName:
self.opts.append(fullP + '%(id)s.%(ext)s')
else:
self.opts.append(fullP + '%(title)s.%(ext)s')
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:
self.opts.append('--audio-quality')
self.opts.append(str(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)

View File

@ -0,0 +1,19 @@
#! /usr/bin/env python
from sys import exit
try:
import wx
except ImportError, e:
print '[ERROR]', e
print 'Please install latest wx.Python'
exit(1)
from .YoutubeDLGUI import MainFrame
def main():
app = wx.App()
frame = MainFrame()
frame.Centre()
frame.Show()
app.MainLoop()

View File

@ -0,0 +1,14 @@
#! /usr/bin/env python
import sys
if __package__ is None and not hasattr(sys, "frozen"):
# direct call of __main__.py
import os.path
path = os.path.realpath(os.path.abspath(__file__))
sys.path.append(os.path.dirname(os.path.dirname(path)))
import youtube_dl_gui
if __name__ == '__main__':
youtube_dl_gui.main()

View File

@ -0,0 +1 @@
__version__ = '0.2'