#!/usr/local/bin/perl use strict; use warnings; use utf8; use open qw/:std :encoding(utf8)/; binmode(STDOUT, ":utf8"); use Gtk3 '-init'; use Glib 'TRUE', 'FALSE'; my %window_params = ( title => [ 'yt-dlp GUI' ], icon_name => [ 'youtube' ], position => [ 'center' ], border_width => [ 20 ], resizable => [ FALSE ], default_size => [ 480, 240 ], ); my @labels = ( { e => undef, label => 'Video URL:', control => 'url' }, { e => undef, label => 'Title:', control => 'title' }, { e => undef, label => 'Resolution:', control => 'resolution' }, { e => undef, label => 'Scale:', control => 'scale' }, ); my %grids = ( buttons => { e => undef, properties => { column_spacing => 8, row_spacing => 8, halign => 'end', valign => 'end' } }, checkboxes => { e => undef, properties => { column_spacing => 8, row_spacing => 8, hexpand => TRUE } }, main => { e => undef, properties => { column_spacing => 8, row_spacing => 8, hexpand => TRUE } } ); my %controls = ( url => { e => undef, type => 'Entry', width => 2, properties => { halign => 'fill', hexpand => TRUE } }, title => { e => undef, type => 'Entry', width => 3, properties => { halign => 'fill', hexpand => TRUE } }, resolution => { e => undef, type => 'ComboBoxText', width => 1, properties => { halign => 'start', hexpand => FALSE } }, scale => { e => undef, type => 'ComboBoxText', width => 1, properties => { halign => 'start', hexpand => FALSE } }, ); my %checkboxes = ( ontop => { e => undef, o => 1, label => 'On-Top', opt => ' --ontop' }, border => { e => undef, o => 2, label => 'Without borders', opt => ' --no-border' }, alldesk => { e => undef, o => 3, label => 'On all workspaces', opt => ' --on-all-workspaces' }, ); my $chk_rows = 3; my %buttons = ( get_info => Gtk3::Button->new_from_icon_name("gtk-find", 4), cancel => Gtk3::Button->new_from_stock("gtk-cancel"), ok => Gtk3::Button->new_from_stock("gtk-ok") ); my ($url, $title) = ('', ''); my $wnd = undef; _ui(); Gtk3::main(); sub _ui { $wnd = Gtk3::Window::new(); my $sr = undef; my $t = undef; my @x = []; my $x = undef; foreach my $k ( keys %window_params ) { $sr = 'set_' . $k; @x = map { $_ } @{$window_params{$k}}; $wnd->$sr(map { defined $_ ? $_ : () } $x[0], map { defined $_ ? $_ : () } $x[1] ); } my $vbox = Gtk3::Box->new('vertical', 8); $wnd->add($vbox); foreach my $k ( keys %grids ) { $grids{$k}{e} = Gtk3::Grid->new(); foreach my $p ( keys %{$grids{$k}{properties}} ) { $sr = 'set_' . $p; $x = $grids{$k}{properties}{$p}; $grids{$k}{e}->$sr($x); } } $vbox->pack_start($grids{main}{e}, TRUE, TRUE, 0); $vbox->pack_start($grids{buttons}{e}, TRUE, TRUE, 0); for (my $i = 0; $i <= $#labels; $i++) { $labels[$i]{e} = Gtk3::Label->new($labels[$i]{label}); $labels[$i]{e}->set_halign("start"); $grids{main}{e}->attach($labels[$i]{e}, 0, $i, 1, 1); $t = 'Gtk3::' . $controls{$labels[$i]{control}}{type}; $controls{$labels[$i]{control}}{e} = $t->new(); foreach my $k ( keys %{$controls{$labels[$i]{control}}{properties}} ) { $sr = 'set_' . $k; $x = $controls{$labels[$i]{control}}{properties}{$k}; $controls{$labels[$i]{control}}{e}->$sr($x); } $grids{main}{e}->attach_next_to($controls{$labels[$i]{control}}{e}, $labels[$i]{e}, 'right', $controls{$labels[$i]{control}}{width}, 1); } $grids{main}{e}->attach_next_to($buttons{get_info}, $controls{$labels[0]{control}}{e}, 'right', 1, 1); $grids{buttons}{e}->attach($buttons{ok}, 0, 0, 1, 1); $grids{buttons}{e}->attach($buttons{cancel}, 1, 0, 1, 1); $grids{main}{e}->attach($grids{checkboxes}{e}, 2, 2, 1, 4); my @k = ( sort { $checkboxes{$a}{o} <=> $checkboxes{$b}{o} } keys %checkboxes ); while ( my ($i, $k) = each @k ) { $checkboxes{$k}{e} = Gtk3::CheckButton->new_with_label( $checkboxes{$k}{label}); $grids{checkboxes}{e}->attach($checkboxes{$k}{e}, int($i / $chk_rows), $i % $chk_rows, 1, 1); } foreach ( "x0.5", "x1", "x2" ) { $controls{scale}{e}->append_text($_); } $controls{scale}{e}->set_active(1); $buttons{get_info}->signal_connect( clicked => \&_get_info ); $buttons{cancel}->signal_connect( clicked => sub { $wnd->destroy } ); $buttons{ok}->signal_connect( clicked => \&_play ); $wnd->signal_connect( destroy => sub { Gtk3->main_quit } ); $wnd->show_all; } sub _get_info { $url = $controls{url}{e}->get_text(); if ( $url ne '' ) { $title = qx{yt-dlp --no-warnings --quiet --get-title "$url"}; if ( $title ne '' ) { my @out = qx{yt-dlp --no-warnings --quiet --list-formats "$url" | grep -v "audio" | awk '\$2=="mp4" {print \$3}' | uniq | awk -F'x' '{print \$2"p"}'}; foreach ( @out ) { $_ =~ s/[\r\n]+$//; $controls{resolution}{e}->append_text( $_ ); } $controls{resolution}{e}->set_active(0); $controls{title}{e}->set_text($title); } else {$controls{title}{e}->set_text("Error: "); } } else { $controls{title}{e}->set_text("Error: Empty url."); } } sub _play { if ( $url ne '' ) { my $resolution = $controls{resolution}{e}->get_active_text(); $resolution =~ s/p$//; my $scale = $controls{scale}{e}->get_active_text(); $scale =~ s/^x//; my $options = '--no-terminal'; foreach my $k ( keys %checkboxes ) { if ( $checkboxes{$k}{e}->get_active ) { $options .= $checkboxes{$k}{opt}; } } $options .= ' --window-scale=' . $scale; $options .= " --ytdl-format=\"bv*[height<=$resolution]+ba/b[height<=$resolution] / wv*+ba/w\""; my $cmd = "mpv $options $url"; { exec ($cmd) }; print STDERR "Couldn't exec mpv: $!"; $wnd->destroy(); } }