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