aboutsummaryrefslogtreecommitdiff
path: root/yttitle.pl
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2024-12-26 17:08:34 -0500
committerB. Watson <urchlay@slackware.uk>2024-12-26 17:08:34 -0500
commit9eed830f296dab257759f5276d0963467007aa6b (patch)
tree83d6225d248ec8d4026b514d8b9b248e6c0fce32 /yttitle.pl
downloadstupid-irssi-tricks-9eed830f296dab257759f5276d0963467007aa6b.tar.gz
initial commit
Diffstat (limited to 'yttitle.pl')
-rw-r--r--yttitle.pl194
1 files changed, 194 insertions, 0 deletions
diff --git a/yttitle.pl b/yttitle.pl
new file mode 100644
index 0000000..a1aaf3a
--- /dev/null
+++ b/yttitle.pl
@@ -0,0 +1,194 @@
+#!/usr/bin/perl
+
+no strict;
+
+use POSIX ":sys_wait_h";
+
+use Irssi qw/
+ signal_add_first
+ signal_continue
+ signal_register
+ command_bind
+ timeout_add_once
+ get_irssi_dir
+/;
+
+our $VERSION = "0.1";
+our %IRSSI = (
+ authors => 'Urchlay',
+ contact => 'Urchlay on Libera',
+ name => 'yttitle',
+ description => 'get titles for youtube videos using yt-dlp',
+ license => 'WTFPL',
+ url => 'none',
+);
+
+# video ID => title
+our %cache;
+
+our $tmpdir = get_irssi_dir . "/yttitle.tmp";
+
+# pid => [server, target, video ID]
+our %jobs;
+
+# video ID keys (values meaningless; only care about key presence)
+our %job_videos;
+
+# attempts to spawn more jobs than this are just ignored.
+our $maxjobs = 10;
+
+# milliseconds: how often to check %jobs to see if any jobs are done.
+our $queue_time = 1000;
+
+# command to execute, 1st %s replaced with video ID, 2nd with tmp filename.
+# --socket-timeout arg is seconds, should be longer that $timeout.
+our $command_fmt =
+ "yt-dlp -q --print '%%(title)s' --socket-timeout 10 -- %s >%s 2>/dev/null";
+
+our $DEBUG = 1;
+
+sub debug {
+ Irssi::print(join "", @_) if $DEBUG;
+}
+
+sub debugf {
+ Irssi::print(sprintf(@_)) if $DEBUG;
+}
+
+sub start_timer {
+ timeout_add_once($queue_time, "check_jobs", undef);
+}
+
+sub get_tmp_filename {
+ my $id = shift;
+ return $tmpdir . "/" . $id;
+}
+
+sub read_tmp_file {
+ my $file = shift;
+
+ open my $fh, "<:encoding(UTF-8)", $file;
+
+ if(!$fh) {
+ debug("read_tmp_file() failed to open $file: $!");
+ return;
+ }
+
+ my $result = <$fh>;
+ close $fh;
+
+ chomp $result if defined $result;
+ return length($result) ? $result : undef;
+}
+
+sub spawn_job {
+ my ($server, $target, $video_id) = @_;
+ my $jobcount = keys %jobs;
+
+ debug("spawn_job() called, video_id $video_id");
+
+ if($jobcount > $maxjobs) {
+ debug("spawn_job(): jobcount $jobcount > maxjobs $maxjobs, ignoring request");
+ return;
+ }
+
+ if($job_videos{$video_id}) {
+ debug("spawn_job(): video_id $video_id already in queue");
+ return;
+ }
+
+ my $cmd = sprintf($command_fmt, $video_id, get_tmp_filename($video_id));
+ debug("spawn_job() command is: $cmd");
+
+ my $pid = fork();
+
+ if($pid) {
+ # parent
+ debug("spawn_job() forked, kid pid is $pid");
+ start_timer unless keys %jobs;
+ $jobs{$pid} = [ $server, $target, $video_id ];
+ $job_videos{$video_id} = 1;
+ debug("spawn_job(): job pids: " . join(",", keys %jobs));
+ } else {
+ # child, debug() and Irssi::print don't work, here.
+ exec $cmd;
+ }
+}
+
+sub job_done {
+ my $pid = shift;
+
+ my ($server, $target, $video_id) = @{$jobs{$pid}};
+
+ debug("job_done() pid $pid, target $target, video_id $video_id");
+
+ my $file = get_tmp_filename($video_id);
+ my $title = read_tmp_file($file);
+
+ unlink $file;
+
+ $cache{$video_id} = $title;
+ delete $job_videos{$video_id};
+
+ if(!defined($title)) {
+ debug("job_done(): video_id $video_id failed to get title");
+ return;
+ }
+
+ say_title($server, $target, $video_id, $title);
+}
+
+sub check_jobs {
+ my @k = keys %jobs;
+
+ debug("check_jobs(): " . @k . " jobs");
+ return unless @k;
+
+ for my $jobpid (@k) {
+ debug("check_jobs(): about to waitpid($jobpid, WNOHANG)");
+ $pid = waitpid($jobpid, WNOHANG);
+
+ debug("check_jobs(): jobpid $jobpid, pid $pid");
+
+ next if $pid == 0; # still running, let it run
+ job_done($jobpid) if $pid > 0; # -1 is "no such pid"
+ delete $jobs{$jobpid};
+ }
+
+ start_timer if keys %jobs;
+}
+
+sub say_title {
+ my ($server, $target, $video_id, $title) = @_;
+ my $tag = "YouTube" . ($DEBUG ? "($video_id)" : "");
+ $server->command("msg $target $tag: $title");
+}
+
+sub on_public_msg {
+ my ($server, $msg, $nick, $address, $target) = @_;
+ my $mynick = $server->{nick};
+
+ unless(length $target) {
+ $target = $nick;
+ $nick = $mynick;
+ }
+
+ if($target eq $mynick) {
+ # private message... send response to sender
+ $target = $nick;
+ }
+
+ for my $video_id ($msg =~ /(?:youtube\.com\S+(?:embed\/|v=)|youtu.be\/)([-0-9a-zA-Z_]{11})/g) {
+ if($cache{$video_id}) {
+ debug("video_id $video_id found in cache");
+ say_title($server, $target, $video_id, $cache{$video_id});
+ } else {
+ debug("video_id $video_id NOT found in cache, queuing job");
+ spawn_job($server, $target, $video_id);
+ }
+ }
+}
+
+### main()
+mkdir $tmpdir;
+signal_add_first("message public", "on_public_msg");