diff --git a/README.md b/README.md index 0ba79c1..c99002c 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,33 @@ __ytdl-gui__ ![](images/ytdl-gui.png) -GUI-обёртка над `www/yt-dlp` для воспроизведения видео с [RuTube](https://rutube.ru) и других видеохостингов с помощью [mpv](https://mpv.io/). +GUI-обёртка над `www/yt-dlp` для воспроизведения видео с +[RuTube](https://rutube.ru) и других видеохостингов с помощью +[mpv](https://mpv.io/). Для работы скрипта требуются следующие утилиты: - `yt-dlp` (`www/yt-dlp`); - `yad` (`x11/yad`); -- `mpv` (`multimedia/mpv`) +- `mpv` (`multimedia/mpv`). + +--- + +__yt-dlp-gui__ +![](images/yt-dlp-gui.png) + +GUI-обёртка над `www/yt-dlp` для воспроизведения видео с +[RuTube](https://rutube.ru) и других видеохостингов с помощью +[mpv](https://mpv.io/). + +В отличии от `ytdl-gui` написан на [Perl](https://www.perl.org/) и +[Gtk3](https://docs.gtk.org/gtk3/). + +Для работы скрипта требуются следующие компоненты: +- `perl5` (`lang/perl5.38`); +- `p5-Gtk3` (`x11-toolkits/p5-Gtk3`); +- `p5-Glib` (`devel/p5-Glib`); +- `yt-dlp` (`www/yt-dlp`); +- `mpv` (`multimedia/mpv`). --- diff --git a/images/yt-dlp-gui.png b/images/yt-dlp-gui.png new file mode 100644 index 0000000..ca939d9 Binary files /dev/null and b/images/yt-dlp-gui.png differ diff --git a/yt-dlp-gui b/yt-dlp-gui new file mode 100755 index 0000000..9cbe465 --- /dev/null +++ b/yt-dlp-gui @@ -0,0 +1,163 @@ +#!/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 $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} +); +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); + +_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 } ); + + $grid->set_column_spacing(10); + $grid->set_row_spacing(10); + $grid->set_hexpand(TRUE); + + $grid_btn->set_column_spacing(10); + $grid_btn->set_row_spacing(10); + $grid_btn->set_halign("end"); + $grid_btn->set_valign("end"); + + $grid_opt->set_column_spacing(10); + $grid_opt->set_row_spacing(10); + $grid_opt->set_hexpand(TRUE); + + $grid_header->set_column_spacing(10); + $grid_header->set_row_spacing(0); + $grid_header->set_hexpand(TRUE); + + foreach ( "Video URL:", "Title:", "Resolution:", "Scale:" ) { + push(@labels, Gtk3::Label->new($_)); + $labels[$#labels]->set_halign("start"); + $grid->attach($labels[$#labels], 0, $#labels, 1, 1); + } + + my @k = ( sort { $chks{$a}{o} <=> $chks{$b}{o} } keys %chks ); + 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); + } + + 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); + + $wnd->add($vbox); + + $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); +} + +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}; + } + } + + $opts .= " --window-scale=" . $scale; + + $opts .= " --ytdl-format=\"bv*[height<=$res]+ba/b[height<=$res] / wv*+ba/w\""; + + my $url = "\"" . $txt_URL->get_text() . "\""; + + my $cmd = "mpv $opts $url"; + + { exec ($cmd) }; print STDERR "Couldn't exec mpv: $!"; + + $wnd->destroy(); + + exit 0; +}