aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2019-02-21 23:08:17 -0700
committerMelody Horn <melody@boringcactus.com>2019-02-21 23:08:17 -0700
commit14723ea9bd40e4102dab99adecc060e69bbde018 (patch)
treec0084882723b384c443ce104bba633b727a2d706
downloadvidslice-14723ea9bd40e4102dab99adecc060e69bbde018.tar.gz
vidslice-14723ea9bd40e4102dab99adecc060e69bbde018.zip
download and ffprobe files
-rw-r--r--.gitignore2
-rw-r--r--requirements.txt1
-rw-r--r--vidslice.py209
3 files changed, 212 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f377c56
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+venv
+.idea
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..cffc1a2
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+wxPython==4.0.4
diff --git a/vidslice.py b/vidslice.py
new file mode 100644
index 0000000..08673cf
--- /dev/null
+++ b/vidslice.py
@@ -0,0 +1,209 @@
+# First things, first. Import the wxPython package.
+import glob
+import json
+import subprocess
+import tempfile
+import threading
+
+import wx
+
+
+def has_ytdl():
+ try:
+ subprocess.run(["youtube-dl", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ return True
+ except FileNotFoundError:
+ return False
+
+
+class SourcesPanel(wx.Panel):
+ """
+ A Panel representing source info
+ """
+
+ def __init__(self, *args, **kw):
+ super(SourcesPanel, self).__init__(*args, **kw)
+ self.update_listeners = []
+
+ root_sizer = wx.StaticBoxSizer(wx.VERTICAL, self, label="Sources")
+ self.SetSizer(root_sizer)
+ main = wx.Panel(self)
+ root_sizer.Add(main, proportion=1, flag=wx.EXPAND, border=3)
+ main_sizer = wx.GridBagSizer()
+ main.SetSizer(main_sizer)
+
+ url_label = wx.StaticText(main, label="URL")
+ main_sizer.Add(url_label, wx.GBPosition(0, 0), flag=wx.EXPAND)
+ self.url_text = wx.TextCtrl(main)
+ main_sizer.Add(self.url_text, wx.GBPosition(0, 1), flag=wx.EXPAND)
+ self.url_download_button = wx.Button(main, label="Download")
+ self.url_download_button.Bind(wx.EVT_BUTTON, self.handle_url_download_pressed)
+ main_sizer.Add(self.url_download_button, wx.GBPosition(0, 2), flag=wx.EXPAND)
+ if not has_ytdl():
+ tooltip = "Could not find youtube-dl"
+ url_label.Disable()
+ self.url_text.Disable()
+ self.url_text.SetToolTip(tooltip)
+ self.url_download_button.Disable()
+ self.url_download_button.SetToolTip(tooltip)
+
+ main_sizer.Add(wx.StaticText(main, label="File"), wx.GBPosition(1, 0), flag=wx.EXPAND)
+ self.file_text = wx.TextCtrl(main)
+ self.file_text.Bind(wx.EVT_TEXT, self.handle_file_changed)
+ main_sizer.Add(self.file_text, wx.GBPosition(1, 1), flag=wx.EXPAND)
+ self.file_browse_button = wx.Button(main, label="Browse")
+ self.file_browse_button.Bind(wx.EVT_BUTTON, self.handle_file_browse_pressed)
+ main_sizer.Add(self.file_browse_button, wx.GBPosition(1, 2), flag=wx.EXPAND)
+
+ self.status_label = wx.StaticText(main, label="Status: Select a file")
+ main_sizer.Add(self.status_label, wx.GBPosition(2, 0), wx.GBSpan(1, 3))
+
+ main_sizer.AddGrowableCol(1, proportion=1)
+
+ def set_status(self, text):
+ self.status_label.SetLabel("Status: " + text)
+
+ def handle_url_download_pressed(self, _):
+ 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.GetValue()
+ ], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ while proc.poll() is None:
+ out_data = proc.stdout.readline()
+ if out_data != '':
+ wx.CallAfter(lambda: self.set_status("Downloading: " + out_data.strip()))
+ output_file = glob.glob(glob.escape(file.name) + '.*')[0]
+ wx.CallAfter(lambda: self.set_status("Downloaded!"))
+ wx.CallAfter(lambda: self.file_text.SetValue(output_file))
+
+ threading.Thread(target=download).start()
+
+ def handle_file_browse_pressed(self, _):
+ dialog = wx.FileDialog(self, message="message")
+ if dialog.ShowModal() == wx.ID_OK:
+ self.file_text.SetValue(dialog.GetPath())
+
+ def handle_file_changed(self, event):
+ 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.GetValue()
+ ], capture_output=True, text=True)
+ if result.stderr != "":
+ print(result.stderr)
+ ffprobe_data = json.loads(result.stdout)
+ del ffprobe_data['programs']
+ print(ffprobe_data)
+ if result.returncode == 0:
+ 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)
+
+ def on_update(self, callback):
+ self.update_listeners.append(callback)
+
+
+class HelloFrame(wx.Frame):
+ """
+ A Frame that says Hello World
+ """
+
+ def __init__(self, *args, **kw):
+ # ensure the parent's __init__ is called
+ super(HelloFrame, self).__init__(*args, **kw)
+
+ main_sizer = wx.BoxSizer(wx.VERTICAL)
+
+ # set up sources panel
+ sources_panel = SourcesPanel(self)
+ main_sizer.Add(sources_panel, proportion=0, flag=wx.EXPAND)
+
+ # create a panel in the frame
+ options_panel = wx.Panel(self)
+ main_sizer.Add(options_panel, proportion=1, flag=wx.EXPAND)
+
+ # and put some text with a larger bold font on it
+ st = wx.StaticText(options_panel, label="Hello World!")
+ font = st.GetFont()
+ font.PointSize += 10
+ font = font.Bold()
+ st.SetFont(font)
+
+ # create a menu bar
+ self.make_menu_bar()
+
+ # and a status bar
+ self.CreateStatusBar()
+ self.SetStatusText("Welcome to wxPython!")
+
+ # main_sizer.SetSizeHints(self)
+ self.SetSizer(main_sizer)
+
+ def make_menu_bar(self):
+ """
+ A menu bar is composed of menus, which are composed of menu items.
+ This method builds a set of menus and binds handlers to be called
+ when the menu item is selected.
+ """
+
+ # Make a file menu with Hello and Exit items
+ file_menu = wx.Menu()
+ # The "\t..." syntax defines an accelerator key that also triggers
+ # the same event
+ hello_item = file_menu.Append(-1, "&Hello...\tCtrl-H",
+ "Help string shown in status bar for this menu item")
+ file_menu.AppendSeparator()
+ # When using a stock ID we don't need to specify the menu item's
+ # label
+ exit_item = file_menu.Append(wx.ID_EXIT)
+
+ # Now a help menu for the about item
+ help_menu = wx.Menu()
+ about_item = help_menu.Append(wx.ID_ABOUT)
+
+ # Make the menu bar and add the two menus to it. The '&' defines
+ # that the next letter is the "mnemonic" for the menu item. On the
+ # platforms that support it those letters are underlined and can be
+ # triggered from the keyboard.
+ menu_bar = wx.MenuBar()
+ menu_bar.Append(file_menu, "&File")
+ menu_bar.Append(help_menu, "&Help")
+
+ # Give the menu bar to the frame
+ self.SetMenuBar(menu_bar)
+
+ # Finally, associate a handler function with the EVT_MENU event for
+ # each of the menu items. That means that when that menu item is
+ # activated then the associated handler function will be called.
+ self.Bind(wx.EVT_MENU, self.on_hello, hello_item)
+ self.Bind(wx.EVT_MENU, self.on_exit, exit_item)
+ self.Bind(wx.EVT_MENU, self.on_about, about_item)
+
+ def on_exit(self, event):
+ """Close the frame, terminating the application."""
+ self.Close(True)
+
+ def on_hello(self, event):
+ """Say hello to the user."""
+ wx.MessageBox("Hello again from wxPython")
+
+ def on_about(self, event):
+ """Display an About Dialog"""
+ wx.MessageBox("This is a wxPython Hello World sample",
+ "About Hello World 2",
+ wx.OK | wx.ICON_INFORMATION)
+
+
+if __name__ == '__main__':
+ # When this module is run (not imported) then create the app, the
+ # frame, show it, and start the event loop.
+ app = wx.App()
+ frm = HelloFrame(None, title='Hello World 2')
+ frm.Show()
+ app.MainLoop()