diff --git a/yt-dlp-gui b/yt-dlp-gui index 9cbe465..fbf48e4 100755 --- a/yt-dlp-gui +++ b/yt-dlp-gui @@ -8,156 +8,177 @@ use open qw/:std :encoding(utf8)/; binmode(STDOUT, ":utf8"); use Gtk3 '-init'; -use Glib "TRUE", "FALSE"; - -my $wnd = Gtk3::Window::new(); -my @labels = undef; -my %chks = ( - ontop => { o => 1, opt => ' --ontop', lbl => 'On-Top', chk => undef }, - border => { o => 2, opt => ' --no-border', lbl => 'Without borders', chk => undef }, - alldesk => { o => 3, opt => ' --on-all-workspaces', lbl => 'On all workspaces', chk => undef} +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 $txt_URL = Gtk3::Entry->new(); -my $txt_Title = Gtk3::Entry->new(); -my $cb_Resolution = Gtk3::ComboBoxText->new(); -my $cb_Scale = Gtk3::ComboBoxText->new(); -my $btn_Get = Gtk3::Button->new_from_icon_name("gtk-find", 4); +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 { - my $vbox = Gtk3::VBox::new(); - my $grid = Gtk3::Grid->new(); - my $grid_btn = Gtk3::Grid->new(); - my $grid_opt = Gtk3::Grid->new(); - my $grid_header = Gtk3::Grid->new(); - - my $btn_Cancel = Gtk3::Button->new_from_stock("gtk-cancel"); - my $btn_Ok = Gtk3::Button->new_from_stock("gtk-ok"); - - my $img = Gtk3::Image->new_from_icon_name("youtube", 6); - $img->set_halign("start"); - - $wnd->set_title("yt-dlp GUI"); - $wnd->set_default_size(480, 240); - $wnd->set_icon_name("youtube"); - $wnd->set_position("center"); - $wnd->set_border_width(20); - $wnd->set_resizable(FALSE); - $wnd->signal_connect( destroy => sub { Gtk3->main_quit } ); + $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] ); + } - $grid->set_column_spacing(10); - $grid->set_row_spacing(10); - $grid->set_hexpand(TRUE); + my $vbox = Gtk3::Box->new('vertical', 8); + $wnd->add($vbox); - $grid_btn->set_column_spacing(10); - $grid_btn->set_row_spacing(10); - $grid_btn->set_halign("end"); - $grid_btn->set_valign("end"); + foreach my $k ( keys %grids ) { + $grids{$k}{e} = Gtk3::Grid->new(); - $grid_opt->set_column_spacing(10); - $grid_opt->set_row_spacing(10); - $grid_opt->set_hexpand(TRUE); + foreach my $p ( keys %{$grids{$k}{properties}} ) { + $sr = 'set_' . $p; + $x = $grids{$k}{properties}{$p}; + $grids{$k}{e}->$sr($x); + } + } - $grid_header->set_column_spacing(10); - $grid_header->set_row_spacing(0); - $grid_header->set_hexpand(TRUE); + $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); + } - foreach ( "Video URL:", "Title:", "Resolution:", "Scale:" ) { - push(@labels, Gtk3::Label->new($_)); - $labels[$#labels]->set_halign("start"); - $grid->attach($labels[$#labels], 0, $#labels, 1, 1); + $grids{main}{e}->attach_next_to($controls{$labels[$i]{control}}{e}, + $labels[$i]{e}, 'right', + $controls{$labels[$i]{control}}{width}, 1); } - my @k = ( sort { $chks{$a}{o} <=> $chks{$b}{o} } keys %chks ); + $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 ) { - $chks{$k}{chk} = Gtk3::CheckButton->new_with_label($chks{$k}{lbl}); - $grid_opt->attach($chks{$k}{chk}, 0, $i, 1, 1); + $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" ) { - $cb_Scale->append_text( $_ ); - } - $cb_Scale->set_active(1); - - $txt_URL->set_halign("fill"); - $txt_URL->set_hexpand(TRUE); - $txt_Title->set_halign("fill"); - $txt_Title->set_hexpand(TRUE); - $cb_Resolution->set_halign("start"); - $cb_Resolution->set_hexpand(FALSE); - $cb_Scale->set_halign("start"); - $cb_Scale->set_hexpand(FALSE); - - $btn_Get->signal_connect( clicked => \&_get_info ); - $btn_Cancel->signal_connect( clicked => sub { $wnd->destroy } ); - $btn_Ok->signal_connect( clicked => \&_play ); - - $grid_header->attach($img, 0, 1, 1, 1); - $grid_header->attach(Gtk3::Label->new("Please enter video URL"), 1, 1, 1, 1); - - $grid->attach($txt_URL, 1, 1, 2, 1); - $grid->attach($btn_Get, 3, 1, 1, 1); - $grid->attach($txt_Title, 1, 2, 3, 1); - $grid->attach($cb_Resolution, 1, 3, 1, 1); - $grid->attach($cb_Scale, 1, 4, 1, 1); - $grid->attach($grid_opt, 2, 3, 1, 4); - - $grid_btn->attach($btn_Ok, 0, 1, 1, 1); - $grid_btn->attach($btn_Cancel, 1, 1, 1, 1); - - $vbox->add($grid_header); - $vbox->add($grid); - $vbox->add($grid_btn); + foreach ( "x0.5", "x1", "x2" ) { $controls{scale}{e}->append_text($_); } + $controls{scale}{e}->set_active(1); - $wnd->add($vbox); + $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 { - my $url = $txt_URL->get_text(); - 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"}'}; - my $title = qx{yt-dlp --no-warnings --quiet --get-title "$url"}; - - foreach ( @out ) { - $_ =~ s/[\r\n]+$//; - $cb_Resolution->append_text( $_ ); - } - - $cb_Resolution->set_active(0); - - $txt_Title->set_text($title); + $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 { - my $res = $cb_Resolution->get_active_text(); - $res =~ s/p$//; - my $scale = $cb_Scale->get_active_text(); - $scale =~ s/^x//; - - my $opts = "--no-terminal"; - - my @k = ( sort { $chks{$a}{o} <=> $chks{$b}{o} } keys %chks ); - while ( my ($i, $k) = each @k ) { - if ( $chks{$k}{chk}->get_active ) { - $opts .= $chks{$k}{opt}; + 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}; + } } - } - - $opts .= " --window-scale=" . $scale; - $opts .= " --ytdl-format=\"bv*[height<=$res]+ba/b[height<=$res] / wv*+ba/w\""; + $options .= ' --window-scale=' . $scale; + $options .= " --ytdl-format=\"bv*[height<=$resolution]+ba/b[height<=$resolution] / wv*+ba/w\""; - my $url = "\"" . $txt_URL->get_text() . "\""; + my $cmd = "mpv $options $url"; - my $cmd = "mpv $opts $url"; + { exec ($cmd) }; print STDERR "Couldn't exec mpv: $!"; - { exec ($cmd) }; print STDERR "Couldn't exec mpv: $!"; + $wnd->destroy(); - $wnd->destroy(); - - exit 0; + exit 0; + } } +