From 451ee83f72fc40eb9ecf728f567c690174544416 Mon Sep 17 00:00:00 2001 From: Damyan Ivanov Date: Thu, 11 Nov 2021 09:43:26 +0000 Subject: [PATCH] Log::Any, proper idle looping could not make Net::Async::MPD->idle work as expected, and the "manual" method seems reliable --- bin/mpd-feeder | 157 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 117 insertions(+), 40 deletions(-) diff --git a/bin/mpd-feeder b/bin/mpd-feeder index 3cc2206..843fbbe 100755 --- a/bin/mpd-feeder +++ b/bin/mpd-feeder @@ -3,12 +3,15 @@ use v5.32; use Getopt::Long (); +use Log::Any qw($log); +use Log::Any::Adapter Stderr => log_level => 'trace'; use Object::Pad; use Syntax::Keyword::Try; class Options { use Time::Duration qw(duration_exact); use Time::Duration::Parse qw(parse_duration); + has $log_level :reader = 'warn'; has $target_queue_length :reader = 10; has $mpd_host :reader = undef; has $mpd_port :reader = undef; @@ -18,24 +21,14 @@ class Options { has $min_album_interval :reader = parse_duration('5h'); has $min_song_interval :reader = parse_duration('13d'); has $min_artist_interval :reader = parse_duration('1h 15m'); - has $verbose :reader = 0; has $single :reader = 0; has $one_shot :reader = 0; has $skip_db_update :reader = 0; has $dump_config :reader = 0; - method verb($message) { - return unless $self->opt->verbose; - warn "$message\n"; - } - method dbg($message) { - return unless $self->opt->verbose > 1; - warn "$message\n"; - } - method parse_command_line { Getopt::Long::GetOptions( - 'v|verbose+' => \$verbose, + 'log-level=s' => \$log_level, 'dump-config!' => \$dump_config, 's|single!' => \$single, 'one-shot!' => \$one_shot, @@ -71,7 +64,7 @@ class Options { method dump { say "[mpd-feeder]"; - say "verbose = $verbose"; + say "log_level = $log_level"; say ""; say "[mpd]"; say "host = " . ( $mpd_host // '' ); @@ -97,7 +90,7 @@ class Options { handle_config_option( $ini => mpd => host => \$mpd_host ); handle_config_option( $ini => mpd => port => \$mpd_port ); - handle_config_option( $ini => 'mpd-feeder' => verbose => \$verbose ); + handle_config_option( $ini => 'mpd-feeder' => log_level => \$log_level ); handle_config_option( $ini => queue => 'target-length' => \$target_queue_length ); @@ -132,7 +125,9 @@ use constant DEFAULT_CONFIG_FILE => '/etc/mpd-feeder/mpd-feeder.conf'; use DBD::Pg; use DBI; +use Log::Any qw($log); use Net::Async::MPD; + ADJUST { $opt = Options->new; @@ -149,6 +144,8 @@ use Net::Async::MPD; $opt->parse_command_line; + Log::Any::Adapter->set( Stderr => log_level => $opt->log_level ); + unless ($opt->dump_config) { my %conn = ( auto_connect => 1 ); $conn{host} = $opt->mpd_host if $opt->mpd_host; @@ -169,7 +166,9 @@ use Net::Async::MPD; $opt->db_user, $opt->db_password, { RaiseError => 1, AutoCommit => 1 } ); + $log->info("Connected to ".$opt->db_path); $db_generation = $self->db_get_option('generation'); + $log->debug("DB generation is $db_generation"); } method db_get_option($name) { @@ -212,12 +211,19 @@ SQL } method db_remove_stale_entries { - $db->prepare_cached('DELETE FROM songs WHERE generation <> ?') - ->execute($db_generation); - $db->prepare_cached('DELETE FROM albums WHERE generation <> ?') - ->execute($db_generation); - $db->prepare_cached('DELETE FROM artists WHERE generation <> ?') - ->execute($db_generation); + my $sth = + $db->prepare_cached('DELETE FROM songs WHERE generation <> ?'); + $sth->execute($db_generation); + $log->debug( sprintf( "Deleted %d stale songs", $sth->rows ) ); + + $sth = $db->prepare_cached('DELETE FROM albums WHERE generation <> ?'); + $sth->execute($db_generation); + $log->debug( sprintf( "Deleted %d stale albums", $sth->rows ) ); + + $sth = + $db->prepare_cached('DELETE FROM artists WHERE generation <> ?'); + $sth->execute($db_generation); + $log->debug( sprintf( "Deleted %d stale artists", $sth->rows ) ); } method db_note_song_qeued($item) { @@ -233,6 +239,7 @@ SQL } method update_db() { + $log->info('Updating song database'); $mpd->send('listallinfo')->on_done( sub { try { @@ -250,7 +257,7 @@ SQL $song_count++; } - $self->db_store_song($song, $artist, $album); + $log->info("Updated data about $song_count songs"); $self->db_remove_stale_entries; @@ -300,31 +307,101 @@ SQL return @result; } - method queue_songs($num = undef) { + method queue_songs($num = undef, $callback = undef) { if (!defined $num) { - $mpd->send('playlist')->on_done( sub { - my $present = scalar @_; + $mpd->send('playlist')->on_done( + sub { + my $present = scalar @{ $_[0] }; + + $log->notice("Playlist contains $present songs"); + if ( $present < $opt->target_queue_length ) { + $self->queue_songs( + $opt->target_queue_length - $present, $callback ); + } + else { + $callback->() if $callback; + } + } + ); - $self->queue_songs( $opt->target_queue_length - $present ) - if $present < $opt->target_queue_length; - } ); + return; } - else { - my @list = $self->db_find_suitable_songs($num); - - if (@list < $num) { - $mpd->loop->add( - IO::Async::Timer::Countdown->new( - delay => 15, - on_expire => sub { $self->queue_songs($num) }, - ) - ); - } - else { - $mpd->send( [ map {"add $_->{song}"} @list ] ); + + my @list = $self->db_find_suitable_songs($num); + + die "Found no suitable songs" unless @list; + + if ( @list < $num ) { + $log->warn( + sprintf( + 'Found only %d suitable songs instead of %d', + scalar(@list), $num + ) + ); + } + + $log->info("About to add $num songs to the playlist"); + + my @paths; + for my $song (@list) { + my $path = $song->{song}; + $path =~ s/"/\\"/g; + push @paths, $path; + } + + $log->debug( "Adding " . join( ', ', map {"«$_»"} @paths ) ); + my @commands; + for (@paths) { + push @commands, [ add => "\"$_\"" ]; + } + my $f = $mpd->send( \@commands ); + warn "here"; + $f->on_fail( sub { die @_ } ); + $f->on_done( + sub { + warn $_ for @_; $self->db_note_song_qeued($_) for @list; + $callback->(@_) if $callback; } - } + ); + + warn "here"; + } + + method prepare_to_wait_idle { + $log->trace('declaring idle mode'); + $mpd->send('idle database playlist')->on_done( + sub { + warn $_ for @_; + my $result = shift; + use JSON; warn to_json($result); + + if ( $result->{changed} eq 'database' ) { + $self->update_db; + $self->prepare_to_wait_idle; + } + elsif ( $result->{changed} eq 'playlist' ) { + $self->queue_songs( undef, + sub { $self->prepare_to_wait_idle } ); + } + else { + use JSON; + $log->warn( + "Unknown result from idle: " . to_json($result) ); + $self->prepare_to_wait_idle; + } + } + ); + } + + method run { + $mpd->on( + close => sub { + die "Connection to MPD lost"; + } + ); + + $self->prepare_to_wait_idle; } } -- 2.39.2