From 04446011f364909096cb49f2b66e4df96d713209 Mon Sep 17 00:00:00 2001 From: Guillermo Ramos Date: Tue, 13 Apr 2021 18:17:14 +0200 Subject: Initial commit --- yt | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100755 yt (limited to 'yt') diff --git a/yt b/yt new file mode 100755 index 0000000..b246b07 --- /dev/null +++ b/yt @@ -0,0 +1,144 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use IPC::Open2 qw; +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("[$chan->{title}] $chan->{description}\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"); +} -- cgit v1.2.3