]> git.draconx.ca Git - mpdhacks.git/blob - mpdreload.pl
mpdreload: Simplify script operation.
[mpdhacks.git] / mpdreload.pl
1 #!/usr/bin/env perl
2 #
3 # Copyright © 2019-2020 Nick Bowler
4 #
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.
8 #
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.
12
13 use strict;
14 use utf8;
15
16 use Encode::Locale qw(decode_argv);
17 decode_argv(Encode::FB_CROAK);
18
19 binmode(STDOUT, ":utf8");
20
21 use Getopt::Long qw(:config gnu_getopt);
22
23 use FindBin;
24 use lib "$FindBin::Bin";
25 use MPDHacks;
26
27 my $sock;
28
29 # Returns a hash reference mapping filenames to an array reference listing
30 # the queue IDs for that file in the current play queue.
31 sub get_tracks_in_play_queue {
32         my %matches;
33         my $entry;
34
35         MPD::exec("playlistinfo");
36         while (<$sock>) {
37                 last if /^OK/;
38                 die($_) if /^ACK/;
39
40                 if (/^(\w+): (.*)$/) {
41                         if ($1 eq "file") {
42                                 $entry = $matches{$2} //= [];
43                         } elsif ($1 eq "Id") {
44                                 push @$entry, $2;
45                         }
46                 }
47         }
48
49         return \%matches;
50 }
51
52 # Given an MPD playlist name, returns a reference to an array containing
53 # (in order) the files in the playlist.
54 sub get_playlist_files {
55         my ($plname) = @_;
56         my @files;
57
58         MPD::exec("listplaylist", $plname);
59         while (<$sock>) {
60                 last if /^OK/;
61                 die($_) if /^ACK/;
62
63                 if (/^(\w+): (.*)$/) {
64                         if ($1 eq "file") {
65                                 push @files, $2;
66                         }
67                 }
68         }
69
70         return \@files;
71 }
72
73 sub print_version {
74         print <<EOF
75 mpdreload.pl 0.8
76 Copyright © 2019 Nick Bowler
77 License GPLv3+: GNU General Public License version 3 or any later version.
78 This is free software: you are free to change and redistribute it.
79 There is NO WARRANTY, to the extent permitted by law.
80 EOF
81 }
82
83 sub print_usage {
84         my ($fh) = (@_, *STDERR);
85
86         print $fh "Usage: $0 [options] playlist\n";
87         print $fh "Try $0 --help for more information.\n" unless (@_ > 0);
88 }
89
90 sub print_help {
91         print_usage(*STDOUT);
92         print <<EOF
93 This is "mpdreload": a tool to reload a stored playlist into the MPD queue.
94
95 Options:
96   -h, --host=HOST   Connect to the MPD server on HOST, overriding defaults.
97   -p, --port=PORT   Connect to the MPD server on PORT, overriding defaults.
98   -V, --version     Print a version message and then exit.
99   -H, --help        Print this message and then exit.
100
101 Report bugs to <nbowler\@draconx.ca>
102 EOF
103 }
104
105 GetOptions(
106         'host|h=s' => \$MPD::host,
107         'port|p=s' => \$MPD::port,
108
109         'V|version' => sub { print_version(); exit },
110         'H|help'    => sub { print_help(); exit },
111 ) or do { print_usage(); exit 1};
112
113 if (@ARGV != 1) {
114         print STDERR "Playlist name is required\n" unless @ARGV;
115         print STDERR "Excess command-line arguments\n" if @ARGV;
116
117         print_usage(); exit 1
118 };
119
120 $sock = MPD::connect();
121
122 # Retrieve the current play queue and target play queue.
123 MPD::run("tagtypes", "clear");
124 my $current = get_tracks_in_play_queue();
125 my $target = get_playlist_files($ARGV[0]);
126
127 MPD::exec("command_list_begin");
128 for (my $i = 0; $i < @$target; $i++) {
129         my $f = $target->[$i];
130         my $id = shift @{ $current->{$f} };
131
132         if (defined $id) {
133                 MPD::exec("moveid", $id, $i);
134         } else {
135                 MPD::exec("addid", $f, $i);
136         }
137 }
138
139 # Remove any tracks left from the old play queue.
140 MPD::exec("deleteid", $_) foreach (map { @$_ } values %$current);
141 MPD::run("command_list_end");