|
|
|
#!/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 isinstance(f['height'], int) 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()
|
|
|
|
|