From b4bb20156a1c1c3d348a766216df738f0eae80e5 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Mon, 4 Jan 2021 22:24:24 -0500 Subject: [PATCH] mpdreload: Handle playlist load errors better. When using the "load" command to load tracks from a stored playlist, there is no guarantee that MPD will actually successfully load them. This can occur, for example, if a playlist is created and then some of the files in it are removed from the database. Such failures are not reported directly by the protocol. The "load" command will succeed but you just get less tracks added to the play queue. This gets mpdreload confused as it no longer has an accurate picture of the exact queue positions. Solve this by querying the new queue length after tracks are loaded from the playlist. This allows mpdreload to determine the number of failures and adjust the expected positions accordingly. --- mpdreload.pl | 58 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/mpdreload.pl b/mpdreload.pl index 66bb39d..8cf010d 100755 --- a/mpdreload.pl +++ b/mpdreload.pl @@ -26,6 +26,8 @@ use MPDHacks; my $sock; +my $pl_current_length; + # Returns a hash reference mapping filenames to an array reference listing # the queue IDs for that file in the current play queue. sub get_tracks_in_play_queue { @@ -48,6 +50,8 @@ sub get_tracks_in_play_queue { } } + $pl_current_length = $pos+1; + return (\%matches, \%idmap); } @@ -126,46 +130,74 @@ MPD::run("tagtypes", "clear"); my ($current, $idmap) = get_tracks_in_play_queue(); my $target = get_playlist_files($ARGV[0]); -my $end_position = (keys %$current); -my $num_added = 0; my $add_start; -sub load_tracks($) { - my ($seq) = @_; +sub load_tracks($$) { + my ($seq, $dst) = @_; + my ($newlen, $count); my $start = $add_start // $seq; + my $add_position = $pl_current_length; my $end = $seq+1; - my $add_position = $end_position; + $dst //= $start; + MPD::exec("load", $ARGV[0], "$start:$end"); - $end_position += $end - $start; - MPD::exec("move", "$add_position:$end_position", "$start") - if ($add_position != $start); + MPD::exec("status"); + MPD::exec("command_list_end"); + + while (<$sock>) { + last if (/^OK/); + die($_) if (/^ACK/); + + if (/^(\w+): (.*)$/) { + if ($1 eq "playlistlength") { + $newlen = int($2); + } + } + } + + $count = $newlen - $pl_current_length; + + MPD::exec("command_list_begin"); + if ($newlen > $pl_current_length) { + MPD::exec("move", "$add_position:$newlen", $dst) + if ($add_position != $dst); + } + $pl_current_length = $newlen; undef $add_start; + + return $count; } MPD::exec("command_list_begin"); +my ($num_added, $num_failed) = (0, 0); for (my $i = 0; $i < @$target; $i++) { my $f = $target->[$i]; my $id = shift @{ $current->{$f} }; - load_tracks($i - 1) if (defined $id and defined $add_start); + if (defined $id and defined $add_start) { + my $n = $i - $add_start; + my $m = load_tracks($i-1, $add_start - $num_failed); + + $num_added += $m; + $num_failed += $n - $m; + } if (defined $id) { # Try not to move tracks already in the right place. - MPD::exec("moveid", $id, $i) - if ($i != $idmap->{$id} + $num_added); + MPD::exec("moveid", $id, $i - $num_failed) + if ($i - $num_failed != $idmap->{$id} + $num_added); } else { $add_start //= $i; - $num_added++; } } # Now all unwanted tracks from the original playqueue have been moved to the # end and can be deleted all at once. -my $pos = $add_start // @$target; +my $pos = ($add_start // @$target) - $num_failed; MPD::exec("delete", $pos . ":") if map { @$_ } values %$current; MPD::exec("load", $ARGV[0], "$add_start:") if defined $add_start; MPD::run("command_list_end"); -- 2.43.2