mpd/src/db_print.c
Max Kellermann 49b84f9229 db_print: print absolute URI of playlist entries
The protocol mandates that playlist URIs are absolute (i.e. full URI
relative to the music directory, not relative to the parent
directory).  This adds the parameter "directory" to the "playlist"
visitor method.
2011-10-08 14:51:18 +02:00

375 lines
8.3 KiB
C

/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* 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 "db_print.h"
#include "db_selection.h"
#include "db_visitor.h"
#include "locate.h"
#include "directory.h"
#include "database.h"
#include "client.h"
#include "song.h"
#include "song_print.h"
#include "tag.h"
#include "strset.h"
#include <glib.h>
typedef struct _ListCommandItem {
int8_t tagType;
const struct locate_item_list *criteria;
} ListCommandItem;
typedef struct _SearchStats {
const struct locate_item_list *criteria;
int numberOfSongs;
unsigned long playTime;
} SearchStats;
static bool
print_visitor_directory(const struct directory *directory, void *data,
G_GNUC_UNUSED GError **error_r)
{
struct client *client = data;
if (!directory_is_root(directory))
client_printf(client, "directory: %s\n", directory_get_path(directory));
return true;
}
static bool
print_visitor_song(struct song *song, void *data,
G_GNUC_UNUSED GError **error_r)
{
struct client *client = data;
song_print_uri(client, song);
return true;
}
static bool
print_visitor_song_info(struct song *song, void *data,
G_GNUC_UNUSED GError **error_r)
{
struct client *client = data;
song_print_info(client, song);
return true;
}
static void
print_playlist_in_directory(struct client *client,
const struct directory *directory,
const char *name_utf8)
{
if (directory_is_root(directory))
client_printf(client, "playlist: %s\n", name_utf8);
else
client_printf(client, "playlist: %s/%s\n",
directory_get_path(directory), name_utf8);
}
static bool
print_visitor_playlist(const struct playlist_metadata *playlist,
const struct directory *directory, void *ctx,
G_GNUC_UNUSED GError **error_r)
{
struct client *client = ctx;
print_playlist_in_directory(client, directory, playlist->name);
return true;
}
static bool
print_visitor_playlist_info(const struct playlist_metadata *playlist,
const struct directory *directory,
void *ctx, G_GNUC_UNUSED GError **error_r)
{
struct client *client = ctx;
print_playlist_in_directory(client, directory, playlist->name);
#ifndef G_OS_WIN32
struct tm tm;
#endif
char timestamp[32];
time_t t = playlist->mtime;
strftime(timestamp, sizeof(timestamp),
#ifdef G_OS_WIN32
"%Y-%m-%dT%H:%M:%SZ",
gmtime(&t)
#else
"%FT%TZ",
gmtime_r(&t, &tm)
#endif
);
client_printf(client, "Last-Modified: %s\n", timestamp);
return true;
}
static const struct db_visitor print_visitor = {
.directory = print_visitor_directory,
.song = print_visitor_song,
.playlist = print_visitor_playlist,
};
static const struct db_visitor print_info_visitor = {
.directory = print_visitor_directory,
.song = print_visitor_song_info,
.playlist = print_visitor_playlist_info,
};
bool
db_selection_print(struct client *client, const struct db_selection *selection,
bool full, GError **error_r)
{
return db_visit(selection, full ? &print_info_visitor : &print_visitor,
client, error_r);
}
struct search_data {
struct client *client;
const struct locate_item_list *criteria;
};
static bool
search_visitor_song(struct song *song, void *_data,
G_GNUC_UNUSED GError **error_r)
{
struct search_data *data = _data;
if (locate_song_search(song, data->criteria))
song_print_info(data->client, song);
return true;
}
static const struct db_visitor search_visitor = {
.song = search_visitor_song,
};
bool
searchForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria,
GError **error_r)
{
struct locate_item_list *new_list
= locate_item_list_casefold(criteria);
struct search_data data;
data.client = client;
data.criteria = new_list;
bool success = db_walk(name, &search_visitor, &data, error_r);
locate_item_list_free(new_list);
return success;
}
static bool
find_visitor_song(struct song *song, void *_data,
G_GNUC_UNUSED GError **error_r)
{
struct search_data *data = _data;
if (locate_song_match(song, data->criteria))
song_print_info(data->client, song);
return true;
}
static const struct db_visitor find_visitor = {
.song = find_visitor_song,
};
bool
findSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria,
GError **error_r)
{
struct search_data data;
data.client = client;
data.criteria = criteria;
return db_walk(name, &find_visitor, &data, error_r);
}
static void printSearchStats(struct client *client, SearchStats *stats)
{
client_printf(client, "songs: %i\n", stats->numberOfSongs);
client_printf(client, "playtime: %li\n", stats->playTime);
}
static bool
stats_visitor_song(struct song *song, void *data,
G_GNUC_UNUSED GError **error_r)
{
SearchStats *stats = data;
if (locate_song_match(song, stats->criteria)) {
stats->numberOfSongs++;
stats->playTime += song_get_duration(song);
}
return true;
}
static const struct db_visitor stats_visitor = {
.song = stats_visitor_song,
};
bool
searchStatsForSongsIn(struct client *client, const char *name,
const struct locate_item_list *criteria,
GError **error_r)
{
SearchStats stats;
stats.criteria = criteria;
stats.numberOfSongs = 0;
stats.playTime = 0;
if (!db_walk(name, &stats_visitor, &stats, error_r))
return false;
printSearchStats(client, &stats);
return true;
}
bool
printAllIn(struct client *client, const char *uri_utf8, GError **error_r)
{
struct db_selection selection;
db_selection_init(&selection, uri_utf8, true);
return db_selection_print(client, &selection, false, error_r);
}
bool
printInfoForAllIn(struct client *client, const char *uri_utf8,
GError **error_r)
{
struct db_selection selection;
db_selection_init(&selection, uri_utf8, true);
return db_selection_print(client, &selection, true, error_r);
}
static ListCommandItem *
newListCommandItem(int tagType, const struct locate_item_list *criteria)
{
ListCommandItem *item = g_new(ListCommandItem, 1);
item->tagType = tagType;
item->criteria = criteria;
return item;
}
static void freeListCommandItem(ListCommandItem * item)
{
g_free(item);
}
static void
visitTag(struct client *client, struct strset *set,
struct song *song, enum tag_type tagType)
{
struct tag *tag = song->tag;
bool found = false;
if (tagType == LOCATE_TAG_FILE_TYPE) {
song_print_uri(client, song);
return;
}
if (!tag)
return;
for (unsigned i = 0; i < tag->num_items; i++) {
if (tag->items[i]->type == tagType) {
strset_add(set, tag->items[i]->value);
found = true;
}
}
if (!found)
strset_add(set, "");
}
struct list_tags_data {
struct client *client;
ListCommandItem *item;
struct strset *set;
};
static bool
unique_tags_visitor_song(struct song *song, void *_data,
G_GNUC_UNUSED GError **error_r)
{
struct list_tags_data *data = _data;
ListCommandItem *item = data->item;
if (locate_song_match(song, item->criteria))
visitTag(data->client, data->set, song, item->tagType);
return true;
}
static const struct db_visitor unique_tags_visitor = {
.song = unique_tags_visitor_song,
};
bool
listAllUniqueTags(struct client *client, int type,
const struct locate_item_list *criteria,
GError **error_r)
{
ListCommandItem *item = newListCommandItem(type, criteria);
struct list_tags_data data = {
.client = client,
.item = item,
};
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
data.set = strset_new();
}
if (!db_walk("", &unique_tags_visitor, &data, error_r)) {
freeListCommandItem(item);
return false;
}
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
const char *value;
strset_rewind(data.set);
while ((value = strset_next(data.set)) != NULL)
client_printf(client, "%s: %s\n",
tag_item_names[type],
value);
strset_free(data.set);
}
freeListCommandItem(item);
return true;
}