Added better debugging. Corrected Lidarr import. Updated README.md
This commit is contained in:
660
poller.pl
660
poller.pl
@@ -1,255 +1,407 @@
|
||||
#!/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 HTTP::Request;
|
||||
use IO::Handle;
|
||||
use JSON::Tiny qw(decode_json encode_json);
|
||||
use LWP::UserAgent;
|
||||
use Time::Duration;
|
||||
use URI::Escape;
|
||||
|
||||
# --- Environment Variables ---
|
||||
use constant LIDARR_BASE => $ENV{LIDARR_BASE} || 'http://localhost:8686';
|
||||
use constant LIDARR_API_KEY => $ENV{LIDARR_API_KEY} || die "FATAL: LIDARR_API_KEY environment variable is required\n";
|
||||
use constant LIDARR_ROOT_FOLDER => $ENV{LIDARR_ROOT_FOLDER} || '/music';
|
||||
use constant LIDARR_QUALITY_PROFILE_ID => $ENV{LIDARR_QUALITY_PROFILE_ID} || 1;
|
||||
use constant LIDARR_METADATA_PROFILE_ID => $ENV{LIDARR_METADATA_PROFILE_ID} || 1;
|
||||
|
||||
# ANSI escape sequences
|
||||
sub ERASE_EOL { return "\033[K"; }
|
||||
sub CURSOR_BACK { return "\033[${_[0]}D"; }
|
||||
sub STATUS { return $_[0] . ERASE_EOL . CURSOR_BACK(length($_[0])) }
|
||||
|
||||
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 ---
|
||||
print STDERR "=== MBID Poller Starting ===\n";
|
||||
print STDERR "Environment variables:\n";
|
||||
print STDERR " LIDARR_BASE: " . (LIDARR_BASE) . "\n";
|
||||
print STDERR " LIDARR_API_KEY: " . (LIDARR_API_KEY ? "***SET***" : "NOT SET") . "\n";
|
||||
print STDERR " LIDARR_ROOT_FOLDER: " . (LIDARR_ROOT_FOLDER) . "\n";
|
||||
print STDERR " LIDARR_QUALITY_PROFILE_ID: " . (LIDARR_QUALITY_PROFILE_ID) . "\n";
|
||||
print STDERR " LIDARR_METADATA_PROFILE_ID: " . (LIDARR_METADATA_PROFILE_ID) . "\n";
|
||||
print STDERR " MBID_API_URL: " . ($ENV{MBID_API_URL} || 'NOT SET') . "\n";
|
||||
print STDERR " MBID_JSON_FILE: " . ($ENV{MBID_JSON_FILE} || 'NOT SET') . "\n";
|
||||
print STDERR " MBID_URL: " . ($ENV{MBID_URL} || 'NOT SET') . "\n";
|
||||
print STDERR "\n";
|
||||
|
||||
# 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;
|
||||
print STDERR "Successfully fetched " . length($json_content) . " bytes from API\n";
|
||||
}
|
||||
# 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";
|
||||
if (-f $json_file) {
|
||||
open my $fh, '<:encoding(UTF-8)', $json_file or die "Could not open $json_file: $!";
|
||||
$json_content = do { local $/; <$fh> };
|
||||
close $fh;
|
||||
print STDERR "Successfully loaded " . length($json_content) . " bytes from file\n";
|
||||
} else {
|
||||
die "FATAL: JSON file $json_file does not exist\n";
|
||||
}
|
||||
}
|
||||
# 3. Check for a single URL/ID (Lowest Priority)
|
||||
elsif (my $single_url = $ENV{MBID_URL}) {
|
||||
print STDERR "Using single URL/ID: $single_url\n";
|
||||
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) {
|
||||
print STDERR "Parsing JSON content...\n";
|
||||
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') {
|
||||
print STDERR "Found JSON array with " . scalar(@$data) . " items\n";
|
||||
foreach my $item (@$data) {
|
||||
if (ref $item eq 'HASH' && exists $item->{foreignId}) {
|
||||
push @ids_to_process, $item->{foreignId};
|
||||
print STDERR " - Added ID: $item->{foreignId}\n";
|
||||
} else {
|
||||
warn "Skipping malformed item in input (missing 'foreignId').\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
die "Input content was not a JSON array.\n";
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR "\nTotal IDs to process: " . scalar(@ids_to_process) . "\n";
|
||||
if (scalar(@ids_to_process) == 0) {
|
||||
print STDERR "WARNING: No IDs found to process!\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# --- Main Processing Loop (CORRECTED TO CALL ADD FUNCTION) ---
|
||||
print STDERR "\n=== Starting Main Processing ===\n";
|
||||
foreach my $id (@ids_to_process) {
|
||||
chomp $id;
|
||||
print "\n--- Processing: $id ---\n";
|
||||
print STDERR "Processing ID: $id\n";
|
||||
|
||||
my $type = 'artist'; # Assuming 'foreignId' provides an artist MBID
|
||||
|
||||
# 1. Ping API to get artist data
|
||||
my $artist_data = ping_api($type, $id);
|
||||
|
||||
# 2. Validate data (e.g., check for albums)
|
||||
my @warnings = validate($type, $artist_data);
|
||||
if (@warnings) {
|
||||
warn "Skipping artist $id (" . ($artist_data->{artistname} || 'Unknown Artist') . ") due to warnings: " . join(', ', @warnings) . "\n";
|
||||
next; # Skip to the next ID if validation fails
|
||||
}
|
||||
|
||||
# 3. Add artist to Lidarr (This is the critical step you wanted)
|
||||
add_artist_to_lidarr($artist_data);
|
||||
|
||||
# Print the link for reference
|
||||
print "- add-to-lidarr: " .
|
||||
LIDARR_BASE . "/add/search?term=" . uri_escape("lidarr:$id") . "\n";
|
||||
}
|
||||
# --- End of Main Processing Loop ---
|
||||
|
||||
print STDERR "\n=== Processing Complete ===\n";
|
||||
|
||||
# --- Subroutines ---
|
||||
|
||||
sub ping_api {
|
||||
my ($type, $id) = @_;
|
||||
my $ua = new LWP::UserAgent;
|
||||
$ua->timeout(30); # Set reasonable timeout per request
|
||||
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 STATUS("- attempt $loops");
|
||||
my $res = $ua->get($api);
|
||||
my $status = $res->code;
|
||||
|
||||
if ($res->is_success) {
|
||||
eval {
|
||||
$json = decode_json($res->content);
|
||||
};
|
||||
if ($@) {
|
||||
chomp $@;
|
||||
warn "Retrying: $@\n";
|
||||
} elsif ($fresh_result and defined $res->header('cf-cache-status') and $res->header('cf-cache-status') eq 'STALE') {
|
||||
$status .= ' STALE';
|
||||
print STDERR STATUS("- attempt $loops: $status (cache warming...)");
|
||||
} else {
|
||||
print STDERR ERASE_EOL;
|
||||
last; # Got fresh data, exit retry loop
|
||||
}
|
||||
} else {
|
||||
print STDERR STATUS("- attempt $loops: $status - " . $res->status_line);
|
||||
}
|
||||
|
||||
sleep 5;
|
||||
}
|
||||
|
||||
my $elapsed = time() - $start;
|
||||
print "- ready ($loops attempts, " . duration($elapsed) . ")\n"
|
||||
if $loops > 1;
|
||||
if ($type eq 'artist') {
|
||||
print "- artist: " . ($json->{artistname} || 'UNKNOWN') . "\n";
|
||||
} else {
|
||||
print "- album: " . ($json->{title} || 'UNKNOWN') . "\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;
|
||||
}
|
||||
|
||||
sub add_artist_to_lidarr {
|
||||
my ($artist_data) = @_;
|
||||
|
||||
print STDERR "Adding artist to Lidarr: " . $artist_data->{artistname} . "\n";
|
||||
|
||||
my $ua = new LWP::UserAgent;
|
||||
$ua->timeout(30);
|
||||
|
||||
# Prepare the payload for Lidarr API
|
||||
my $payload = {
|
||||
'foreignArtistId' => $artist_data->{foreignArtistId},
|
||||
'artistName' => $artist_data->{artistname},
|
||||
'monitored' => JSON::Tiny::true,
|
||||
'monitorNewItems' => 'all',
|
||||
'qualityProfileId' => LIDARR_QUALITY_PROFILE_ID + 0, # Ensure it's a number
|
||||
'metadataProfileId' => LIDARR_METADATA_PROFILE_ID + 0, # Ensure it's a number
|
||||
'path' => LIDARR_ROOT_FOLDER . '/' . $artist_data->{artistname},
|
||||
'rootFolderPath' => LIDARR_ROOT_FOLDER,
|
||||
'addOptions' => {
|
||||
'monitor' => 'all',
|
||||
'searchForMissingAlbums' => JSON::Tiny::false
|
||||
}
|
||||
};
|
||||
|
||||
# Convert to JSON
|
||||
my $json_payload = JSON::Tiny::encode_json($payload);
|
||||
|
||||
# Make the API request
|
||||
my $lidarr_api_url = LIDARR_BASE . '/api/v1/artist';
|
||||
my $req = HTTP::Request->new(POST => $lidarr_api_url);
|
||||
$req->header('Content-Type' => 'application/json');
|
||||
$req->header('X-Api-Key' => LIDARR_API_KEY);
|
||||
$req->content($json_payload);
|
||||
|
||||
my $res = $ua->request($req);
|
||||
|
||||
if ($res->is_success) {
|
||||
print "- added to Lidarr successfully\n";
|
||||
} elsif ($res->code == 400) {
|
||||
# Artist might already exist
|
||||
print "- artist already exists in Lidarr or validation failed\n";
|
||||
} else {
|
||||
die "Lidarr API request failed: " . $res->status_line . " - " . $res->content . "\n";
|
||||
}
|
||||
#!/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 HTTP::Request;
|
||||
use IO::Handle;
|
||||
use JSON::Tiny qw(decode_json encode_json);
|
||||
use LWP::UserAgent;
|
||||
use Time::Duration;
|
||||
use URI::Escape;
|
||||
|
||||
# --- Environment Variables ---
|
||||
use constant LIDARR_BASE => $ENV{LIDARR_BASE} || 'http://localhost:8686';
|
||||
use constant LIDARR_API_KEY => $ENV{LIDARR_API_KEY} || die "FATAL: LIDARR_API_KEY environment variable is required\n";
|
||||
use constant LIDARR_ROOT_FOLDER => $ENV{LIDARR_ROOT_FOLDER} || '/music';
|
||||
use constant LIDARR_QUALITY_PROFILE_ID => $ENV{LIDARR_QUALITY_PROFILE_ID} || 1;
|
||||
use constant LIDARR_METADATA_PROFILE_ID => $ENV{LIDARR_METADATA_PROFILE_ID} || 1;
|
||||
use constant LOG_LEVEL => lc($ENV{LOG_LEVEL} || 'info'); # info, debug
|
||||
|
||||
# ANSI escape sequences
|
||||
sub ERASE_EOL { return "\033[K"; }
|
||||
sub CURSOR_BACK { return "\033[${_[0]}D"; }
|
||||
sub STATUS { return $_[0] . ERASE_EOL . CURSOR_BACK(length($_[0])) }
|
||||
|
||||
# Logging functions
|
||||
sub debug_log {
|
||||
my ($message) = @_;
|
||||
if (LOG_LEVEL eq 'debug') {
|
||||
print STDERR "[DEBUG] $message\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub info_log {
|
||||
my ($message) = @_;
|
||||
print STDERR "[INFO] $message\n";
|
||||
}
|
||||
|
||||
sub error_log {
|
||||
my ($message) = @_;
|
||||
print STDERR "[ERROR] $message\n";
|
||||
}
|
||||
|
||||
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 ---
|
||||
print STDERR "=== MBID Poller Starting ===\n";
|
||||
print STDERR "Environment variables:\n";
|
||||
print STDERR " LIDARR_BASE: " . (LIDARR_BASE) . "\n";
|
||||
print STDERR " LIDARR_API_KEY: " . (LIDARR_API_KEY ? "***SET***" : "NOT SET") . "\n";
|
||||
print STDERR " LIDARR_ROOT_FOLDER: " . (LIDARR_ROOT_FOLDER) . "\n";
|
||||
print STDERR " LIDARR_QUALITY_PROFILE_ID: " . (LIDARR_QUALITY_PROFILE_ID) . "\n";
|
||||
print STDERR " LIDARR_METADATA_PROFILE_ID: " . (LIDARR_METADATA_PROFILE_ID) . "\n";
|
||||
print STDERR " LOG_LEVEL: " . (LOG_LEVEL) . "\n";
|
||||
print STDERR " MBID_API_URL: " . ($ENV{MBID_API_URL} || 'NOT SET') . "\n";
|
||||
print STDERR " MBID_JSON_FILE: " . ($ENV{MBID_JSON_FILE} || 'NOT SET') . "\n";
|
||||
print STDERR " MBID_URL: " . ($ENV{MBID_URL} || 'NOT SET') . "\n";
|
||||
print STDERR "\n";
|
||||
|
||||
# 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;
|
||||
print STDERR "Successfully fetched " . length($json_content) . " bytes from API\n";
|
||||
}
|
||||
# 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";
|
||||
if (-f $json_file) {
|
||||
open my $fh, '<:encoding(UTF-8)', $json_file or die "Could not open $json_file: $!";
|
||||
$json_content = do { local $/; <$fh> };
|
||||
close $fh;
|
||||
print STDERR "Successfully loaded " . length($json_content) . " bytes from file\n";
|
||||
} else {
|
||||
die "FATAL: JSON file $json_file does not exist\n";
|
||||
}
|
||||
}
|
||||
# 3. Check for a single URL/ID (Lowest Priority)
|
||||
elsif (my $single_url = $ENV{MBID_URL}) {
|
||||
print STDERR "Using single URL/ID: $single_url\n";
|
||||
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) {
|
||||
print STDERR "Parsing JSON content...\n";
|
||||
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') {
|
||||
print STDERR "Found JSON array with " . scalar(@$data) . " items\n";
|
||||
foreach my $item (@$data) {
|
||||
if (ref $item eq 'HASH' && exists $item->{foreignId}) {
|
||||
push @ids_to_process, $item->{foreignId};
|
||||
print STDERR " - Added ID: $item->{foreignId}\n";
|
||||
} else {
|
||||
warn "Skipping malformed item in input (missing 'foreignId').\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
die "Input content was not a JSON array.\n";
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR "\nTotal IDs to process: " . scalar(@ids_to_process) . "\n";
|
||||
if (scalar(@ids_to_process) == 0) {
|
||||
print STDERR "WARNING: No IDs found to process!\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Counters for summary
|
||||
my $processed_count = 0;
|
||||
my $success_count = 0;
|
||||
my $error_count = 0;
|
||||
my $skip_count = 0;
|
||||
|
||||
# --- Main Processing Loop (ENHANCED WITH BETTER ERROR HANDLING) ---
|
||||
print STDERR "\n=== Starting Main Processing ===\n";
|
||||
foreach my $id (@ids_to_process) {
|
||||
chomp $id;
|
||||
$processed_count++;
|
||||
print "\n--- Processing ($processed_count/" . scalar(@ids_to_process) . "): $id ---\n";
|
||||
print STDERR "Processing ID: $id\n";
|
||||
|
||||
my $type = 'artist'; # Assuming 'foreignId' provides an artist MBID
|
||||
|
||||
# 1. Ping API to get artist data
|
||||
my $artist_data = ping_api($type, $id);
|
||||
|
||||
# Check if we actually got valid data
|
||||
if (!$artist_data) {
|
||||
error_log("Failed to get artist data for $id - skipping");
|
||||
$error_count++;
|
||||
next;
|
||||
}
|
||||
|
||||
# 2. Validate data (e.g., check for albums)
|
||||
my @warnings = validate($type, $artist_data);
|
||||
if (@warnings) {
|
||||
warn "Skipping artist $id (" . ($artist_data->{artistname} || 'Unknown Artist') . ") due to warnings: " . join(', ', @warnings) . "\n";
|
||||
$skip_count++;
|
||||
next; # Skip to the next ID if validation fails
|
||||
}
|
||||
|
||||
# 3. Add artist to Lidarr (This is the critical step you wanted)
|
||||
my $add_result = add_artist_to_lidarr($artist_data);
|
||||
if ($add_result) {
|
||||
$success_count++;
|
||||
} else {
|
||||
$error_count++;
|
||||
}
|
||||
|
||||
# Print the link for reference
|
||||
print "- add-to-lidarr: " .
|
||||
LIDARR_BASE . "/add/search?term=" . uri_escape("lidarr:$id") . "\n";
|
||||
}
|
||||
# --- End of Main Processing Loop ---
|
||||
|
||||
print STDERR "\n=== Processing Complete ===\n";
|
||||
print STDERR "Summary:\n";
|
||||
print STDERR " Total processed: $processed_count\n";
|
||||
print STDERR " Successfully added: $success_count\n";
|
||||
print STDERR " Errors: $error_count\n";
|
||||
print STDERR " Skipped (validation failed): $skip_count\n";
|
||||
|
||||
# --- Subroutines ---
|
||||
|
||||
sub ping_api {
|
||||
my ($type, $id) = @_;
|
||||
my $ua = new LWP::UserAgent;
|
||||
$ua->timeout(30); # Set reasonable timeout per request
|
||||
my $start = time();
|
||||
my $loops = 0;
|
||||
my $max_attempts = 15; # Limit attempts to prevent infinite loops
|
||||
my $api = "https://api.lidarr.audio/api/v0.4/$type/$id";
|
||||
my $json;
|
||||
|
||||
print STDERR "Pinging $api\n";
|
||||
|
||||
while (1) {
|
||||
$loops++;
|
||||
if (LOG_LEVEL eq 'debug') {
|
||||
debug_log("attempt $loops: making request...");
|
||||
} else {
|
||||
print STDERR STATUS("- attempt $loops");
|
||||
}
|
||||
my $res = $ua->get($api);
|
||||
debug_log("attempt $loops: got response");
|
||||
my $status = $res->code;
|
||||
|
||||
if ($res->is_success) {
|
||||
debug_log("attempt $loops: success, parsing JSON...");
|
||||
eval {
|
||||
$json = decode_json($res->content);
|
||||
};
|
||||
if ($@) {
|
||||
chomp $@;
|
||||
if (LOG_LEVEL eq 'debug') {
|
||||
debug_log("attempt $loops: JSON parse error: $@");
|
||||
} else {
|
||||
warn "Retrying: $@\n";
|
||||
}
|
||||
} elsif ($fresh_result and defined $res->header('cf-cache-status') and $res->header('cf-cache-status') eq 'STALE') {
|
||||
$status .= ' STALE';
|
||||
if (LOG_LEVEL eq 'debug') {
|
||||
debug_log("attempt $loops: $status (cache warming...)");
|
||||
} else {
|
||||
print STDERR STATUS("- attempt $loops: $status (cache warming...)");
|
||||
}
|
||||
} else {
|
||||
debug_log("attempt $loops: got valid data!");
|
||||
if (LOG_LEVEL ne 'debug') {
|
||||
print STDERR ERASE_EOL;
|
||||
}
|
||||
last; # Got fresh data, exit retry loop
|
||||
}
|
||||
} else {
|
||||
if (LOG_LEVEL eq 'debug') {
|
||||
debug_log("attempt $loops: $status - " . $res->status_line);
|
||||
} else {
|
||||
print STDERR STATUS("- attempt $loops: $status - " . $res->status_line);
|
||||
}
|
||||
}
|
||||
|
||||
# Break if we've exceeded max attempts
|
||||
if ($loops >= $max_attempts) {
|
||||
error_log("Failed after $max_attempts attempts");
|
||||
if (LOG_LEVEL ne 'debug') {
|
||||
print STDERR ERASE_EOL;
|
||||
}
|
||||
last;
|
||||
}
|
||||
|
||||
debug_log("sleeping 5 seconds before retry...");
|
||||
sleep 5;
|
||||
}
|
||||
|
||||
# Check if we exhausted all attempts without success
|
||||
if ($loops >= $max_attempts && !$json) {
|
||||
error_log("Failed to get valid response after $max_attempts attempts");
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $elapsed = time() - $start;
|
||||
print "- ready ($loops attempts, " . duration($elapsed) . ")\n"
|
||||
if $loops > 1;
|
||||
if ($type eq 'artist') {
|
||||
print "- artist: " . ($json->{artistname} || 'UNKNOWN') . "\n";
|
||||
debug_log("Artist data keys: " . join(", ", keys %$json));
|
||||
debug_log("foreignArtistId: " . ($json->{foreignArtistId} || 'NOT FOUND'));
|
||||
debug_log("artistId: " . ($json->{artistId} || 'NOT FOUND'));
|
||||
debug_log("id: " . ($json->{id} || 'NOT FOUND'));
|
||||
} else {
|
||||
print "- album: " . ($json->{title} || 'UNKNOWN') . "\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;
|
||||
}
|
||||
|
||||
sub add_artist_to_lidarr {
|
||||
my ($artist_data) = @_;
|
||||
|
||||
info_log("Adding artist to Lidarr: " . ($artist_data->{artistname} || 'UNKNOWN'));
|
||||
|
||||
# Debug: show what fields we have
|
||||
debug_log("Available fields in artist data: " . join(", ", keys %$artist_data));
|
||||
|
||||
# The foreignArtistId might be in a different field - let's check common possibilities
|
||||
my $foreign_artist_id = $artist_data->{foreignArtistId} || $artist_data->{artistId} || $artist_data->{id};
|
||||
|
||||
# Enhanced validation of required fields
|
||||
if (!$foreign_artist_id) {
|
||||
error_log("Missing foreignArtistId/artistId/id in artist data");
|
||||
debug_log("Artist data dump: " . Dumper($artist_data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$artist_data->{artistname}) {
|
||||
error_log("Missing artistname in artist data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
my $ua = new LWP::UserAgent;
|
||||
$ua->timeout(30);
|
||||
|
||||
# Clean artist name for path (remove invalid characters)
|
||||
my $clean_artist_name = $artist_data->{artistname};
|
||||
$clean_artist_name =~ s/[<>:"|?*\\\/]/_/g; # Replace invalid path characters
|
||||
|
||||
# Prepare the payload for Lidarr API
|
||||
my $payload = {
|
||||
'foreignArtistId' => $foreign_artist_id, # Use the found ID
|
||||
'artistName' => $artist_data->{artistname},
|
||||
'monitored' => JSON::Tiny::true,
|
||||
'monitorNewItems' => 'all',
|
||||
'qualityProfileId' => LIDARR_QUALITY_PROFILE_ID + 0, # Ensure it's a number
|
||||
'metadataProfileId' => LIDARR_METADATA_PROFILE_ID + 0, # Ensure it's a number
|
||||
'path' => LIDARR_ROOT_FOLDER . '/' . $clean_artist_name,
|
||||
'rootFolderPath' => LIDARR_ROOT_FOLDER,
|
||||
'addOptions' => {
|
||||
'monitor' => 'all',
|
||||
'searchForMissingAlbums' => JSON::Tiny::false
|
||||
}
|
||||
};
|
||||
|
||||
# Convert to JSON
|
||||
my $json_payload;
|
||||
eval {
|
||||
$json_payload = JSON::Tiny::encode_json($payload);
|
||||
};
|
||||
if ($@) {
|
||||
error_log("Failed to encode JSON payload: $@");
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug_log("Lidarr API payload: " . substr($json_payload, 0, 200) . "...");
|
||||
|
||||
# Make the API request
|
||||
my $lidarr_api_url = LIDARR_BASE . '/api/v1/artist';
|
||||
debug_log("Making request to: $lidarr_api_url");
|
||||
|
||||
my $req = HTTP::Request->new(POST => $lidarr_api_url);
|
||||
$req->header('Content-Type' => 'application/json');
|
||||
$req->header('X-Api-Key' => LIDARR_API_KEY);
|
||||
$req->content($json_payload);
|
||||
|
||||
my $res = $ua->request($req);
|
||||
|
||||
debug_log("Lidarr API Response Code: " . $res->code);
|
||||
debug_log("Lidarr API Response Message: " . $res->message);
|
||||
|
||||
if ($res->is_success) {
|
||||
print "- added to Lidarr successfully\n";
|
||||
info_log("SUCCESS: Artist added to Lidarr");
|
||||
|
||||
# Try to parse response to get artist ID
|
||||
if (my $response_data = eval { decode_json($res->content) }) {
|
||||
debug_log("Added artist with Lidarr ID: " . ($response_data->{id} || 'unknown'));
|
||||
}
|
||||
return 1;
|
||||
} elsif ($res->code == 400) {
|
||||
# Artist might already exist or validation failed
|
||||
print "- artist already exists in Lidarr or validation failed\n";
|
||||
info_log("WARNING: " . $res->content);
|
||||
|
||||
# Try to parse error response
|
||||
if (my $error_data = eval { decode_json($res->content) }) {
|
||||
if (ref $error_data eq 'ARRAY' && @$error_data) {
|
||||
info_log("Validation errors:");
|
||||
foreach my $error (@$error_data) {
|
||||
if (ref $error eq 'HASH') {
|
||||
info_log(" - " . ($error->{errorMessage} || $error->{message} || "Unknown error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} elsif ($res->code == 401) {
|
||||
error_log("Authentication failed - check your API key");
|
||||
return 0;
|
||||
} elsif ($res->code == 404) {
|
||||
error_log("Lidarr API endpoint not found - check your Lidarr URL and version");
|
||||
return 0;
|
||||
} else {
|
||||
error_log("Lidarr API request failed: " . $res->status_line);
|
||||
debug_log("Response body: " . $res->content);
|
||||
return 0;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user