3 # Copyright © 2019-2020 Nick Bowler
5 # Replace the current MPD play queue with a saved playlist, by rearranging
6 # existing queue entries when possible. This avoids losing the current
7 # player state when the loaded playlist is similar to the current queue.
9 # License GPLv3+: GNU General Public License version 3 or any later version.
10 # This is free software: you are free to change and redistribute it.
11 # There is NO WARRANTY, to the extent permitted by law.
16 use Encode::Locale qw(decode_argv);
17 decode_argv(Encode::FB_CROAK);
19 binmode(STDOUT, ":utf8");
21 use Getopt::Long qw(:config gnu_getopt);
24 use lib "$FindBin::Bin";
29 # Returns a hash reference containing all tracks in the current play queue.
30 # The hash keys are filenames.
31 sub get_tracks_in_play_queue {
35 MPD::exec("playlistinfo");
40 if (/^(\w+): (.*)$/) {
42 if (exists($matches{$2})) {
43 $entry = $matches{$2};
46 $matches{$2} = $entry;
50 if (exists($entry->{$1})) {
51 $entry->{$1}->{$2} = 1;
53 $entry->{$1} = { $2 => 1 }
61 # Given an MPD playlist name, returns a reference to an array containing
62 # (in order) the files in the playlist.
63 sub get_playlist_files {
67 MPD::exec("listplaylist", $plname);
72 if (/^(\w+): (.*)$/) {
85 Copyright © 2019 Nick Bowler
86 License GPLv3+: GNU General Public License version 3 or any later version.
87 This is free software: you are free to change and redistribute it.
88 There is NO WARRANTY, to the extent permitted by law.
93 my ($fh) = (@_, *STDERR);
95 print $fh "Usage: $0 [options] playlist\n";
96 print $fh "Try $0 --help for more information.\n" unless (@_ > 0);
100 print_usage(*STDOUT);
102 This is "mpdreload": a tool to reload a stored playlist into the MPD queue.
105 -h, --host=HOST Connect to the MPD server on HOST, overriding defaults.
106 -p, --port=PORT Connect to the MPD server on PORT, overriding defaults.
107 -V, --version Print a version message and then exit.
108 -H, --help Print this message and then exit.
110 Report bugs to <nbowler\@draconx.ca>
115 'host|h=s' => \$MPD::host,
116 'port|p=s' => \$MPD::port,
118 'V|version' => sub { print_version(); exit },
119 'H|help' => sub { print_help(); exit },
120 ) or do { print_usage(); exit 1};
123 print STDERR "Playlist name is required\n" unless @ARGV;
124 print STDERR "Excess command-line arguments\n" if @ARGV;
126 print_usage(); exit 1
129 $sock = MPD::connect();
131 # Retrieve the current play queue and target play queue.
132 my $current = get_tracks_in_play_queue();
133 my $target = get_playlist_files($ARGV[0]);
135 MPD::exec("command_list_begin");
136 for (my $i = 0; $i < @$target; $i++) {
137 my $f = $target->[$i];
138 my $ids = $current->{$f}->{Id};
140 my $id = (keys %$ids)[0];
143 # Remove tracks with no unused queue IDs
144 delete $current->{$f} unless (keys %$ids > 0);
147 MPD::exec("moveid", $id, $i);
149 MPD::exec("addid", $f, $i);
153 # Remove any tracks left from the old play queue.
154 foreach (keys %$current) {
155 my $ids = $current->{$_}->{Id};
156 foreach (keys %$ids) {
157 MPD::exec("deleteid", $_);
161 MPD::exec("command_list_end");