first commit
commit
1ca60f921f
|
@ -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/>
|
||||
|
|
@ -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.
|
|
@ -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]
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -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'])
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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')
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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()
|
|
@ -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()
|
|
@ -0,0 +1 @@
|
|||
__version__ = '0.2'
|
Loading…
Reference in New Issue