MrS0m30n3
10 years ago
commit
1ca60f921f
13 changed files with 1235 additions and 0 deletions
Split 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