From 201316cd67d374357c1aabc8a4aa2210fe20f1e8 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sat, 26 Dec 2009 12:55:24 +0100 Subject: playlist_queue: resolve relative URIs, database lookup Prepend the playlist's base URI to relative song URIs. Look up songs in the database (if the URI refers to a local song file). Merge existing database metadata with metadata from the playlist plugin. --- src/playlist_queue.c | 131 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 17 deletions(-) (limited to 'src/playlist_queue.c') diff --git a/src/playlist_queue.c b/src/playlist_queue.c index 76ac64bad..af7444e10 100644 --- a/src/playlist_queue.c +++ b/src/playlist_queue.c @@ -22,43 +22,139 @@ #include "playlist_list.h" #include "playlist_plugin.h" #include "stored_playlist.h" +#include "database.h" #include "mapper.h" #include "song.h" +#include "tag.h" #include "uri.h" #include "ls.h" #include "input_stream.h" +static void +merge_song_metadata(struct song *dest, const struct song *base, + const struct song *add) +{ + dest->tag = base->tag != NULL + ? (add->tag != NULL + ? tag_merge(base->tag, add->tag) + : tag_dup(base->tag)) + : (add->tag != NULL + ? tag_dup(add->tag) + : NULL); + + dest->mtime = base->mtime; + dest->start_ms = add->start_ms; + dest->end_ms = add->end_ms; +} + +static struct song * +apply_song_metadata(struct song *dest, const struct song *src) +{ + struct song *tmp; + + assert(dest != NULL); + assert(src != NULL); + + if (src->tag == NULL && src->start_ms == 0 && src->end_ms == 0) + return dest; + + if (song_in_database(dest)) { + char *path_fs = map_song_fs(dest); + if (path_fs == NULL) + return dest; + + tmp = song_file_new(path_fs, NULL); + merge_song_metadata(tmp, dest, src); + } else { + tmp = song_file_new(dest->uri, NULL); + merge_song_metadata(tmp, dest, src); + song_free(dest); + } + + return tmp; +} + /** - * Determins if it's allowed to add this song to the playlist. For - * safety reasons, we disallow local files. + * Verifies the song, returns NULL if it is unsafe. Translate the + * song to a new song object within the database, if it is a local + * file. The old song object is freed. */ -static inline bool -accept_song(const struct song *song) +static struct song * +check_translate_song(struct song *song, const char *base_uri) { - return !song_is_file(song) && uri_has_scheme(song->uri) && - uri_supported_scheme(song->uri); + struct song *dest; + char *uri; + + if (song_in_database(song)) + /* already ok */ + return song; + + if (uri_has_scheme(song->uri)) { + if (uri_supported_scheme(song->uri)) + /* valid remote song */ + return song; + else { + /* unsupported remote song */ + song_free(song); + return NULL; + } + } + + if (g_path_is_absolute(song->uri)) { + /* local files must be relative to the music + directory */ + song_free(song); + return NULL; + } + + if (base_uri != NULL) + uri = g_build_filename(base_uri, song->uri, NULL); + else + uri = g_strdup(song->uri); + + if (uri_has_scheme(base_uri)) { + dest = song_remote_new(uri); + g_free(uri); + } else { + dest = db_get_song(uri); + g_free(uri); + if (dest == NULL) { + /* not found in database */ + song_free(song); + return dest; + } + } + + dest = apply_song_metadata(dest, song); + song_free(song); + + return dest; } enum playlist_result -playlist_load_into_queue(struct playlist_provider *source, +playlist_load_into_queue(const char *uri, struct playlist_provider *source, struct playlist *dest) { enum playlist_result result; struct song *song; + char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL; while ((song = playlist_plugin_read(source)) != NULL) { - if (!accept_song(song)) { - song_free(song); + song = check_translate_song(song, base_uri); + if (song == NULL) continue; - } result = playlist_append_song(dest, song, NULL); if (result != PLAYLIST_RESULT_SUCCESS) { - song_free(song); + if (!song_in_database(song)) + song_free(song); + g_free(base_uri); return result; } } + g_free(base_uri); + return PLAYLIST_RESULT_SUCCESS; } @@ -93,7 +189,7 @@ playlist_open_remote_into_queue(const char *uri, struct playlist *dest) } } - result = playlist_load_into_queue(playlist, dest); + result = playlist_load_into_queue(uri, playlist, dest); playlist_plugin_close(playlist); if (stream) @@ -103,16 +199,17 @@ playlist_open_remote_into_queue(const char *uri, struct playlist *dest) } static enum playlist_result -playlist_open_path_into_queue(const char *path_fs, struct playlist *dest) +playlist_open_path_into_queue(const char *path_fs, const char *uri, + struct playlist *dest) { struct playlist_provider *playlist; struct input_stream is; enum playlist_result result; if ((playlist = playlist_list_open_uri(path_fs)) != NULL) - result = playlist_load_into_queue(playlist, dest); + result = playlist_load_into_queue(uri, playlist, dest); else if ((playlist = playlist_list_open_path(&is, path_fs)) != NULL) { - result = playlist_load_into_queue(playlist, dest); + result = playlist_load_into_queue(uri, playlist, dest); input_stream_close(&is); } else return PLAYLIST_RESULT_NO_SUCH_LIST; @@ -139,7 +236,7 @@ playlist_open_local_into_queue(const char *uri, struct playlist *dest) return PLAYLIST_RESULT_DISABLED; path_fs = g_build_filename(playlist_directory_fs, uri, NULL); - result = playlist_open_path_into_queue(path_fs, dest); + result = playlist_open_path_into_queue(path_fs, NULL, dest); g_free(path_fs); return result; @@ -160,7 +257,7 @@ playlist_open_local_into_queue2(const char *uri, struct playlist *dest) if (path_fs == NULL) return PLAYLIST_RESULT_NO_SUCH_LIST; - result = playlist_open_path_into_queue(path_fs, dest); + result = playlist_open_path_into_queue(path_fs, uri, dest); g_free(path_fs); return result; -- cgit v1.2.3