QueueCommands.cxx 9.75 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2013 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"
#include "QueueCommands.hxx"
22
#include "CommandError.hxx"
23
#include "DatabaseQueue.hxx"
24
#include "SongFilter.hxx"
25
#include "DatabaseSelection.hxx"
26
#include "Playlist.hxx"
27
#include "PlaylistPrint.hxx"
Max Kellermann's avatar
Max Kellermann committed
28
#include "ClientFile.hxx"
29
#include "Client.hxx"
30
#include "Partition.hxx"
Max Kellermann's avatar
Max Kellermann committed
31 32
#include "protocol/ArgParser.hxx"
#include "protocol/Result.hxx"
Max Kellermann's avatar
Max Kellermann committed
33
#include "ls.hxx"
Max Kellermann's avatar
Max Kellermann committed
34
#include "util/UriUtil.hxx"
35
#include "util/Error.hxx"
36
#include "fs/AllocatedPath.hxx"
37

38
#include <limits>
39

40 41
#include <string.h>

42
CommandResult
43
handle_add(Client &client, gcc_unused int argc, char *argv[])
44 45
{
	char *uri = argv[1];
46
	PlaylistResult result;
47

48
	if (memcmp(uri, "file:///", 8) == 0) {
49
		const char *path_utf8 = uri + 7;
50
		const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
51 52 53 54

		if (path_fs.IsNull()) {
			command_error(client, ACK_ERROR_NO_EXIST,
				      "unsupported file name");
55
			return CommandResult::ERROR;
56
		}
57

58 59
		Error error;
		if (!client_allow_file(client, path_fs, error))
60 61
			return print_error(client, error);

62
		result = client.partition.AppendFile(path_utf8);
63 64 65 66 67 68 69
		return print_playlist_result(client, result);
	}

	if (uri_has_scheme(uri)) {
		if (!uri_supported_scheme(uri)) {
			command_error(client, ACK_ERROR_NO_EXIST,
				      "unsupported URI scheme");
70
			return CommandResult::ERROR;
71 72
		}

73
		result = client.partition.AppendURI(uri);
74 75 76
		return print_playlist_result(client, result);
	}

77
	const DatabaseSelection selection(uri, true);
78
	Error error;
79
	return AddFromDatabase(client.partition, selection, error)
80
		? CommandResult::OK
81 82 83
		: print_error(client, error);
}

84
CommandResult
85
handle_addid(Client &client, int argc, char *argv[])
86 87 88
{
	char *uri = argv[1];
	unsigned added_id;
89
	PlaylistResult result;
90

91
	if (memcmp(uri, "file:///", 8) == 0) {
92
		const char *path_utf8 = uri + 7;
93
		const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
94 95 96 97

		if (path_fs.IsNull()) {
			command_error(client, ACK_ERROR_NO_EXIST,
				      "unsupported file name");
98
			return CommandResult::ERROR;
99
		}
100

101 102
		Error error;
		if (!client_allow_file(client, path_fs, error))
103 104
			return print_error(client, error);

105
		result = client.partition.AppendFile(path_utf8, &added_id);
106 107 108 109
	} else {
		if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
			command_error(client, ACK_ERROR_NO_EXIST,
				      "unsupported URI scheme");
110
			return CommandResult::ERROR;
111 112
		}

113
		result = client.partition.AppendURI(uri, &added_id);
114 115
	}

116
	if (result != PlaylistResult::SUCCESS)
117 118 119 120 121
		return print_playlist_result(client, result);

	if (argc == 3) {
		unsigned to;
		if (!check_unsigned(client, &to, argv[2]))
122
			return CommandResult::ERROR;
123
		result = client.partition.MoveId(added_id, to);
124
		if (result != PlaylistResult::SUCCESS) {
125
			CommandResult ret =
126
				print_playlist_result(client, result);
127
			client.partition.DeleteId(added_id);
128 129 130 131 132
			return ret;
		}
	}

	client_printf(client, "Id: %u\n", added_id);
133
	return CommandResult::OK;
134 135
}

