1 /* music player command (mpc)
2 * Copyright (C) 2003-2008 Warren Dukes <warren.dukes@gmail.com>,
3 Eric Wong <normalperson@yhbt.net>,
4 Daniel Brown <danb@cs.utexas.edu>
5 * Copyright (C) 2008-2010 Max Kellermann <max@duempel.org>
6 * Project homepage: http://musicpd.org
7
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "queue.h"
24 #include "tags.h"
25 #include "args.h"
26 #include "charset.h"
27 #include "options.h"
28 #include "util.h"
29 #include "path.h"
30 #include "Compiler.h"
31
32 #include <mpd/client.h>
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37
38 SIMPLE_CMD(cmd_clear, mpd_run_clear, 1)
39 SIMPLE_CMD(cmd_shuffle, mpd_run_shuffle, 1)
40
41 int
cmd_add(int argc,char ** argv,struct mpd_connection * conn)42 cmd_add(int argc, char **argv, struct mpd_connection *conn)
43 {
44 if (contains_absolute_path(argc, argv) && !path_prepare(conn))
45 printErrorAndExit(conn);
46
47 if (!mpd_command_list_begin(conn, false))
48 printErrorAndExit(conn);
49
50 for (int i = 0; i < argc; ++i) {
51 strip_trailing_slash(argv[i]);
52
53 const char *path = argv[i];
54 const char *relative_path = to_relative_path(path);
55 if (relative_path != NULL)
56 path = relative_path;
57
58 if (options.verbosity >= V_VERBOSE)
59 printf("adding: %s\n", path);
60 mpd_send_add(conn, charset_to_utf8(path));
61 }
62
63 if (!mpd_command_list_end(conn))
64 printErrorAndExit(conn);
65
66 if (!mpd_response_finish(conn)) {
67 if (mpd_connection_get_error(conn) == MPD_ERROR_SERVER) {
68 /* check which of the arguments has failed */
69 unsigned location =
70 mpd_connection_get_server_error_location(conn);
71 if (location < (unsigned)argc) {
72 /* we've got a valid location from the
73 server */
74 const char *message =
75 mpd_connection_get_error_message(conn);
76 message = charset_from_utf8(message);
77 fprintf(stderr, "error adding %s: %s\n",
78 argv[location], message);
79 exit(EXIT_FAILURE);
80 }
81 }
82
83 printErrorAndExit(conn);
84 }
85
86 return 0;
87 }
88
89 int
cmd_crop(gcc_unused int argc,gcc_unused char ** argv,struct mpd_connection * conn)90 cmd_crop(gcc_unused int argc, gcc_unused char **argv,
91 struct mpd_connection *conn)
92 {
93 struct mpd_status *status = getStatus(conn);
94 int length = mpd_status_get_queue_length(status) - 1;
95
96 if (length < 0) {
97 mpd_status_free(status);
98 DIE("A playlist longer than 1 song in length is required to crop.\n");
99 } else if (mpd_status_get_state(status) == MPD_STATE_PLAY ||
100 mpd_status_get_state(status) == MPD_STATE_PAUSE) {
101 if (!mpd_command_list_begin(conn, false))
102 printErrorAndExit(conn);
103
104 for (; length >= 0; --length)
105 if (length != mpd_status_get_song_pos(status))
106 mpd_send_delete(conn, length);
107
108 mpd_status_free(status);
109
110 mpd_command_list_end(conn);
111 my_finishCommand(conn);
112 return 0;
113 } else {
114 mpd_status_free(status);
115 DIE("You need to be playing to crop the playlist\n");
116 }
117 }
118
119 int
cmd_del(int argc,char ** argv,struct mpd_connection * conn)120 cmd_del(int argc, char **argv, struct mpd_connection *conn)
121 {
122 struct mpd_status *status = getStatus(conn);
123
124 const unsigned plLength = mpd_status_get_queue_length(status);
125
126 bool *songsToDel = malloc(plLength);
127 memset(songsToDel, false, plLength);
128
129 for (unsigned i = 0; i < (unsigned)argc; ++i) {
130 const char *const s = argv[i];
131
132 char *t;
133 int range[2];
134 range[0] = strtol(s, &t, 10);
135
136 /* If argument is 0 current song and we're not stopped */
137 if (range[0] == 0 && strlen(s) == 1 &&
138 (mpd_status_get_state(status) == MPD_STATE_PLAY ||
139 mpd_status_get_state(status) == MPD_STATE_PAUSE))
140 range[0] = mpd_status_get_song_pos(status) + 1;
141
142 if (s==t)
143 DIE("error parsing song numbers from: %s\n", argv[i]);
144 else if (*t=='-') {
145 char *t2;
146 range[1] = strtol(t+1, &t2, 10);
147 if(t + 1 == t2 || *t2!='\0')
148 DIE("error parsing range from: %s\n", argv[i]);
149 } else if (*t=='\0')
150 range[1] = range[0];
151 else
152 DIE("error parsing song numbers from: %s\n", argv[i]);
153
154 if (range[0] <= 0 || range[1] <= 0) {
155 if (range[0] == range[1])
156 DIE("song number must be positive: %i\n",
157 range[0]);
158 else
159 DIE("song numbers must be positive: %i to %i\n",
160 range[0], range[1]);
161 }
162
163 if (range[1] < range[0])
164 DIE("song range must be from low to high: %i to %i\n",range[0],range[1]);
165
166 if ((unsigned)range[1] > plLength)
167 DIE("song number does not exist: %i\n",range[1]);
168
169 memset(songsToDel + range[0] - 1, true, range[1] - range[0] + 1);
170 }
171
172 if (!mpd_command_list_begin(conn, false))
173 printErrorAndExit(conn);
174
175 int songsDeleted = 0;
176 for (unsigned i = 0; i < plLength; ++i) {
177 if (songsToDel[i]) {
178 mpd_send_delete(conn, i - songsDeleted);
179 ++songsDeleted;
180 }
181 }
182
183 mpd_status_free(status);
184 free(songsToDel);
185
186 mpd_command_list_end(conn);
187 my_finishCommand(conn);
188 return 0;
189 }
190
191 int
cmd_playlist(int argc,char ** argv,struct mpd_connection * conn)192 cmd_playlist(int argc, char **argv, struct mpd_connection *conn)
193 {
194 bool command_list = false;
195
196 #if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
197 /* ask MPD to omit the tags which are not used by the
198 `--format` to reduce network transfer for tag values we're
199 not going to use anyway (requires MPD 0.21 and libmpdclient
200 2.12) */
201 if (mpd_connection_cmp_server_version(conn, 0, 21, 0) >= 0) {
202 if (!mpd_command_list_begin(conn, false) ||
203 !send_tag_types_for_format(conn, options.format))
204 printErrorAndExit(conn);
205 command_list = true;
206 }
207 #endif
208
209 bool ret = argc > 0
210 ? mpd_send_list_playlist_meta(conn, argv[0])
211 : mpd_send_list_queue_meta(conn);
212
213 if (ret == false)
214 printErrorAndExit(conn);
215
216 if (command_list && !mpd_command_list_end(conn))
217 printErrorAndExit(conn);
218
219 print_entity_list(conn, MPD_ENTITY_TYPE_SONG, true);
220 my_finishCommand(conn);
221 return 0;
222 }
223
224 gcc_pure
225 static unsigned
query_queue_length(struct mpd_connection * conn)226 query_queue_length(struct mpd_connection *conn)
227 {
228 struct mpd_status *status = getStatus(conn);
229 const unsigned length = mpd_status_get_queue_length(status);
230 mpd_status_free(status);
231 return length;
232 }
233
234 static void
queue_range(struct mpd_connection * conn,unsigned start,unsigned end,int next_id)235 queue_range(struct mpd_connection *conn, unsigned start, unsigned end,
236 int next_id)
237 {
238 struct mpd_song *song = next_id >= 0
239 ? mpd_run_get_queue_song_id(conn, next_id)
240 : mpd_run_current_song(conn);
241 unsigned prio = 0;
242 if (song != NULL) {
243 prio = mpd_song_get_prio(song);
244 mpd_song_free(song);
245 }
246
247 if (prio < 255)
248 ++prio;
249
250 if (!mpd_run_prio_range(conn, prio, start, end))
251 printErrorAndExit(conn);
252 }
253
cmd_insert(int argc,char ** argv,struct mpd_connection * conn)254 int cmd_insert (int argc, char ** argv, struct mpd_connection *conn )
255 {
256 struct mpd_status *status = getStatus(conn);
257 const unsigned from = mpd_status_get_queue_length(status);
258 const int cur_pos = mpd_status_get_song_pos(status);
259 const int next_id = mpd_status_get_next_song_id(status);
260 const bool random_mode = mpd_status_get_random(status);
261 mpd_status_free(status);
262
263 int ret = cmd_add(argc, argv, conn);
264 if (ret != 0)
265 return ret;
266
267 /* check the new queue length to find out how many songs were
268 appended */
269 const unsigned end = query_queue_length(conn);
270
271 if (random_mode) {
272 queue_range(conn, from, end, next_id);
273 return 0;
274 }
275
276 if (end == from)
277 return 0;
278
279 /* move those songs to right after the current one */
280 if (!mpd_run_move_range(conn, from, end, cur_pos + 1))
281 printErrorAndExit(conn);
282
283 return 0;
284 }
285
286
287 int
cmd_prio(int argc,char ** argv,struct mpd_connection * conn)288 cmd_prio(int argc, char **argv, struct mpd_connection *conn)
289 {
290 char *endptr;
291 int i = 0;
292 const char *s = argv[i++];
293 int prio = strtol(s, &endptr, 10);
294 if (endptr == s || *endptr != 0)
295 DIE("Failed to parse number: %s\n", s);
296 if (prio < 0 || prio > 255)
297 DIE("Priority must be between 0 and 255: %s\n", s);
298
299 if (!mpd_command_list_begin(conn, false))
300 printErrorAndExit(conn);
301
302 while (i < argc) {
303 s = argv[i++];
304 int position = strtol(s, &endptr, 10);
305 if (endptr == s || *endptr != 0)
306 DIE("Failed to parse number: %s\n", s);
307 if (position < 1)
308 DIE("Invalid song position: %s\n", s);
309
310 /* mpc's song positions are 1-based, but MPD uses
311 0-based positions */
312 --position;
313
314 if (!mpd_send_prio(conn, prio, position))
315 break;
316 }
317
318 mpd_command_list_end(conn);
319 my_finishCommand(conn);
320 return 0;
321 }
322