]> git.draconx.ca Git - mpdhacks.git/commitdiff
mpdreload: Protocol optimizations.
authorNick Bowler <nbowler@draconx.ca>
Fri, 19 Jun 2020 05:01:39 +0000 (01:01 -0400)
committerNick Bowler <nbowler@draconx.ca>
Fri, 19 Jun 2020 05:01:39 +0000 (01:01 -0400)
Try harder to send fewer commands to the MPD server.  In particular:

* We can submit commands to batch-load subranges from the target
  playlist into the play queue, then move them as a group into the
  final location.

* We don't need to send move commands for tracks that are already in
  the correct final position, which helps when reloading a playlist
  very similar to the current play queue.

* By arranging for all unwanted tracks to be shifted to the end of
  the play queue during the consolidation process, these can all be
  deleted with a single command.

mpdreload.pl

index c82353eb3e91f435a7bc0103b00e2e5fa3de6691..3a7e1a1bd01f9537cc1aaa017b869e2c56411ca2 100755 (executable)
@@ -29,8 +29,8 @@ my $sock;
 # 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 {
-       my %matches;
-       my $entry;
+       my (%matches, %idmap, $entry);
+       my $pos = -1;
 
        MPD::exec("playlistinfo");
        while (<$sock>) {
@@ -40,13 +40,15 @@ sub get_tracks_in_play_queue {
                if (/^(\w+): (.*)$/) {
                        if ($1 eq "file") {
                                $entry = $matches{$2} //= [];
+                               $pos++;
                        } elsif ($1 eq "Id") {
                                push @$entry, $2;
+                               $idmap{$2} = $pos;
                        }
                }
        }
 
-       return \%matches;
+       return (\%matches, \%idmap);
 }
 
 # Given an MPD playlist name, returns a reference to an array containing
@@ -121,21 +123,41 @@ $sock = MPD::connect();
 
 # Retrieve the current play queue and target play queue.
 MPD::run("tagtypes", "clear");
-my $current = get_tracks_in_play_queue();
+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;
+
 MPD::exec("command_list_begin");
 for (my $i = 0; $i < @$target; $i++) {
        my $f = $target->[$i];
        my $id = shift @{ $current->{$f} };
 
+       if (defined $id and defined $add_start) {
+               my $add_position = $end_position;
+
+               MPD::exec("load", $ARGV[0], "$add_start:$i");
+               $end_position += $i - $add_start;
+               MPD::exec("move", "$add_position:$end_position", "$add_start");
+
+               undef $add_start;
+       }
+
        if (defined $id) {
-               MPD::exec("moveid", $id, $i);
+               # Try not to move tracks already in the right place.
+               MPD::exec("moveid", $id, $i)
+                       if ($i != $idmap->{$id} + $num_added);
        } else {
-               MPD::exec("addid", $f, $i);
+               $add_start //= $i;
+               $num_added++;
        }
 }
 
-# Remove any tracks left from the old play queue.
-MPD::exec("deleteid", $_) foreach (map { @$_ } values %$current);
+# Now all unwanted tracks from the original playqueue have been moved to the
+# end and can be deleted all at once.
+my $rem = ($add_start // @$target) - @$target;
+MPD::exec("delete", @$target - $rem . ":") if map { @$_ } values %$current;
+MPD::exec("load", $ARGV[0], "$add_start:") if defined $add_start;
 MPD::run("command_list_end");