Files
mbid-poller/poller.pl

161 lines
4.3 KiB
Perl

#!/usr/bin/env perl
# -*- Perl -*-
use strict;
use warnings;
use open qw(:std :encoding(UTF-8));
use Data::Dumper qw(Dumper);
use Getopt::Std;
use IO::Handle;
use JSON::Tiny qw(decode_json);
use LWP::UserAgent;
use Time::Duration;
use URI::Escape;
# --- Environment Variables ---
use constant LIDARR_BASE => $ENV{LIDARR_BASE} || 'http://localhost:8686';
getopts('bfhv');
my $fresh_result = defined $::opt_f ? $::opt_f : 0; # vs STALE
# Define the list of IDs to process
my @ids_to_process;
my $ua = new LWP::UserAgent; # Initialize LWP::UserAgent
my $json_content;
# --- Input Logic: Prioritized Checks ---
# 1. Check for API URL (Highest Priority)
if (my $api_url = $ENV{MBID_API_URL}) {
print STDERR "Fetching IDs from API URL: $api_url\n";
my $res = $ua->get($api_url);
unless ($res->is_success) {
die "FATAL: Failed to fetch data from API: " . $res->status_line . "\n";
}
$json_content = $res->content;
}
# 2. Check for JSON File (Second Priority)
elsif (my $json_file = $ENV{MBID_JSON_FILE}) {
print STDERR "Loading IDs from JSON file: $json_file\n";
open my $fh, '<:encoding(UTF-8)', $json_file or die "Could not open $json_file: $!";
$json_content = do { local $/; <$fh> };
close $fh;
}
# 3. Check for a single URL/ID (Lowest Priority)
elsif (my $single_url = $ENV{MBID_URL}) {
push @ids_to_process, $single_url;
}
else {
die "FATAL: Must set MBID_API_URL, MBID_JSON_FILE, OR MBID_URL.\n";
}
# --- JSON Parsing Logic (Applies to API URL and JSON File) ---
if ($json_content) {
my $data;
eval {
$data = decode_json($json_content);
};
if ($@) {
die "Error decoding JSON from source: $@\n";
}
# Extract the 'foreignId' from each object in the array
if (ref $data eq 'ARRAY') {
foreach my $item (@$data) {
if (ref $item eq 'HASH' && exists $item->{foreignId}) {
push @ids_to_process, $item->{foreignId};
} else {
warn "Skipping malformed item in input (missing 'foreignId').\n";
}
}
} else {
die "Input content was not a JSON array.\n";
}
}
# --- Main Processing Loop ---
foreach my $id (@ids_to_process) {
chomp $id;
print "\n--- Processing: $id ---\n";
my $type = 'artist'; # Assuming 'foreignId' provides an artist MBID
my $json = ping_api($type, $id);
print "- add-to-lidarr: " .
LIDARR_BASE . "/add/search?term=" . uri_escape("lidarr:$id") . "\n";
}
## subroutines
sub ping_api {
my ($type, $id) = @_;
my $ua = new LWP::UserAgent;
my $start = time();
my $loops = 0;
my $api = "https://api.lidarr.audio/api/v0.4/$type/$id";
my $json;
print STDERR "Pinging $api\n";
while (1) {
$loops++;
print STDERR "- attempt $loops...\n";
my $res = $ua->get($api);
my $status = $res->code;
if ($res->is_success) {
eval {
$json = decode_json($res->content);
};
if ($@) {
chomp $@;
warn "Retrying: JSON decode failed: $@\n";
} elsif ($fresh_result and $res->header('cf-cache-status') eq 'STALE') {
$status .= ' STALE';
warn "Retrying: Response is STALE ($status)\n";
} else {
# *** VALIDATION CHECK ***
my @validation_warnings = validate($type, $json);
if (@validation_warnings) {
warn "Retrying: Validation failed: " . join(', ', @validation_warnings) . "\n";
} else {
# Success: HTTP 2xx, JSON parsed, and content passed validation
last;
}
}
} else {
# Log failure if not success and retry
warn "Retrying: HTTP failed with status $status\n";
}
sleep 5;
}
my $elapsed = time() - $start;
print "- ready ($loops attempts, " . duration($elapsed) . ")\n"
if $loops > 1;
if ($type eq 'artist') {
print "- artist: " . $json->{artistname} . "\n";
} else {
print "- album: " . $json->{title} . "\n";
}
return $json;
}
sub validate {
my ($type, $json) = @_;
my @warnings;
if ($type eq 'artist') {
# make sure there are albums
unless (exists $json->{albums} and scalar @{$json->{albums}}) {
push(@warnings, 'no albums');
}
}
return @warnings;
}