aboutsummaryrefslogtreecommitdiff
path: root/sources.py
blob: 180e51c05120142d6d8dce7628e086336296b496 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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()