]> git.draconx.ca Git - mpdhacks.git/blob - mpdreload.pl
Fix print_usage file handle in the perl scripts.
[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 containing all tracks in the current play queue.
30 # The hash keys are filenames.
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                                 if (exists($matches{$2})) {
43                                         $entry = $matches{$2};
44                                 } else {
45                                         $entry = {};
46                                         $matches{$2} = $entry;
47                                 }
48                         }
49
50                         if (exists($entry->{$1})) {
51                                 $entry->{$1}->{$2} = 1;
52                         } else {
53                                 $entry->{$1} = { $2 => 1 }
54                         }
55                 }
56         }
57
58         return \%matches;
59 }
60
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 {
64         my ($plname) = @_;
65         my @files;
66
67         MPD::exec("listplaylist", $plname);
68         while (<$sock>) {
69                 last if /^OK/;
70                 die($_) if /^ACK/;
71
72                 if (/^(\w+): (.*)$/) {
73                         if ($1 eq "file") {
74                                 push @files, $2;
75                         }
76                 }
77         }
78
79         return \@files;
80 }
81
82 sub print_version {
83         print <<EOF
84 mpdreload.pl 0.8
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.
89 EOF
90 }
91
92 sub print_usage {
93         my ($fh) = (@_, *STDERR);
94
95         print $fh "Usage: $0 [options] playlist\n";
96         print $fh "Try $0 --help for more information.\n" unless (@_ > 0);
97 }
98
99 sub print_help {
100         print_usage(*STDOUT);
101         print <<EOF
102 This is "mpdreload": a tool to reload a stored playlist into the MPD queue.
103
104 Options:
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.
109
110 Report bugs to <nbowler\@draconx.ca>
111 EOF
112 }
113
114 GetOptions(
115         'host|h=s' => \$MPD::host,
116         'port|p=s' => \$MPD::port,
117
118         'V|version' => sub { print_version(); exit },
119         'H|help'    => sub { print_help(); exit },
120 ) or do { print_usage(); exit 1};
121
122 if (@ARGV != 1) {
123         print STDERR "Playlist name is required\n" unless @ARGV;
124         print STDERR "Excess command-line arguments\n" if @ARGV;
125
126         print_usage(); exit 1
127 };
128
129 $sock = MPD::connect();
130
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]);
134
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};
139
140         my $id = (keys %$ids)[0];
141         delete $ids->{$id};
142
143         # Remove tracks with no unused queue IDs
144         delete $current->{$f} unless (keys %$ids > 0);
145
146         if (defined $id) {
147                 MPD::exec("moveid", $id, $i);
148         } else {
149                 MPD::exec("addid", $f, $i);
150         }
151 }
152
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", $_);
158         }
159 }
160
161 MPD::exec("command_list_end");
162 while (<$sock>) {
163         last if /^OK$/;
164         die($_) if /^ACK/;
165 }