MrS0m30n3
10 years ago
commit
1ca60f921f
13 changed files with 1235 additions and 0 deletions
Unified View
Diff Options
-
25LICENSE
-
44README
-
40SIGNALS.txt
-
BINicons/ytube.png
-
11setup.py
-
148youtube_dl_gui/DownloadThread.py
-
129youtube_dl_gui/OptionsHandler.py
-
32youtube_dl_gui/UpdateThread.py
-
616youtube_dl_gui/YoutubeDLGUI.py
-
156youtube_dl_gui/YoutubeDLInterpreter.py
-
19youtube_dl_gui/__init__.py
-
14youtube_dl_gui/__main__.py
-
1youtube_dl_gui/version.py
@ -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] |
||||
|
|
@ -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' |
Write
Preview
Loading…
Cancel
Save