136
CommandResult
137
handle_delete(Client &client, gcc_unused int argc, char *argv[])
138 139 140 141
{
	unsigned start, end;

	if (!check_range(client, &start, &end, argv[1]))
142
		return CommandResult::ERROR;
143

144
	PlaylistResult result = client.partition.DeleteRange(start, end);
145 146 147
	return print_playlist_result(client, result);
}

148
CommandResult
149
handle_deleteid(Client &client, gcc_unused int argc, char *argv[])
150 151 152 153
{
	unsigned id;

	if (!check_unsigned(client, &id, argv[1]))
154
		return CommandResult::ERROR;
155

156
	PlaylistResult result = client.partition.DeleteId(id);
157 158 159
	return print_playlist_result(client, result);
}

160
CommandResult
161
handle_playlist(Client &client,
162
		gcc_unused int argc, gcc_unused char *argv[])
163
{
164
	playlist_print_uris(client, client.playlist);
165
	return CommandResult::OK;
166 167
}

168
CommandResult
169
handle_shuffle(gcc_unused Client &client,
170
	       gcc_unused int argc, gcc_unused char *argv[])
171
{
172
	unsigned start = 0, end = client.playlist.queue.GetLength();
173
	if (argc == 2 && !check_range(client, &start, &end, argv[1]))
174
		return CommandResult::ERROR;
175

176
	client.partition.Shuffle(start, end);
177
	return CommandResult::OK;
178 179
}

180
CommandResult
181
handle_clear(gcc_unused Client &client,
182
	     gcc_unused int argc, gcc_unused char *argv[])
183
{
184
	client.partition.ClearQueue();
185
	return CommandResult::OK;
186 187
}

188
CommandResult
189
handle_plchanges(Client &client, gcc_unused int argc, char *argv[])
190 191 192 193
{
	uint32_t version;

	if (!check_uint32(client, &version, argv[1]))
194
		return CommandResult::ERROR;
195

196
	playlist_print_changes_info(client, client.playlist, version);
197
	return CommandResult::OK;
198 199
}

200
CommandResult
201
handle_plchangesposid(Client &client, gcc_unused int argc, char *argv[])
202 203 204 205
{
	uint32_t version;

	if (!check_uint32(client, &version, argv[1]))
206
		return CommandResult::ERROR;
207

208
	playlist_print_changes_position(client, client.playlist, version);
209
	return CommandResult::OK;
210 211
}

212
CommandResult
213
handle_playlistinfo(Client &client, int argc, char *argv[])
214
{
215
	unsigned start = 0, end = std::numeric_limits<unsigned>::max();
216 217 218
	bool ret;

	if (argc == 2 && !check_range(client, &start, &end, argv[1]))
219
		return CommandResult::ERROR;
220

221
	ret = playlist_print_info(client, client.playlist, start, end);
222 223
	if (!ret)
		return print_playlist_result(client,
224
					     PlaylistResult::BAD_RANGE);
225

226
	return CommandResult::OK;
227 228
}

229
CommandResult
230
handle_playlistid(Client &client, int argc, char *argv[])
231 232 233 234
{
	if (argc >= 2) {
		unsigned id;
		if (!check_unsigned(client, &id, argv[1]))
235
			return CommandResult::ERROR;
236

237
		bool ret = playlist_print_id(client, client.playlist, id);
238 239
		if (!ret)
			return print_playlist_result(client,
240
						     PlaylistResult::NO_SUCH_SONG);
241
	} else {
242
		playlist_print_info(client, client.playlist,
243
				    0, std::numeric_limits<unsigned>::max());
244 245
	}

246
	return CommandResult::OK;
247 248
}

249
static CommandResult
250
handle_playlist_match(Client &client, int argc, char *argv[],
251 252
		      bool fold_case)
{
253 254
	SongFilter filter;
	if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
255
		command_error(client, ACK_ERROR_ARG, "incorrect arguments");
256
		return CommandResult::ERROR;
257 258
	}

259
	playlist_print_find(client, client.playlist, filter);
260
	return CommandResult::OK;
261 262
}

263
CommandResult
264
handle_playlistfind(Client &client, int argc, char *argv[])
265 266 267 268
{
	return handle_playlist_match(client, argc, argv, false);
}

269
CommandResult
270
handle_playlistsearch(Client &client, int argc, char *argv[])
271 272 273 274
{
	return handle_playlist_match(client, argc, argv, true);
}

275
CommandResult
276
handle_prio(Client &client, int argc, char *argv[])
277 278 279 280
{
	unsigned priority;

	if (!check_unsigned(client, &priority, argv[1]))
281
		return CommandResult::ERROR;
282 283 284 285

	if (priority > 0xff) {
		command_error(client, ACK_ERROR_ARG,
			      "Priority out of range: %s", argv[1]);
286
		return CommandResult::ERROR;
287 288 289 290 291 292
	}

	for (int i = 2; i < argc; ++i) {
		unsigned start_position, end_position;
		if (!check_range(client, &start_position, &end_position,
				 argv[i]))
293
			return CommandResult::ERROR;
294

295
		PlaylistResult result =
296
			client.partition.SetPriorityRange(start_position,
297 298
							   end_position,
							   priority);
299
		if (result != PlaylistResult::SUCCESS)
300 301 302
			return print_playlist_result(client, result);
	}

303
	return CommandResult::OK;
304 305
}

306
CommandResult
307
handle_prioid(Client &client, int argc, char *argv[])
308 309 310 311
{
	unsigned priority;

	if (!check_unsigned(client, &priority, argv[1]))
312
		return CommandResult::ERROR;
313 314 315 316

	if (priority > 0xff) {
		command_error(client, ACK_ERROR_ARG,
			      "Priority out of range: %s", argv[1]);
317
		return CommandResult::ERROR;
318 319 320 321 322
	}

	for (int i = 2; i < argc; ++i) {
		unsigned song_id;
		if (!check_unsigned(client, &song_id, argv[i]))
323
			return CommandResult::ERROR;
324

325
		PlaylistResult result =
326
			client.partition.SetPriorityId(song_id, priority);
327
		if (result != PlaylistResult::SUCCESS)
328 329 330
			return print_playlist_result(client, result);
	}

331
	return CommandResult::OK;
332 333
}

334
CommandResult
335
handle_move(Client &client, gcc_unused int argc, char *argv[])
336 337 338 339 340
{
	unsigned start, end;
	int to;

	if (!check_range(client, &start, &end, argv[1]))
341
		return CommandResult::ERROR;
342
	if (!check_int(client, &to, argv[2]))
343
		return CommandResult::ERROR;
344

345
	PlaylistResult result =
346
		client.partition.MoveRange(start, end, to);
347 348 349
	return print_playlist_result(client, result);
}

350
CommandResult
351
handle_moveid(Client &client, gcc_unused int argc, char *argv[])
352 353 354 355 356
{
	unsigned id;
	int to;

	if (!check_unsigned(client, &id, argv[1]))
357
		return CommandResult::ERROR;
358
	if (!check_int(client, &to, argv[2]))
359
		return CommandResult::ERROR;
360
	PlaylistResult result = client.partition.MoveId(id, to);
361 362 363
	return print_playlist_result(client, result);
}

364
CommandResult
365
handle_swap(Client &client, gcc_unused int argc, char *argv[])
366 367 368 369
{
	unsigned song1, song2;

	if (!check_unsigned(client, &song1, argv[1]))
370
		return CommandResult::ERROR;
371
	if (!check_unsigned(client, &song2, argv[2]))
372
		return CommandResult::ERROR;
373

374
	PlaylistResult result =
375
		client.partition.SwapPositions(song1, song2);
376 377 378
	return print_playlist_result(client, result);
}

379
CommandResult
380
handle_swapid(Client &client, gcc_unused int argc, char *argv[])
381 382 383 384
{
	unsigned id1, id2;

	if (!check_unsigned(client, &id1, argv[1]))
385
		return CommandResult::ERROR;
386
	if (!check_unsigned(client, &id2, argv[2]))
387
		return CommandResult::ERROR;
388

389
	PlaylistResult result = client.partition.SwapIds(id1, id2);
390 391
	return print_playlist_result(client, result);
}