#!/usr/bin/env python3 # coding: utf-8 # vim:et:sta:sts=4:sw=4:ts=8:tw=79: import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk import shlex, subprocess import yt_dlp class wnd(Gtk.Window): def __init__(self): super().__init__( border_width = 20, default_height = 240, default_width = 480, icon_name = "youtube", resizable = False, title = "yt-dlp GUI", window_position = Gtk.WindowPosition.CENTER, ) self.controls = { 'url' : { 'e':Gtk.Entry(hexpand = True), 'l':Gtk.Label(label = "Video URL:", xalign = 0), 'w':2 }, 'title' : { 'e':Gtk.Entry(editable = False, hexpand = True), 'l':Gtk.Label(label = "Title:", xalign = 0), 'w':3 }, 'resolution' : { 'e':Gtk.ComboBoxText(), 'l':Gtk.Label(label = "Resolution:", xalign = 0), 'w':1 }, 'scale' : { 'e':Gtk.ComboBoxText(), 'l':Gtk.Label(label = "Scale:", xalign = 0), 'w':1 } } self.chk = [ { 'e':Gtk.CheckButton(label = "On-Top"), 'opt':' --ontop' }, { 'e':Gtk.CheckButton(label = "Without borders"), 'opt':' --no-border' }, { 'e':Gtk.CheckButton(label = "On all workspaces"), 'opt':' --on-all-workspaces' } ] self.url = "" vbox = Gtk.Box(orientation = Gtk.Orientation.VERTICAL, spacing = 8) self.add(vbox) g_main = Gtk.Grid(column_spacing = 8, row_spacing = 8, hexpand = True) vbox.pack_start(g_main, True, True, 0) g_buttons = Gtk.Grid(column_spacing = 8, row_spacing = 8, halign = Gtk.Align.END, valign = Gtk.Align.END) vbox.pack_start(g_buttons, True, True, 0) for k in self.controls.keys(): g_main.attach_next_to(self.controls[k]['l'], None, Gtk.PositionType.BOTTOM, 1, 1) g_main.attach_next_to(self.controls[k]['e'], self.controls[k]['l'], Gtk.PositionType.RIGHT, self.controls[k]['w'], 1) b_get_info = Gtk.Button(image = Gtk.Image.new_from_icon_name( "gtk-find", Gtk.IconSize.BUTTON)) g_main.attach_next_to(b_get_info, self.controls['url']['e'], Gtk.PositionType.RIGHT, 1, 1) g_checkboxes = Gtk.Grid(column_spacing = 8, row_spacing = 8, hexpand = True) g_main.attach(g_checkboxes, 2, 2, 1, 4) for c in self.chk: g_checkboxes.attach_next_to(c['e'], None, Gtk.PositionType.BOTTOM, 1, 1) b_ok = Gtk.Button(label = "Ok", image = Gtk.Image.new_from_icon_name( "gtk-ok", Gtk.IconSize.BUTTON)) b_cancel = Gtk.Button(label = "Cancel", image = Gtk.Image.new_from_icon_name( "gtk-cancel", Gtk.IconSize.BUTTON)) g_buttons.attach(b_ok, 0, 0, 1, 1) g_buttons.attach(b_cancel, 1, 0, 1, 1) for s in ["x0.5", "x1", "x2"]: self.controls['scale']['e'].append_text(s) self.controls['scale']['e'].set_active(1) b_cancel.connect("clicked", self.on_cancel_clicked) b_ok.connect("clicked", self.on_ok_clicked) b_get_info.connect("clicked", self.get_info) def on_cancel_clicked(self, button): self.destroy() def on_ok_clicked(self, button): cmd = "mpv --no-terminal" for c in self.chk: if c['e'].get_active(): cmd += c['opt'] cmd += " --window-scale=" cmd += self.controls['scale']['e'].get_active_text()[1:] cmd += " --ytdl-format=\"bv*[height<=" cmd += self.controls['resolution']['e'].get_active_text()[0:-1] cmd += "]+ba/b[height<=" cmd += self.controls['resolution']['e'].get_active_text()[0:-1] cmd += "] / wv*+ba/w\" " cmd += self.url args = shlex.split(cmd) p = subprocess.Popen(args, stdin = subprocess.DEVNULL, stdout = subprocess.DEVNULL, stderr = subprocess.DEVNULL) self.destroy() def get_info(self, button): self.url = self.controls['url']['e'].get_text() if self.url: ydl_opts = { 'quiet': True, 'no-warnings': True } with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(self.url, download = False) self.controls['title']['e'].set_text(info['title']) h = [] for f in info['formats']: if f['vcodec'] != "none" and f['height'] not in h: h.append(f['height']) self.controls['resolution']['e'].append_text( "{}p".format(f['height'])) self.controls['resolution']['e'].set_active(0) else: self.controls['title']['e'].set_text("Error: Empty url.") win = wnd() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()