diff options
| author | Guillermo Ramos | 2021-04-13 18:17:14 +0200 | 
|---|---|---|
| committer | Guillermo Ramos | 2021-04-13 18:17:14 +0200 | 
| commit | 04446011f364909096cb49f2b66e4df96d713209 (patch) | |
| tree | 6c2bbb722d63487a766d91e5ecc9560f01442f44 /yt | |
| download | cli-04446011f364909096cb49f2b66e4df96d713209.tar.gz | |
Initial commit
Diffstat (limited to 'yt')
| -rwxr-xr-x | yt | 144 | 
1 files changed, 144 insertions, 0 deletions
@@ -0,0 +1,144 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use IPC::Open2 qw<open2>; +use JSON; + +my $ENDPOINT = "https://www.googleapis.com/youtube/v3"; +my $YT_API_KEY = $ENV{'YOUTUBE_API_KEY'} +    || die "Error: You need to define the env variable 'YOUTUBE_API_KEY'\n"; + +my $MAX_RESULTS = 30; + +# Currently unused +my $graphical; + +sub decode_json_utf8 { +    my $json = join "", @_; +    utf8::encode($json); +    return decode_json($json); +} + +sub api_search { +    my $query = shift; +    # TODO sanitize query properly... +    $query =~ s/'/'\\''/g; +    return (decode_json_utf8 `curl -s '$ENDPOINT/search?part=snippet&q=$query&maxResults=$MAX_RESULTS&key=$YT_API_KEY'`)->{items}; +} + +sub api_videos { +    my $ids = join(',', @_); +    return (decode_json_utf8 `curl -s '$ENDPOINT/videos?part=snippet,statistics,contentDetails&id=$ids&key=$YT_API_KEY'`)->{items}; +} + +sub api_channel_videos { +    use Data::Dumper; +    my $id = shift; +    my $uploads = (decode_json_utf8 `curl -s '$ENDPOINT/channels?part=contentDetails&id=$id&key=$YT_API_KEY'`)->{items}[0]{contentDetails}{relatedPlaylists}{uploads}; +    my $videos = (decode_json_utf8 `curl -s '$ENDPOINT/playlistItems?part=snippet&maxResults=$MAX_RESULTS&playlistId=$uploads&key=$YT_API_KEY'`)->{items}; +    return map { $_->{snippet}{resourceId}{videoId} } @$videos; +} + +sub play { +    my $url = shift; +    `mpv --really-quiet $url`; +} + +sub ask_query { +   my $query = `rofi -dmenu -no-fixed-num-lines -p "Youtube search" | sed 's: :+:g'`; +   $query =~ s/\s*$//g; +   $graphical = 1; +   return $query; +} + +sub sanitize_rofi { +    return shift =~ s/&/&/gr; +} + +sub select_entry { +    my ($channels, $video_ids) = @_; + +    my @entries; + +    open2(my $outh, my $inh, 'rofi -i -markup-rows -dmenu -format i -p "Select video"'); +    foreach my $chan (@$channels) { +        my $id = $chan->{channelId}; +        push @entries, $id; + +        print $inh sanitize_rofi("<b>[$chan->{title}] $chan->{description}</b>\n"); +    } + +    foreach my $video (@{api_videos(@$video_ids)}) { +        my $id = $video->{id}; +        push @entries, $id; + +        my $publishedAt = $video->{snippet}{publishedAt}; +        my ($y, $mo, $d, $h, $mi) = $publishedAt =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):\d{2}Z$/; +        my (undef, $cmi, $ch, $cd, $cmo, $cy) = gmtime(); +        $cy += 1900; +        $cmo++; +        my $published; +        if ($y < $cy-1) { +            $published = $cy - $y . " years ago"; +        } elsif ($mo != $cmo || $y == $cy-1) { +            $published = (12 * ($cy-$y) + $cmo) - $mo . " months ago"; +        } elsif ($cd != $d) { +            $published = $cd - $d . " days ago"; +        } elsif ($ch != $h) { +            $published = $ch - $h . " hours ago"; +        } elsif ($cmi != $mi) { +            $published = $cmi - $mi . " minutes ago"; +        } + +        my $stats = $video->{statistics}; +        my $likes = $stats->{likeCount}; +        my $dislikes = $stats->{dislikeCount}; +        my $rating = ""; +        if ($likes && $dislikes) { +            my $total = $likes + $dislikes; +            $rating = "*" x (6 * $likes / $total); +        } + +        print $inh sanitize_rofi("($published) [$video->{snippet}{channelTitle}] $video->{snippet}{title} $rating\n"); +    } +    close $inh; + +    my $idx = <$outh> || die "cancelled\n"; +    if ($idx < @$channels) { +        return ('channel', $entries[$idx]); +    } else { +        return ('video', $entries[$idx]); +    } +} + + +## MAIN + +my $query = join("+", @ARGV) || ask_query(); +die "empty query\n" unless $query; + +if ($query =~ /^http/) { +    play($query); +    exit 0; +} + +my @channels; +my @video_ids; +foreach my $entry (@{api_search($query)}) { +    if ($entry->{id}{kind} eq 'youtube#channel') { +        push @channels, $entry->{snippet}; +    } elsif ($entry->{id}{kind} eq 'youtube#video') { +        push @video_ids, $entry->{id}{videoId}; +    } +} + +my ($type, $entry) = select_entry(\@channels, \@video_ids); +if ($type eq 'channel') { +    my @videos = api_channel_videos($entry); +    my (undef, $entry) = select_entry([], \@videos); +    play("https://youtu.be/$entry"); +} elsif ($type eq 'video') { +    play("https://youtu.be/$entry"); +}  | 
