Layout validation
This commit is contained in:
parent
bbe85e88eb
commit
ff123174c6
42
core.py
42
core.py
|
@ -1,42 +0,0 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
import youtube_dl
|
|
||||||
from feedgen.feed import FeedGenerator
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import __main__
|
|
||||||
import argparse
|
|
||||||
import ydl
|
|
||||||
import interface
|
|
||||||
import tkinter as tk
|
|
||||||
|
|
||||||
#TODO:
|
|
||||||
# offer download video option
|
|
||||||
# create good README.md
|
|
||||||
# .gitignore
|
|
||||||
# LICENSE
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-i","--interface", help="Launch GUI (beta)",action="store_true")
|
|
||||||
parser.add_argument("-s","--simulate", help="Do not download videos, just do as if",action="store_true")
|
|
||||||
parser.add_argument("-q","--quiet", help="Very quiet option for youtube-dl",action="store_true")
|
|
||||||
parser.add_argument("-f","--feed", help="Create Podcast feed",action="store_true")
|
|
||||||
parser.add_argument("-video", help="Download videos instead of creating audio",action="store_true")
|
|
||||||
parser.add_argument("-d","--dir", help="Define download directory for files, default value:'~/Vidéos'", default="~/Vidéos")
|
|
||||||
parser.add_argument("-url","--podcast_url", help="Define base url for podcasts, default value:'http://podcasts.lutix.org'", default="http://podcasts.lutix.org")
|
|
||||||
parser.add_argument("-yturl","--youtube_url", help="Define youtube url to fetch", default ="https://www.youtube.com/watch?v=xJO5GstqTSY&list=PLxzM9a5lhAumFRpcigmGY1QLDYxb4-P2B")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if not args.interface:
|
|
||||||
ydlo = ydl.ydl_object(args)
|
|
||||||
ydlo.print_infos()
|
|
||||||
ydlo.process_args()
|
|
||||||
else:
|
|
||||||
print('Waiting for interface to be loaded...')
|
|
||||||
root = tk.Tk() #create window
|
|
||||||
root.title("Ydl - Youtube downloader")
|
|
||||||
|
|
||||||
app = interface.Interface(master=root) #create all components inside the window
|
|
||||||
app.mainloop()
|
|
|
@ -2,6 +2,7 @@ import tkinter as tk
|
||||||
from tkinter import filedialog
|
from tkinter import filedialog
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
class Interface(tk.Frame):
|
class Interface(tk.Frame):
|
||||||
def __init__(self, master=None):
|
def __init__(self, master=None):
|
||||||
|
@ -10,10 +11,7 @@ class Interface(tk.Frame):
|
||||||
self.create_widgets()
|
self.create_widgets()
|
||||||
|
|
||||||
def create_command(self):
|
def create_command(self):
|
||||||
if os.path.split(sys.argv[0])[1] == 'core.py':
|
command = '{}/venv/bin/python {}'.format(Path(os.path.dirname(os.path.abspath(sys.argv[0]))).parent,os.path.abspath(sys.argv[0]))
|
||||||
command = 'source {}/venv/bin/activate & {}/venv/bin/python {}'.format(os.path.dirname(os.path.abspath(sys.argv[0])),os.path.dirname(os.path.abspath(sys.argv[0])),os.path.abspath(sys.argv[0]))
|
|
||||||
else:
|
|
||||||
command = '{}'.format(os.path.abspath(sys.argv[0]))
|
|
||||||
|
|
||||||
for key in self.args:
|
for key in self.args:
|
||||||
if bool(self.args[key].get()):
|
if bool(self.args[key].get()):
|
||||||
|
|
86
interface.py
86
interface.py
|
@ -1,86 +0,0 @@
|
||||||
import tkinter as tk
|
|
||||||
from tkinter import filedialog
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
class Interface(tk.Frame):
|
|
||||||
def __init__(self, master=None):
|
|
||||||
super().__init__(master)
|
|
||||||
self.master = master
|
|
||||||
self.create_widgets()
|
|
||||||
|
|
||||||
def create_command(self):
|
|
||||||
if os.path.split(sys.argv[0])[1] == 'core.py':
|
|
||||||
command = 'source {}/venv/bin/activate & {}/venv/bin/python {}'.format(os.path.dirname(os.path.abspath(sys.argv[0])),os.path.dirname(os.path.abspath(sys.argv[0])),os.path.abspath(sys.argv[0]))
|
|
||||||
else:
|
|
||||||
command = '{}'.format(os.path.abspath(sys.argv[0]))
|
|
||||||
|
|
||||||
for key in self.args:
|
|
||||||
if bool(self.args[key].get()):
|
|
||||||
print('{}: Option non vide'.format(key))
|
|
||||||
if self.args[key].get() in [0,1]:
|
|
||||||
value = ''
|
|
||||||
else:
|
|
||||||
value = '\'{}\''.format(self.args[key].get())
|
|
||||||
command = '{} --{} {}'.format(command,key,value)
|
|
||||||
else:
|
|
||||||
print('{}: Option vide'.format(key))
|
|
||||||
|
|
||||||
print(command)
|
|
||||||
return command
|
|
||||||
|
|
||||||
def process_window(self,command):
|
|
||||||
term_window = tk.Toplevel(self.master,height='600',width='600')
|
|
||||||
term_window.title("YDL: process in progress")
|
|
||||||
wid = term_window.winfo_id()
|
|
||||||
for key,value in self.args.items():
|
|
||||||
print('{} : {}'.format(key,value.get()))
|
|
||||||
system_command = 'xterm -into {} -geometry 400x200 -hold -sb -e \"{}\" &'.format(wid,command)
|
|
||||||
print(system_command)
|
|
||||||
os.system(system_command)
|
|
||||||
|
|
||||||
def select_directory(self):
|
|
||||||
folder_selected = filedialog.askdirectory()
|
|
||||||
self.dir.set(folder_selected)
|
|
||||||
|
|
||||||
def create_widgets(self):
|
|
||||||
|
|
||||||
tk.Label(self.master, padx=10,text="Youtube URL",bg="red").grid(row=0,columnspan='1')
|
|
||||||
tk.Label(self.master, padx=10,text="Folder").grid(row=1,columnspan='1')
|
|
||||||
tk.Label(self.master, padx=10,text="Podcast website root").grid(row=2,columnspan='1')
|
|
||||||
|
|
||||||
# Vars attached to args
|
|
||||||
self.youtube_url = tk.StringVar()
|
|
||||||
self.dir = tk.StringVar()
|
|
||||||
self.podcast_url = tk.StringVar()
|
|
||||||
self.simulate = tk.IntVar()
|
|
||||||
self.feed = tk.IntVar()
|
|
||||||
self.video = tk.IntVar()
|
|
||||||
|
|
||||||
# Default values
|
|
||||||
# TODO:
|
|
||||||
self.youtube_url.set('https://www.youtube.com/watch?v=xJO5GstqTSY&list=PLxzM9a5lhAumFRpcigmGY1QLDYxb4-P2B')
|
|
||||||
self.dir.set('~/Vidéos')
|
|
||||||
self.podcast_url.set('podcast.lutix.org')
|
|
||||||
|
|
||||||
tk.Entry(self.master,width=70,textvariable = self.youtube_url).grid(row=0, column=1,columnspan='8',sticky='W')
|
|
||||||
tk.Entry(self.master, textvariable = self.dir).grid(row=1, column=1,columnspan='6',sticky='WE')
|
|
||||||
tk.Entry(self.master,width=70, textvariable = self.podcast_url).grid(row=2, column=1,columnspan='8',sticky='W')
|
|
||||||
tk.Checkbutton(self.master, text="simulate", variable=self.simulate).grid(row=3,column=1,sticky='WE')
|
|
||||||
tk.Checkbutton(self.master, text="feed", variable=self.feed).grid(row=3,column=2,sticky='WE')
|
|
||||||
tk.Checkbutton(self.master, text="video only", variable=self.video).grid(row=3,column=3,sticky='WE')
|
|
||||||
|
|
||||||
self.args = {'youtube_url':self.youtube_url,'dir':self.dir,'podcast_url':self.podcast_url,'simulate':self.simulate,'feed':self.feed,'video':self.video}
|
|
||||||
|
|
||||||
|
|
||||||
tk.Button(self.master,text='Choose folder', command = self.select_directory).grid(row=1,column=8,sticky='WE')
|
|
||||||
tk.Button(self.master,text='Process', command = lambda: self.process_window(self.create_command())).grid(row=4,column=8,sticky='WE')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
root = tk.Tk() #create window
|
|
||||||
root.title("Ydl - Youtube downloader")
|
|
||||||
|
|
||||||
app = Interface(master=root) #create all components inside the window
|
|
||||||
app.mainloop()
|
|
||||||
|
|
118
ydl.py
118
ydl.py
|
@ -1,118 +0,0 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
import youtube_dl
|
|
||||||
from feedgen.feed import FeedGenerator
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
class ydl_object:
|
|
||||||
def __init__(self,args):
|
|
||||||
self.args = args
|
|
||||||
self.infos = self.fetch_info()
|
|
||||||
# print(self.args)
|
|
||||||
|
|
||||||
def fetch_info(self):
|
|
||||||
print('Fetching Youtube Link informations...')
|
|
||||||
return youtube_dl.YoutubeDL({'quiet':True,'ignoreerrors':True}).extract_info(self.args.youtube_url, False)
|
|
||||||
|
|
||||||
def create_feed(self):
|
|
||||||
"""
|
|
||||||
replace results by info
|
|
||||||
#results keys
|
|
||||||
_type entries id title uploader uploader_id uploader_url extractor webpage_url webpage_url_basename extractor_key
|
|
||||||
"""
|
|
||||||
fg = FeedGenerator()
|
|
||||||
fg.load_extension('podcast')
|
|
||||||
fg.podcast.itunes_category('Podcasting')
|
|
||||||
|
|
||||||
fg.title(self.infos['title'])
|
|
||||||
fg.description('none')
|
|
||||||
fg.link(href=self.args.podcast_url,rel='self')
|
|
||||||
|
|
||||||
for item in self.infos['entries']:
|
|
||||||
"""
|
|
||||||
#infos['entries'] keys
|
|
||||||
id uploader uploader_id uploader_url channel_id channel_url upload_date license creator title alt_title thumbnail description categories tags subtitles automatic_captions duration
|
|
||||||
"""
|
|
||||||
fe = fg.add_entry()
|
|
||||||
fe.id(item['id'])
|
|
||||||
fe.title(item['title'])
|
|
||||||
fe.description(item['description'])
|
|
||||||
item_full_path = self.args.podcast_url +'/'+self.infos['title']+'/'+item['title']+'.mp3'
|
|
||||||
fe.enclosure(item_full_path,str(item['duration']),'audio/mpeg')
|
|
||||||
|
|
||||||
fg.rss_str(pretty=True)
|
|
||||||
# create folder of feed if it doesn't exists
|
|
||||||
os.makedirs(self.args.dir+'/'+self.infos['title'], exist_ok=True)
|
|
||||||
fg.rss_file(self.args.dir+'/'+self.infos['title']+'/podcast.xml')
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def my_hook(self,d):
|
|
||||||
if d['status'] == 'finished':
|
|
||||||
print('Done downloading, now converting ...')
|
|
||||||
|
|
||||||
def yt_download(self,ydl_opts):
|
|
||||||
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
|
|
||||||
print('Downloading Videos...')
|
|
||||||
print('Depending on number of files, it can take a while...')
|
|
||||||
results = ydl.extract_info(self.args.youtube_url, not self.args.simulate)
|
|
||||||
print('Done')
|
|
||||||
|
|
||||||
def create_opts(self,type):
|
|
||||||
if type=='audio':
|
|
||||||
ydl_opts = {
|
|
||||||
'quiet':self.args.quiet,
|
|
||||||
'format': 'bestaudio/best',
|
|
||||||
'ignoreerrors': True,
|
|
||||||
'postprocessors': [{
|
|
||||||
'key': 'FFmpegExtractAudio',
|
|
||||||
'preferredcodec': 'mp3',
|
|
||||||
'preferredquality': '192',
|
|
||||||
}],
|
|
||||||
'progress_hooks': [self.my_hook],
|
|
||||||
}
|
|
||||||
elif type=='video':
|
|
||||||
ydl_opts= {}
|
|
||||||
|
|
||||||
|
|
||||||
# if youtube url is a list, then download archive
|
|
||||||
print('Fetching Url properties...')
|
|
||||||
if 'entries' in self.infos:
|
|
||||||
ydl_opts['download_archive']= self.args.dir+'/archive.txt'
|
|
||||||
ydl_opts['outtmpl']= self.args.dir+'/%(playlist_title)s/%(title)s.%(ext)s'
|
|
||||||
else:
|
|
||||||
ydl_opts['download_archive']= self.args.dir+'/archive.txt'
|
|
||||||
ydl_opts['outtmpl']= self.args.dir+'/%(title)s.%(ext)s'
|
|
||||||
print('Done')
|
|
||||||
|
|
||||||
return ydl_opts
|
|
||||||
|
|
||||||
def print_infos(self):
|
|
||||||
print('Downloads folder name: {}'.format(self.args.dir))
|
|
||||||
if 'entries' in self.infos:
|
|
||||||
print('')
|
|
||||||
print('List of episodes online:')
|
|
||||||
for k,i in enumerate(self.infos['entries']):
|
|
||||||
print('Episode {}: {}, Durée:{}'.format(k+1,i['title'],i['duration']))
|
|
||||||
|
|
||||||
def process_args(self):
|
|
||||||
if 'entries' in self.infos:
|
|
||||||
if self.args.feed:
|
|
||||||
print('')
|
|
||||||
print('Feed creation...{}'.format("Done" if self.create_feed() else "Error"))
|
|
||||||
else:
|
|
||||||
print('')
|
|
||||||
print('Warning: no podcast feed has been created, please add --feed argument.')
|
|
||||||
else:
|
|
||||||
if self.args.feed:
|
|
||||||
print('')
|
|
||||||
print('In spite of --feed argument, as you specified contradictory options, no feed can be created!')
|
|
||||||
|
|
||||||
if self.args.simulate:
|
|
||||||
print('Downloads...Not requested!')
|
|
||||||
else:
|
|
||||||
self.yt_download(self.create_opts(type='audio' if not self.args.video else 'video'))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue