DatabasePrint.cxx 6.61 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * 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"
21
#include "DatabasePrint.hxx"
Max Kellermann's avatar
Max Kellermann committed
22
#include "Selection.hxx"
23
#include "SongFilter.hxx"
Max Kellermann's avatar
Max Kellermann committed
24 25
#include "SongPrint.hxx"
#include "TimePrint.hxx"
26
#include "client/Client.hxx"
27
#include "tag/Tag.hxx"
28
#include "LightSong.hxx"
29 30
#include "LightDirectory.hxx"
#include "PlaylistInfo.hxx"
31
#include "Interface.hxx"
32
#include "fs/Traits.hxx"
33

34
#include <functional>
35

36 37 38 39 40 41 42 43
static const char *
ApplyBaseFlag(const char *uri, bool base)
{
	if (base)
		uri = PathTraitsUTF8::GetBase(uri);
	return uri;
}

44
static void
45
PrintDirectoryURI(Client &client, bool base, const LightDirectory &directory)
46
{
47 48
	client_printf(client, "directory: %s\n",
		      ApplyBaseFlag(directory.GetPath(), base));
49 50
}

51
static bool
52
PrintDirectoryBrief(Client &client, bool base, const LightDirectory &directory)
53
{
54
	if (!directory.IsRoot())
55
		PrintDirectoryURI(client, base, directory);
56

57
	return true;
58 59
}

60
static bool
61
PrintDirectoryFull(Client &client, bool base, const LightDirectory &directory)
62 63
{
	if (!directory.IsRoot()) {
64
		PrintDirectoryURI(client, base, directory);
65 66 67

		if (directory.mtime > 0)
			time_print(client, "Last-Modified", directory.mtime);
68 69 70 71 72
	}

	return true;
}

73
static void
74
print_playlist_in_directory(Client &client, bool base,
75 76 77
			    const char *directory,
			    const char *name_utf8)
{
78 79 80
	if (base || directory == nullptr)
		client_printf(client, "playlist: %s\n",
			      ApplyBaseFlag(name_utf8, base));
81 82 83 84
	else
		client_printf(client, "playlist: %s/%s\n",
			      directory, name_utf8);
}
85

86
static void
87
print_playlist_in_directory(Client &client, bool base,
88
			    const LightDirectory *directory,
89 90
			    const char *name_utf8)
{
91
	if (base || directory == nullptr || directory->IsRoot())
92 93 94
		client_printf(client, "playlist: %s\n", name_utf8);
	else
		client_printf(client, "playlist: %s/%s\n",
95
			      directory->GetPath(), name_utf8);
96 97
}

98
static bool
99
PrintSongBrief(Client &client, bool base, const LightSong &song)
100
{
101
	song_print_uri(client, song, base);
102

103
	if (song.tag->has_playlist)
104
		/* this song file has an embedded CUE sheet */
105 106
		print_playlist_in_directory(client, base,
					    song.directory, song.uri);
107

108
	return true;
109 110
}

111
static bool
112
PrintSongFull(Client &client, bool base, const LightSong &song)
113
{
114
	song_print_info(client, song, base);
115

116
	if (song.tag->has_playlist)
117
		/* this song file has an embedded CUE sheet */
118 119
		print_playlist_in_directory(client, base,
					    song.directory, song.uri);
120 121

	return true;
122 123
}

124
static bool
125
PrintPlaylistBrief(Client &client, bool base,
126
		   const PlaylistInfo &playlist,
127
		   const LightDirectory &directory)
128
{
129 130
	print_playlist_in_directory(client, base,
				    &directory, playlist.name.c_str());
131 132 133 134
	return true;
}

static bool
135
PrintPlaylistFull(Client &client, bool base,
136
		  const PlaylistInfo &playlist,
137
		  const LightDirectory &directory)
138
{
139 140
	print_playlist_in_directory(client, base,
				    &directory, playlist.name.c_str());
141

142 143
	if (playlist.mtime > 0)
		time_print(client, "Last-Modified", playlist.mtime);
144 145 146 147

	return true;
}

148
bool
149
db_selection_print(Client &client, const DatabaseSelection &selection,
150
		   bool full, bool base, Error &error)
151
{
152
	const Database *db = client.GetDatabase(error);
153 154 155
	if (db == nullptr)
		return false;

156
	using namespace std::placeholders;
157
	const auto d = selection.filter == nullptr
158
		? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief,
159
			    std::ref(client), base, _1)
160
		: VisitDirectory();
161
	const auto s = std::bind(full ? PrintSongFull : PrintSongBrief,
162
				 std::ref(client), base, _1);
163
	const auto p = selection.filter == nullptr
164
		? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
165
			    std::ref(client), base, _1, _2)
166
		: VisitPlaylist();
167

168
	return db->Visit(selection, d, s, p, error);
169 170
}

171
struct SearchStats {
172
	unsigned numberOfSongs;
173 174 175
	unsigned long playTime;
};

176 177
static void
printSearchStats(Client &client, const SearchStats &stats)
178
{
179 180 181 182
	client_printf(client,
		      "songs: %u\n"
		      "playtime: %lu\n",
		      stats.numberOfSongs, stats.playTime);
183 184
}

185
static bool
186
stats_visitor_song(SearchStats &stats, const LightSong &song)
187
{
188
	stats.numberOfSongs++;
189
	stats.playTime += song.GetDuration();
190

191
	return true;
192 193
}

194
bool
195
searchStatsForSongsIn(Client &client, const char *name,
196
		      const SongFilter *filter,
197
		      Error &error)
198
{
199
	const Database *db = client.GetDatabase(error);
200 201 202
	if (db == nullptr)
		return false;

203
	const DatabaseSelection selection(name, true, filter);
204

205
	SearchStats stats;
206 207 208
	stats.numberOfSongs = 0;
	stats.playTime = 0;

209
	using namespace std::placeholders;
210
	const auto f = std::bind(stats_visitor_song, std::ref(stats),
211
				 _1);
212
	if (!db->Visit(selection, f, error))
213
		return false;
214

215
	printSearchStats(client, stats);
216
	return true;
217 218
}

219
static bool
220
PrintSongURIVisitor(Client &client, const LightSong &song)
221
{
222
	song_print_uri(client, song);
223

224
	return true;
225 226
}

227
static bool
228
PrintUniqueTag(Client &client, TagType tag_type,
229
	       const Tag &tag)
230
{
231 232
	const char *value = tag.GetValue(tag_type);
	assert(value != nullptr);
233
	client_printf(client, "%s: %s\n", tag_item_names[tag_type], value);
234 235 236 237 238 239 240 241

	for (unsigned i = 0, n = tag.num_items; i < n; i++) {
		const TagItem &item = *tag.items[i];
		if (item.type != tag_type)
			client_printf(client, "%s: %s\n",
				      tag_item_names[item.type], item.value);
	}

242
	return true;
243 244
}

245
bool
246
listAllUniqueTags(Client &client, unsigned type, uint32_t group_mask,
247
		  const SongFilter *filter,
248
		  Error &error)
249
{
250
	const Database *db = client.GetDatabase(error);
251 252 253
	if (db == nullptr)
		return false;

254
	const DatabaseSelection selection("", true, filter);
255

256 257
	if (type == LOCATE_TAG_FILE_TYPE) {
		using namespace std::placeholders;
258 259
		const auto f = std::bind(PrintSongURIVisitor,
					 std::ref(client), _1);
260
		return db->Visit(selection, f, error);
261
	} else {
262 263
		assert(type < TAG_NUM_OF_ITEM_TYPES);

264
		using namespace std::placeholders;
265
		const auto f = std::bind(PrintUniqueTag, std::ref(client),
266 267
					 (TagType)type, _1);
		return db->VisitUniqueTags(selection, (TagType)type,
268
					   group_mask,
269
					   f, error);
270
	}
271
}