#!/usr/bin/env perl use strict; use warnings; use Sys::Hostname; use Scalar::Util qw(looks_like_number); use File::HomeDir qw(home); use File::Basename qw(basename); use Cwd qw(cwd); use Env qw(BROWSER TERMINAL); use Term::ReadKey; use Capture::Tiny qw(capture); use POSIX qw(setsid); use feature qw(switch); no warnings qw(experimental::smartmatch); use constant { TERMINAL_PATH => $TERMINAL, MPV_PATH => 'mpv', YTDL_PATH => 'yt-dlp', FETCH_PATH => 'fetch', CURL_PATH => 'curl', IMGVIEW_PATH => 'nsxiv', ZATHURA_PATH => 'zathura', W3M_PATH => 'w3m', BROWSER_PATH => $BROWSER, NOTIFYSEND_PATH => 'notify-send', COPYQ_PATH => 'copyq', HOSTNAME => (split /\./, hostname()) }; use constant PROG_LIST => "" . "mpv" . "\n" . "mpv-term" . "\n" . "ytdl" . "\n" . "ytdl thumbnail" . "\n" . "fetch" . "\n" . "nsxiv" . "\n" . "zathura" . "\n" . "w3m" . "\n" . "browser" . "\n" . "clip" . "\n"; use constant QUAL_LIST => "" . "1440" . "\n" . "1080" . "\n" . "720" . "\n" . "480" . "\n" . "360" . "\n" . "240" . "\n" . "144" . "\n" . "uncap" . "\n"; sub open_link { my ($a, $url) = @_; my $count; my $file_name; my $i; my $list; my $pid; my $pid2; my $pwd; my $quality; my $ret; my $tmp; my $tmpfile; my $val; my @wc; if ($a eq "mpv") { if (system(MPV_PATH, $url) != 0) { system( NOTIFYSEND_PATH, '-u', 'critical', '-t', '10000', 'playback failed', ' Failed to open ' . $url . '' ); } exit 0; } elsif ($a eq "mpv-term") { exec(TERMINAL_PATH, '-e', MPV_PATH, '--audio-channels=stereo', $url); } elsif ($a eq "fetch" || $a eq "ytdl") { $pid = fork(); if (not $pid) { setsid(); $ret = -1; if ($a eq "ytdl") { $list = QUAL_LIST; $quality = `printf "$list" | dmenu -i -l 8 -m 0`; if (not $quality) { exit 0; } chomp $quality; chdir home() . "/dl" or chdir home() . "/Downloads" or chdir home() or die $!; if (basename(cwd()) eq basename(home())) { $pwd = '~/'; } else { $pwd = '~/' . basename(cwd()); } $file_name = $url; $file_name =~ s/.+\///g; $file_name = `yt-dlp -e $url`; if (not $file_name) { $file_name = "YouTube video"; } chomp $file_name; @wc = split / /, $file_name; if (@wc > 8) { $file_name = ""; $i = 0; while ($i < 8) { $file_name .= "$wc[$i] "; $i++; } $file_name .= "[...]"; } $file_name =~ s/^[0-9]+/\[\.\.\.\]/; system( NOTIFYSEND_PATH, '-u', 'low', '-t', '2000', 'download started', ' ' . $file_name . ' started downloading' ); $tmpfile = `mktemp`; chomp $tmpfile; $pid2 = fork(); if (not $pid2) { (undef, undef, $ret) = capture { if ($quality eq 'uncap') { system(YTDL_PATH . ' --newline --add-metadata ' . $url . ' >' . $tmpfile); } elsif ($quality eq '1440') { system(YTDL_PATH . " -f '308+140' --newline --add-metadata " . $url . ' >' . $tmpfile); } elsif ($quality eq '1080') { system(YTDL_PATH . " -f '299+140' --newline --add-metadata " . $url . ' >' . $tmpfile); } elsif ($quality eq '720') { system(YTDL_PATH . " -f '298+140' --newline --add-metadata " . $url . ' >' . $tmpfile); } else { system(YTDL_PATH . " -f '[height<=" . $quality . "]' --newline --add-metadata " . $url . ' >' . $tmpfile); } }; if ($ret == 0) { system( NOTIFYSEND_PATH, '-u', 'normal', '-t', '10000', 'download complete', ' ' . $file_name . ' downloaded successfully to ' . '' . $pwd . '' ); } else { system( NOTIFYSEND_PATH, '-u', 'critical', '-t', '10000', 'download failed', ' failed to download ' . $file_name . '' ); } exit 0; } elsif ($pid2 < 0) { system( NOTIFYSEND_PATH, '-u', 'critical', '-t', '10000', 'download failed', ' failed to fork(2)' ); exit 0; } else { $count = 0; while (1) { $val = `tail -n 1 "$tmpfile" | awk '{print \$2}' | tr -d '%'`; if (looks_like_number($val) != 0) { system( NOTIFYSEND_PATH, '-h', 'int:value:' . $val, '-u', 'low', '-t', '5000', 'downloading', ' downloading ' . $file_name . '' ); if ($val == 100 || system('pgrep python >/dev/null 2>&1') != 0) { last; } } else { if ($count == 10) { last; } $count += 1; } sleep 2; } unlink $tmpfile; } exit 0; } else { chdir home() . "/dl" or chdir home() . "/Downloads" or chdir home() or die $!; if (basename(cwd()) eq basename(home())) { $pwd = '~/'; } else { $pwd = '~/' . basename(cwd()); } $file_name = $url; $file_name =~ s/.+\///g; @wc = split / /, $file_name; if (@wc > 8) { $file_name = ""; $i = 0; while ($i < 8) { $file_name .= "$wc[$i] "; $i++; } $file_name .= "[...]"; } $file_name =~ s/^[0-9]+/\[\.\.\.\]/; system( NOTIFYSEND_PATH, '-u', 'low', '-t', '10000', 'download started', ' ' . $file_name . ' started downloading' ); (undef, undef, $ret) = capture { if (HOSTNAME eq "mother" or "po-rbo") { system(CURL_PATH, "-fsSLO", $url); } elsif (HOSTNAME == "mars") { system(FETCH_PATH, $url); } }; if ($ret == 0) { system( NOTIFYSEND_PATH, '-u', 'normal', '-t', '10000', 'download complete', ' ' . $file_name . ' downloaded successfully to ' . '' . $pwd . '' ); } else { system( NOTIFYSEND_PATH, '-u', 'critical', '-t', '10000', 'download failed', ' failed to download ' . $file_name . '' ); } } exit 0; } elsif ($pid < 0) { system( NOTIFYSEND_PATH, '-u', 'critical', '-t', '10000', 'download failed', ' failed to fork(2)' ); exit 0; } else { exit 0; } } elsif ($a eq "ytdl thumbnail") { $pid = fork(); if (not $pid) { setsid(); chdir '/tmp' or die $!; $file_name = $url; $file_name =~ s/.+\///g; if ($file_name =~ m/^watch\?v=.+/) { $file_name = `yt-dlp -e $url`; if (not $file_name) { $file_name = "Youtube video"; } chomp $file_name; } system( NOTIFYSEND_PATH, '-u', 'low', '-t', '5000', 'download started', ' fetching thumbnail for '. $file_name . '' ); ($tmp, undef, $ret) = capture { system(YTDL_PATH, '--get-thumbnail', $url); }; chomp $tmp; if ($ret != 0) { system( NOTIFYSEND_PATH, '-u', 'critical', '-t', '10000', 'failed to get thumbnail', ' failed to get thumbail for '. $file_name . '' ); exit 0; } given (HOSTNAME) { when(["mother", "po-rbo"]) { system(CURL_PATH, "-fsSLO", $tmp); } when("mars") { system(FETCH_PATH, '-q', $tmp); } } $file_name = $tmp; $file_name =~ s/.+\///g; chomp $file_name; system(IMGVIEW_PATH, '-b', '-a', $file_name); unlink($file_name); exit 0; } } elsif ($a eq "nsxiv" || $a eq "zathura") { $pid = fork(); if (not $pid) { setsid(); chdir '/tmp' or die $!; $file_name = $url; $file_name =~ s/.+\///g; system( NOTIFYSEND_PATH, '-u', 'low', '-t', '5000', 'download started', ' fetching '. $file_name . ' to /tmp' ); given (HOSTNAME) { when(["mother", "po-rbo"]) { system(CURL_PATH, "-fsSLO", $url); } when("mars") { system(FETCH_PATH, '-q', $url); } } if ($a eq "nsxiv") { exec(IMGVIEW_PATH, '-b', '-a', $file_name); } else { exec(ZATHURA_PATH, $file_name); } } } elsif ($a eq "w3m") { exec(TERMINAL_PATH, '-e', W3M_PATH, $url); } elsif ($a eq "browser") { $pid = fork(); if (not $pid) { setsid(); capture { exec(BROWSER_PATH, $url); }; } } elsif ($a eq "clip") { $pid = fork(); if (not $pid) { setsid(); capture { exec(COPYQ_PATH, "copy", $url); }; } } return; } sub dmenu_prompt { my ($url) = @_; my $answer; my $list = PROG_LIST; $answer = `printf "$list\nURL: $url\n" | dmenu -i -l 12 -m 0`; chomp $answer; return $answer; } sub main { my $answer; if (@ARGV == 0) { print STDERR "linkview: no URL\n"; exit 1; } $answer = dmenu_prompt($ARGV[0]); open_link($answer, $ARGV[0]); return; } main(); __END__