From 326cbf53a2cfa1c115523b9eb4837f629011a1f7 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Fri, 29 Jan 2021 01:10:56 -0700 Subject: pull code out into a python module --- vidslice/sources.py | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 vidslice/sources.py (limited to 'vidslice/sources.py') diff --git a/vidslice/sources.py b/vidslice/sources.py new file mode 100644 index 0000000..180e51c --- /dev/null +++ b/vidslice/sources.py @@ -0,0 +1,134 @@ +import glob +import json +import os +import subprocess +import tempfile +import threading +from tkinter import * +from tkinter import filedialog +from tkinter import messagebox +from tkinter import ttk + + +def has_ytdl(): + try: + subprocess.run(["youtube-dl", "--version"], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=subprocess.CREATE_NO_WINDOW) + return True + except FileNotFoundError: + return False + + +def update_ytdl(root): + try: + youtube_dl_found = subprocess.run(['where', 'youtube-dl'], stdout=subprocess.PIPE, text=True, + creationflags=subprocess.CREATE_NO_WINDOW) + except FileNotFoundError: + youtube_dl_found = subprocess.run(['which', 'youtube-dl'], stdout=subprocess.PIPE, text=True, + creationflags=subprocess.CREATE_NO_WINDOW) + if youtube_dl_found.returncode != 0: + answer = messagebox.askyesno(message="Could not find youtube-dl. Open vidslice README?", title="Error", + icon='error', parent=root) + if answer: + import webbrowser + webbrowser.open("https://github.com/boringcactus/vidslice/blob/master/README.md") + youtube_dl_path = youtube_dl_found.stdout.split("\n")[0] + old_mtime = os.stat(youtube_dl_path).st_mtime + proc = subprocess.run(["youtube-dl", "-U"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + creationflags=subprocess.CREATE_NO_WINDOW) + if not proc.stdout.startswith("youtube-dl is up-to-date") and not proc.stdout.startswith("ERROR"): + while os.stat(youtube_dl_path).st_mtime == old_mtime: + from time import sleep + sleep(0.25) + messagebox.showinfo(message="Updated youtube-dl successfully", title="Complete", parent=root) + + +class SourcesPanel(ttk.LabelFrame): + """ + A Panel representing source info + """ + + def __init__(self, *args, **kw): + super(SourcesPanel, self).__init__(*args, text='Sources', **kw) + self.update_listeners = [] + + if has_ytdl(): + ttk.Label(self, text="URL").grid(column=0, row=0, sticky=(E, W)) + self.url_text = StringVar(self) + ttk.Entry(self, textvariable=self.url_text).grid(column=1, row=0, sticky=(E, W)) + ttk.Button(self, text="Download", command=self.handle_url_download_pressed + ).grid(column=2, row=0, sticky=(E, W)) + else: + ttk.Label(self, text="Could not find youtube-dl, can't download videos automatically" + ).grid(column=0, row=0, columnspan=3, sticky=(E, W)) + self.url_text = None + + ttk.Label(self, text="File").grid(column=0, row=1, sticky=(E, W)) + self.file_text = StringVar(self) + self.file_text.trace_add("write", self.handle_file_changed) + ttk.Entry(self, textvariable=self.file_text).grid(column=1, row=1, sticky=(E, W)) + self.columnconfigure(1, weight=1) + ttk.Button(self, text="Browse", command=self.handle_file_browse_pressed).grid(column=2, row=1, sticky=(E, W)) + + self.status_label = StringVar(self, "Status: Select a file") + ttk.Label(self, textvariable=self.status_label).grid(column=0, row=2, columnspan=3, sticky=(E, W)) + + for child in self.winfo_children(): + child.grid_configure(padx=2, pady=2) + + def set_status(self, text): + self.status_label.set("Status: " + text) + + def handle_url_download_pressed(self, *args): + self.set_status("Downloading...") + + def download(): + file = tempfile.NamedTemporaryFile(delete=False) + # noinspection PyArgumentList + proc = subprocess.Popen([ + 'youtube-dl', '-o', file.name + '.%(ext)s', self.url_text.get() + ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, + creationflags=subprocess.CREATE_NO_WINDOW) + while proc.poll() is None: + out_data = proc.stdout.readline() + if out_data != '': + self.set_status("Downloading: " + out_data.strip()) + if proc.returncode == 0: + output_file = glob.glob(glob.escape(file.name) + '.*')[0] + self.set_status("Downloaded!") + self.file_text.set(output_file) + else: + error = ''.join(proc.stderr.readlines()).strip() + self.set_status("Couldn't download: " + error) + + threading.Thread(target=download).start() + + def handle_file_browse_pressed(self, *args): + filename = filedialog.askopenfilename(parent=self) + if filename != '': + self.file_text.set(filename) + + def handle_file_changed(self, *args): + def probe(): + result = subprocess.run([ + 'ffprobe', '-v', 'error', '-of', 'json', + '-show_entries', 'format=start_time,duration:stream=index,codec_type,avg_frame_rate,width,height', + self.file_text.get() + ], capture_output=True, text=True, creationflags=subprocess.CREATE_NO_WINDOW) + if result.returncode == 0: + ffprobe_data = json.loads(result.stdout) + self.set_status("Successfully loaded media info") + for listener in self.update_listeners: + listener(ffprobe_data) + else: + self.set_status("Failed to load media info: " + result.stderr) + for listener in self.update_listeners: + listener(None) + + threading.Thread(target=probe).start() + + def on_update(self, callback): + self.update_listeners.append(callback) + + def get_file(self): + return self.file_text.get() -- cgit v1.2.3