mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-26 22:23:17 -05:00
Refactor adding non library items to the queue
Instead of asking a client to first scan the path into a media_file_info object and afterwards add it to the queue, we now offer a library function to directly add a path. The library-source that can handle the given path translates the path into new queue item(s) and adds them to the queue.
This commit is contained in:
parent
acd5bd3272
commit
60ebac076b
450
src/db.c
450
src/db.c
@ -32,6 +32,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <unictype.h>
|
||||
#include <uninorm.h>
|
||||
#include <unistr.h>
|
||||
#include <sys/mman.h>
|
||||
#include <limits.h>
|
||||
|
||||
@ -641,6 +644,324 @@ unicode_fixup_mfi(struct media_file_info *mfi)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sort_tag_create(char **sort_tag, char *src_tag)
|
||||
{
|
||||
const uint8_t *i_ptr;
|
||||
const uint8_t *n_ptr;
|
||||
const uint8_t *number;
|
||||
uint8_t out[1024];
|
||||
uint8_t *o_ptr;
|
||||
int append_number;
|
||||
ucs4_t puc;
|
||||
int numlen;
|
||||
size_t len;
|
||||
int charlen;
|
||||
|
||||
/* Note: include terminating NUL in string length for u8_normalize */
|
||||
|
||||
if (*sort_tag)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Existing sort tag will be normalized: %s\n", *sort_tag);
|
||||
o_ptr = u8_normalize(UNINORM_NFD, (uint8_t *)*sort_tag, strlen(*sort_tag) + 1, NULL, &len);
|
||||
free(*sort_tag);
|
||||
*sort_tag = (char *)o_ptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!src_tag || ((len = strlen(src_tag)) == 0))
|
||||
{
|
||||
*sort_tag = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set input pointer past article if present
|
||||
if ((strncasecmp(src_tag, "a ", 2) == 0) && (len > 2))
|
||||
i_ptr = (uint8_t *)(src_tag + 2);
|
||||
else if ((strncasecmp(src_tag, "an ", 3) == 0) && (len > 3))
|
||||
i_ptr = (uint8_t *)(src_tag + 3);
|
||||
else if ((strncasecmp(src_tag, "the ", 4) == 0) && (len > 4))
|
||||
i_ptr = (uint8_t *)(src_tag + 4);
|
||||
else
|
||||
i_ptr = (uint8_t *)src_tag;
|
||||
|
||||
// Poor man's natural sort. Makes sure we sort like this: a1, a2, a10, a11, a21, a111
|
||||
// We do this by padding zeroes to (short) numbers. As an alternative we could have
|
||||
// made a proper natural sort algorithm in sqlext.c, but we don't, since we don't
|
||||
// want any risk of hurting response times
|
||||
memset(&out, 0, sizeof(out));
|
||||
o_ptr = (uint8_t *)&out;
|
||||
number = NULL;
|
||||
append_number = 0;
|
||||
|
||||
do
|
||||
{
|
||||
n_ptr = u8_next(&puc, i_ptr);
|
||||
|
||||
if (uc_is_digit(puc))
|
||||
{
|
||||
if (!number) // We have encountered the beginning of a number
|
||||
number = i_ptr;
|
||||
append_number = (n_ptr == NULL); // If last char in string append number now
|
||||
}
|
||||
else
|
||||
{
|
||||
if (number)
|
||||
append_number = 1; // A number has ended so time to append it
|
||||
else
|
||||
{
|
||||
charlen = u8_strmblen(i_ptr);
|
||||
if (charlen >= 0)
|
||||
o_ptr = u8_stpncpy(o_ptr, i_ptr, charlen); // No numbers in sight, just append char
|
||||
}
|
||||
}
|
||||
|
||||
// Break if less than 100 bytes remain (prevent buffer overflow)
|
||||
if (sizeof(out) - u8_strlen(out) < 100)
|
||||
break;
|
||||
|
||||
// Break if number is very large (prevent buffer overflow)
|
||||
if (number && (i_ptr - number > 50))
|
||||
break;
|
||||
|
||||
if (append_number)
|
||||
{
|
||||
numlen = i_ptr - number;
|
||||
if (numlen < 5) // Max pad width
|
||||
{
|
||||
u8_strcpy(o_ptr, (uint8_t *)"00000");
|
||||
o_ptr += (5 - numlen);
|
||||
}
|
||||
o_ptr = u8_stpncpy(o_ptr, number, numlen + u8_strmblen(i_ptr));
|
||||
|
||||
number = NULL;
|
||||
append_number = 0;
|
||||
}
|
||||
|
||||
i_ptr = n_ptr;
|
||||
}
|
||||
while (n_ptr);
|
||||
|
||||
*sort_tag = (char *)u8_normalize(UNINORM_NFD, (uint8_t *)&out, u8_strlen(out) + 1, NULL, &len);
|
||||
}
|
||||
|
||||
void
|
||||
fixup_tags_mfi(struct media_file_info *mfi)
|
||||
{
|
||||
cfg_t *lib;
|
||||
size_t len;
|
||||
char *tag;
|
||||
char *sep = " - ";
|
||||
char *ca;
|
||||
|
||||
if (mfi->genre && (strlen(mfi->genre) == 0))
|
||||
{
|
||||
free(mfi->genre);
|
||||
mfi->genre = NULL;
|
||||
}
|
||||
|
||||
if (mfi->artist && (strlen(mfi->artist) == 0))
|
||||
{
|
||||
free(mfi->artist);
|
||||
mfi->artist = NULL;
|
||||
}
|
||||
|
||||
if (mfi->title && (strlen(mfi->title) == 0))
|
||||
{
|
||||
free(mfi->title);
|
||||
mfi->title = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default to mpeg4 video/audio for unknown file types
|
||||
* in an attempt to allow streaming of DRM-afflicted files
|
||||
*/
|
||||
if (mfi->codectype && strcmp(mfi->codectype, "unkn") == 0)
|
||||
{
|
||||
if (mfi->has_video)
|
||||
{
|
||||
strcpy(mfi->codectype, "mp4v");
|
||||
strcpy(mfi->type, "m4v");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(mfi->codectype, "mp4a");
|
||||
strcpy(mfi->type, "m4a");
|
||||
}
|
||||
}
|
||||
|
||||
if (!mfi->artist)
|
||||
{
|
||||
if (mfi->orchestra && mfi->conductor)
|
||||
{
|
||||
len = strlen(mfi->orchestra) + strlen(sep) + strlen(mfi->conductor);
|
||||
tag = (char *)malloc(len + 1);
|
||||
if (tag)
|
||||
{
|
||||
sprintf(tag,"%s%s%s", mfi->orchestra, sep, mfi->conductor);
|
||||
mfi->artist = tag;
|
||||
}
|
||||
}
|
||||
else if (mfi->orchestra)
|
||||
{
|
||||
mfi->artist = strdup(mfi->orchestra);
|
||||
}
|
||||
else if (mfi->conductor)
|
||||
{
|
||||
mfi->artist = strdup(mfi->conductor);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle TV shows, try to present prettier metadata */
|
||||
if (mfi->tv_series_name && strlen(mfi->tv_series_name) != 0)
|
||||
{
|
||||
mfi->media_kind = MEDIA_KIND_TVSHOW; /* tv show */
|
||||
|
||||
/* Default to artist = series_name */
|
||||
if (mfi->artist && strlen(mfi->artist) == 0)
|
||||
{
|
||||
free(mfi->artist);
|
||||
mfi->artist = NULL;
|
||||
}
|
||||
|
||||
if (!mfi->artist)
|
||||
mfi->artist = strdup(mfi->tv_series_name);
|
||||
|
||||
/* Default to album = "<series_name>, Season <season_num>" */
|
||||
if (mfi->album && strlen(mfi->album) == 0)
|
||||
{
|
||||
free(mfi->album);
|
||||
mfi->album = NULL;
|
||||
}
|
||||
|
||||
if (!mfi->album)
|
||||
{
|
||||
len = snprintf(NULL, 0, "%s, Season %u", mfi->tv_series_name, mfi->tv_season_num);
|
||||
|
||||
mfi->album = (char *)malloc(len + 1);
|
||||
if (mfi->album)
|
||||
sprintf(mfi->album, "%s, Season %u", mfi->tv_series_name, mfi->tv_season_num);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the 4 top-tags are filled */
|
||||
if (!mfi->artist)
|
||||
mfi->artist = strdup("Unknown artist");
|
||||
if (!mfi->album)
|
||||
mfi->album = strdup("Unknown album");
|
||||
if (!mfi->genre)
|
||||
mfi->genre = strdup("Unknown genre");
|
||||
if (!mfi->title)
|
||||
{
|
||||
/* fname is left untouched by unicode_fixup_mfi() for
|
||||
* obvious reasons, so ensure it is proper UTF-8
|
||||
*/
|
||||
mfi->title = unicode_fixup_string(mfi->fname, "ascii");
|
||||
if (mfi->title == mfi->fname)
|
||||
mfi->title = strdup(mfi->fname);
|
||||
}
|
||||
|
||||
/* Ensure sort tags are filled, manipulated and normalized */
|
||||
sort_tag_create(&mfi->artist_sort, mfi->artist);
|
||||
sort_tag_create(&mfi->album_sort, mfi->album);
|
||||
sort_tag_create(&mfi->title_sort, mfi->title);
|
||||
|
||||
/* We need to set album_artist according to media type and config */
|
||||
if (mfi->compilation) /* Compilation */
|
||||
{
|
||||
lib = cfg_getsec(cfg, "library");
|
||||
ca = cfg_getstr(lib, "compilation_artist");
|
||||
if (ca && mfi->album_artist)
|
||||
{
|
||||
free(mfi->album_artist);
|
||||
mfi->album_artist = strdup(ca);
|
||||
}
|
||||
else if (ca && !mfi->album_artist)
|
||||
{
|
||||
mfi->album_artist = strdup(ca);
|
||||
}
|
||||
else if (!ca && !mfi->album_artist)
|
||||
{
|
||||
mfi->album_artist = strdup("");
|
||||
mfi->album_artist_sort = strdup("");
|
||||
}
|
||||
}
|
||||
else if (mfi->media_kind == MEDIA_KIND_PODCAST) /* Podcast */
|
||||
{
|
||||
if (mfi->album_artist)
|
||||
free(mfi->album_artist);
|
||||
mfi->album_artist = strdup("");
|
||||
mfi->album_artist_sort = strdup("");
|
||||
}
|
||||
else if (!mfi->album_artist) /* Regular media without album_artist */
|
||||
{
|
||||
mfi->album_artist = strdup(mfi->artist);
|
||||
}
|
||||
|
||||
if (!mfi->album_artist_sort && (strcmp(mfi->album_artist, mfi->artist) == 0))
|
||||
mfi->album_artist_sort = strdup(mfi->artist_sort);
|
||||
else
|
||||
sort_tag_create(&mfi->album_artist_sort, mfi->album_artist);
|
||||
|
||||
/* Composer is not one of our mandatory tags, so take extra care */
|
||||
if (mfi->composer_sort || mfi->composer)
|
||||
sort_tag_create(&mfi->composer_sort, mfi->composer);
|
||||
}
|
||||
|
||||
static void
|
||||
fixup_tags_queue_item(struct db_queue_item *queue_item)
|
||||
{
|
||||
if (queue_item->genre && (strlen(queue_item->genre) == 0))
|
||||
{
|
||||
free(queue_item->genre);
|
||||
queue_item->genre = NULL;
|
||||
}
|
||||
|
||||
if (queue_item->artist && (strlen(queue_item->artist) == 0))
|
||||
{
|
||||
free(queue_item->artist);
|
||||
queue_item->artist = NULL;
|
||||
}
|
||||
|
||||
if (queue_item->title && (strlen(queue_item->title) == 0))
|
||||
{
|
||||
free(queue_item->title);
|
||||
queue_item->title = NULL;
|
||||
}
|
||||
|
||||
/* Check the 4 top-tags are filled */
|
||||
if (!queue_item->artist)
|
||||
queue_item->artist = strdup("Unknown artist");
|
||||
if (!queue_item->album)
|
||||
queue_item->album = strdup("Unknown album");
|
||||
if (!queue_item->genre)
|
||||
queue_item->genre = strdup("Unknown genre");
|
||||
if (!queue_item->title)
|
||||
queue_item->title = strdup(queue_item->path);
|
||||
|
||||
/* Ensure sort tags are filled, manipulated and normalized */
|
||||
sort_tag_create(&queue_item->artist_sort, queue_item->artist);
|
||||
sort_tag_create(&queue_item->album_sort, queue_item->album);
|
||||
|
||||
/* We need to set album_artist according to media type and config */
|
||||
if (queue_item->media_kind == MEDIA_KIND_PODCAST) /* Podcast */
|
||||
{
|
||||
if (queue_item->album_artist)
|
||||
free(queue_item->album_artist);
|
||||
queue_item->album_artist = strdup("");
|
||||
queue_item->album_artist_sort = strdup("");
|
||||
}
|
||||
else if (!queue_item->album_artist) /* Regular media without album_artist */
|
||||
{
|
||||
queue_item->album_artist = strdup(queue_item->artist);
|
||||
}
|
||||
|
||||
if (!queue_item->album_artist_sort && (strcmp(queue_item->album_artist, queue_item->artist) == 0))
|
||||
queue_item->album_artist_sort = strdup(queue_item->artist_sort);
|
||||
else
|
||||
sort_tag_create(&queue_item->album_artist_sort, queue_item->album_artist);
|
||||
}
|
||||
|
||||
|
||||
/* Unlock notification support */
|
||||
static void
|
||||
@ -4219,6 +4540,38 @@ queue_add_file(struct db_media_file_info *dbmfi, int pos, int shuffle_pos, int q
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
static int
|
||||
queue_add_item(struct db_queue_item *item, int pos, int shuffle_pos, int queue_version)
|
||||
{
|
||||
#define Q_TMPL "INSERT INTO queue " \
|
||||
"(id, file_id, song_length, data_kind, media_kind, " \
|
||||
"pos, shuffle_pos, path, virtual_path, title, " \
|
||||
"artist, album_artist, album, genre, songalbumid, " \
|
||||
"time_modified, artist_sort, album_sort, album_artist_sort, year, " \
|
||||
"track, disc, queue_version)" \
|
||||
"VALUES" \
|
||||
"(NULL, %d, %d, %d, %d, " \
|
||||
"%d, %d, %Q, %Q, %Q, " \
|
||||
"%Q, %Q, %Q, %Q, %" PRIi64 ", " \
|
||||
"%d, %Q, %Q, %Q, %d, " \
|
||||
"%d, %d, %d);"
|
||||
|
||||
char *query;
|
||||
int ret;
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL,
|
||||
item->file_id, item->song_length, item->data_kind, item->media_kind,
|
||||
pos, pos, item->path, item->virtual_path, item->title,
|
||||
item->artist, item->album_artist, item->album, item->genre, item->songalbumid,
|
||||
item->time_modified, item->artist_sort, item->album_sort, item->album_artist_sort, item->year,
|
||||
item->track, item->disc, queue_version);
|
||||
ret = db_query_run(query, 1, 0);
|
||||
|
||||
return ret;
|
||||
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
int
|
||||
db_queue_update_item(struct db_queue_item *qi)
|
||||
{
|
||||
@ -4337,6 +4690,46 @@ db_queue_add_by_queryafteritemid(struct query_params *qp, uint32_t item_id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
db_queue_add_start(struct db_queue_add_info *queue_add_info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
memset(queue_add_info, 0, sizeof(struct db_queue_add_info));
|
||||
queue_add_info->queue_version = queue_transaction_begin();
|
||||
|
||||
queue_add_info->pos = db_queue_get_count();
|
||||
if (queue_add_info->pos < 0)
|
||||
{
|
||||
ret = -1;
|
||||
queue_transaction_end(ret, queue_add_info->queue_version);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
db_queue_add_end(struct db_queue_add_info *queue_add_info, int ret)
|
||||
{
|
||||
queue_transaction_end(ret, queue_add_info->queue_version);
|
||||
}
|
||||
|
||||
int
|
||||
db_queue_add_item(struct db_queue_add_info *queue_add_info, struct db_queue_item *item)
|
||||
{
|
||||
int ret;
|
||||
|
||||
fixup_tags_queue_item(item);
|
||||
ret = queue_add_item(item, queue_add_info->pos, queue_add_info->pos, queue_add_info->queue_version);
|
||||
if (ret == 0)
|
||||
queue_add_info->pos++;
|
||||
|
||||
return ret;
|
||||
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds the files matching the given query to the queue
|
||||
*
|
||||
@ -4465,63 +4858,6 @@ db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
db_queue_add_item(struct db_queue_item *queue_item, char reshuffle, uint32_t item_id)
|
||||
{
|
||||
#define Q_TMPL "INSERT INTO queue " \
|
||||
"(id, file_id, song_length, data_kind, media_kind, " \
|
||||
"pos, shuffle_pos, path, virtual_path, title, " \
|
||||
"artist, album_artist, album, genre, songalbumid, " \
|
||||
"time_modified, artist_sort, album_sort, album_artist_sort, year, " \
|
||||
"track, disc, queue_version)" \
|
||||
"VALUES" \
|
||||
"(NULL, %d, %d, %d, %d, " \
|
||||
"%d, %d, %Q, %Q, %Q, " \
|
||||
"%Q, %Q, %Q, %Q, %d, " \
|
||||
"%d, %Q, %Q, %Q, %d, " \
|
||||
"%d, %d, %d);"
|
||||
|
||||
int queue_version;
|
||||
char *query;
|
||||
int pos;
|
||||
int new_item_id = 0; // Quell compiler warning about uninitialized use of new_item_id
|
||||
int ret;
|
||||
|
||||
queue_version = queue_transaction_begin();
|
||||
|
||||
pos = db_queue_get_count();
|
||||
if (pos < 0)
|
||||
{
|
||||
ret = -1;
|
||||
goto end_transaction;
|
||||
}
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL,
|
||||
queue_item->file_id, queue_item->song_length, queue_item->data_kind, queue_item->media_kind,
|
||||
pos, pos, queue_item->path, queue_item->virtual_path, queue_item->title,
|
||||
queue_item->artist, queue_item->album_artist, queue_item->album, queue_item->genre, queue_item->songalbumid,
|
||||
queue_item->time_modified, queue_item->artist_sort, queue_item->album_sort, queue_item->album_artist_sort, queue_item->year,
|
||||
queue_item->track, queue_item->disc, queue_version);
|
||||
ret = db_query_run(query, 1, 0);
|
||||
if (ret < 0)
|
||||
goto end_transaction;
|
||||
|
||||
new_item_id = (int) sqlite3_last_insert_rowid(hdl);
|
||||
|
||||
// Reshuffle after adding new items
|
||||
if (reshuffle)
|
||||
{
|
||||
ret = queue_reshuffle(item_id, queue_version);
|
||||
}
|
||||
|
||||
end_transaction:
|
||||
queue_transaction_end(ret, queue_version);
|
||||
|
||||
return (ret == 0) ? new_item_id : ret;
|
||||
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
static int
|
||||
queue_enum_start(struct query_params *qp)
|
||||
{
|
||||
|
17
src/db.h
17
src/db.h
@ -454,6 +454,12 @@ struct db_queue_item
|
||||
uint32_t queue_version;
|
||||
};
|
||||
|
||||
struct db_queue_add_info
|
||||
{
|
||||
int queue_version;
|
||||
int pos;
|
||||
};
|
||||
|
||||
char *
|
||||
db_escape_string(const char *str); // TODO Remove this, use db_mprintf instead
|
||||
|
||||
@ -484,6 +490,9 @@ free_queue_item(struct db_queue_item *queue_item, int content_only);
|
||||
void
|
||||
unicode_fixup_mfi(struct media_file_info *mfi);
|
||||
|
||||
void
|
||||
fixup_tags_mfi(struct media_file_info *mfi);
|
||||
|
||||
/* Maintenance and DB hygiene */
|
||||
void
|
||||
db_hook_post_scan(void);
|
||||
@ -764,7 +773,13 @@ int
|
||||
db_queue_add_by_fileid(int id, char reshuffle, uint32_t item_id);
|
||||
|
||||
int
|
||||
db_queue_add_item(struct db_queue_item *queue_item, char reshuffle, uint32_t item_id);
|
||||
db_queue_add_start(struct db_queue_add_info *queue_add_info);
|
||||
|
||||
void
|
||||
db_queue_add_end(struct db_queue_add_info *queue_add_info, int ret);
|
||||
|
||||
int
|
||||
db_queue_add_item(struct db_queue_add_info *queue_add_info, struct db_queue_item *item);
|
||||
|
||||
int
|
||||
db_queue_enum_start(struct query_params *qp);
|
||||
|
337
src/library.c
337
src/library.c
@ -111,270 +111,6 @@ handle_deferred_update_notifications(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
sort_tag_create(char **sort_tag, char *src_tag)
|
||||
{
|
||||
const uint8_t *i_ptr;
|
||||
const uint8_t *n_ptr;
|
||||
const uint8_t *number;
|
||||
uint8_t out[1024];
|
||||
uint8_t *o_ptr;
|
||||
int append_number;
|
||||
ucs4_t puc;
|
||||
int numlen;
|
||||
size_t len;
|
||||
int charlen;
|
||||
|
||||
/* Note: include terminating NUL in string length for u8_normalize */
|
||||
|
||||
if (*sort_tag)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Existing sort tag will be normalized: %s\n", *sort_tag);
|
||||
o_ptr = u8_normalize(UNINORM_NFD, (uint8_t *)*sort_tag, strlen(*sort_tag) + 1, NULL, &len);
|
||||
free(*sort_tag);
|
||||
*sort_tag = (char *)o_ptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!src_tag || ((len = strlen(src_tag)) == 0))
|
||||
{
|
||||
*sort_tag = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set input pointer past article if present
|
||||
if ((strncasecmp(src_tag, "a ", 2) == 0) && (len > 2))
|
||||
i_ptr = (uint8_t *)(src_tag + 2);
|
||||
else if ((strncasecmp(src_tag, "an ", 3) == 0) && (len > 3))
|
||||
i_ptr = (uint8_t *)(src_tag + 3);
|
||||
else if ((strncasecmp(src_tag, "the ", 4) == 0) && (len > 4))
|
||||
i_ptr = (uint8_t *)(src_tag + 4);
|
||||
else
|
||||
i_ptr = (uint8_t *)src_tag;
|
||||
|
||||
// Poor man's natural sort. Makes sure we sort like this: a1, a2, a10, a11, a21, a111
|
||||
// We do this by padding zeroes to (short) numbers. As an alternative we could have
|
||||
// made a proper natural sort algorithm in sqlext.c, but we don't, since we don't
|
||||
// want any risk of hurting response times
|
||||
memset(&out, 0, sizeof(out));
|
||||
o_ptr = (uint8_t *)&out;
|
||||
number = NULL;
|
||||
append_number = 0;
|
||||
|
||||
do
|
||||
{
|
||||
n_ptr = u8_next(&puc, i_ptr);
|
||||
|
||||
if (uc_is_digit(puc))
|
||||
{
|
||||
if (!number) // We have encountered the beginning of a number
|
||||
number = i_ptr;
|
||||
append_number = (n_ptr == NULL); // If last char in string append number now
|
||||
}
|
||||
else
|
||||
{
|
||||
if (number)
|
||||
append_number = 1; // A number has ended so time to append it
|
||||
else
|
||||
{
|
||||
charlen = u8_strmblen(i_ptr);
|
||||
if (charlen >= 0)
|
||||
o_ptr = u8_stpncpy(o_ptr, i_ptr, charlen); // No numbers in sight, just append char
|
||||
}
|
||||
}
|
||||
|
||||
// Break if less than 100 bytes remain (prevent buffer overflow)
|
||||
if (sizeof(out) - u8_strlen(out) < 100)
|
||||
break;
|
||||
|
||||
// Break if number is very large (prevent buffer overflow)
|
||||
if (number && (i_ptr - number > 50))
|
||||
break;
|
||||
|
||||
if (append_number)
|
||||
{
|
||||
numlen = i_ptr - number;
|
||||
if (numlen < 5) // Max pad width
|
||||
{
|
||||
u8_strcpy(o_ptr, (uint8_t *)"00000");
|
||||
o_ptr += (5 - numlen);
|
||||
}
|
||||
o_ptr = u8_stpncpy(o_ptr, number, numlen + u8_strmblen(i_ptr));
|
||||
|
||||
number = NULL;
|
||||
append_number = 0;
|
||||
}
|
||||
|
||||
i_ptr = n_ptr;
|
||||
}
|
||||
while (n_ptr);
|
||||
|
||||
*sort_tag = (char *)u8_normalize(UNINORM_NFD, (uint8_t *)&out, u8_strlen(out) + 1, NULL, &len);
|
||||
}
|
||||
|
||||
static void
|
||||
fixup_tags(struct media_file_info *mfi)
|
||||
{
|
||||
cfg_t *lib;
|
||||
size_t len;
|
||||
char *tag;
|
||||
char *sep = " - ";
|
||||
char *ca;
|
||||
|
||||
if (mfi->genre && (strlen(mfi->genre) == 0))
|
||||
{
|
||||
free(mfi->genre);
|
||||
mfi->genre = NULL;
|
||||
}
|
||||
|
||||
if (mfi->artist && (strlen(mfi->artist) == 0))
|
||||
{
|
||||
free(mfi->artist);
|
||||
mfi->artist = NULL;
|
||||
}
|
||||
|
||||
if (mfi->title && (strlen(mfi->title) == 0))
|
||||
{
|
||||
free(mfi->title);
|
||||
mfi->title = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default to mpeg4 video/audio for unknown file types
|
||||
* in an attempt to allow streaming of DRM-afflicted files
|
||||
*/
|
||||
if (mfi->codectype && strcmp(mfi->codectype, "unkn") == 0)
|
||||
{
|
||||
if (mfi->has_video)
|
||||
{
|
||||
strcpy(mfi->codectype, "mp4v");
|
||||
strcpy(mfi->type, "m4v");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(mfi->codectype, "mp4a");
|
||||
strcpy(mfi->type, "m4a");
|
||||
}
|
||||
}
|
||||
|
||||
if (!mfi->artist)
|
||||
{
|
||||
if (mfi->orchestra && mfi->conductor)
|
||||
{
|
||||
len = strlen(mfi->orchestra) + strlen(sep) + strlen(mfi->conductor);
|
||||
tag = (char *)malloc(len + 1);
|
||||
if (tag)
|
||||
{
|
||||
sprintf(tag,"%s%s%s", mfi->orchestra, sep, mfi->conductor);
|
||||
mfi->artist = tag;
|
||||
}
|
||||
}
|
||||
else if (mfi->orchestra)
|
||||
{
|
||||
mfi->artist = strdup(mfi->orchestra);
|
||||
}
|
||||
else if (mfi->conductor)
|
||||
{
|
||||
mfi->artist = strdup(mfi->conductor);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle TV shows, try to present prettier metadata */
|
||||
if (mfi->tv_series_name && strlen(mfi->tv_series_name) != 0)
|
||||
{
|
||||
mfi->media_kind = MEDIA_KIND_TVSHOW; /* tv show */
|
||||
|
||||
/* Default to artist = series_name */
|
||||
if (mfi->artist && strlen(mfi->artist) == 0)
|
||||
{
|
||||
free(mfi->artist);
|
||||
mfi->artist = NULL;
|
||||
}
|
||||
|
||||
if (!mfi->artist)
|
||||
mfi->artist = strdup(mfi->tv_series_name);
|
||||
|
||||
/* Default to album = "<series_name>, Season <season_num>" */
|
||||
if (mfi->album && strlen(mfi->album) == 0)
|
||||
{
|
||||
free(mfi->album);
|
||||
mfi->album = NULL;
|
||||
}
|
||||
|
||||
if (!mfi->album)
|
||||
{
|
||||
len = snprintf(NULL, 0, "%s, Season %u", mfi->tv_series_name, mfi->tv_season_num);
|
||||
|
||||
mfi->album = (char *)malloc(len + 1);
|
||||
if (mfi->album)
|
||||
sprintf(mfi->album, "%s, Season %u", mfi->tv_series_name, mfi->tv_season_num);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the 4 top-tags are filled */
|
||||
if (!mfi->artist)
|
||||
mfi->artist = strdup("Unknown artist");
|
||||
if (!mfi->album)
|
||||
mfi->album = strdup("Unknown album");
|
||||
if (!mfi->genre)
|
||||
mfi->genre = strdup("Unknown genre");
|
||||
if (!mfi->title)
|
||||
{
|
||||
/* fname is left untouched by unicode_fixup_mfi() for
|
||||
* obvious reasons, so ensure it is proper UTF-8
|
||||
*/
|
||||
mfi->title = unicode_fixup_string(mfi->fname, "ascii");
|
||||
if (mfi->title == mfi->fname)
|
||||
mfi->title = strdup(mfi->fname);
|
||||
}
|
||||
|
||||
/* Ensure sort tags are filled, manipulated and normalized */
|
||||
sort_tag_create(&mfi->artist_sort, mfi->artist);
|
||||
sort_tag_create(&mfi->album_sort, mfi->album);
|
||||
sort_tag_create(&mfi->title_sort, mfi->title);
|
||||
|
||||
/* We need to set album_artist according to media type and config */
|
||||
if (mfi->compilation) /* Compilation */
|
||||
{
|
||||
lib = cfg_getsec(cfg, "library");
|
||||
ca = cfg_getstr(lib, "compilation_artist");
|
||||
if (ca && mfi->album_artist)
|
||||
{
|
||||
free(mfi->album_artist);
|
||||
mfi->album_artist = strdup(ca);
|
||||
}
|
||||
else if (ca && !mfi->album_artist)
|
||||
{
|
||||
mfi->album_artist = strdup(ca);
|
||||
}
|
||||
else if (!ca && !mfi->album_artist)
|
||||
{
|
||||
mfi->album_artist = strdup("");
|
||||
mfi->album_artist_sort = strdup("");
|
||||
}
|
||||
}
|
||||
else if (mfi->media_kind == MEDIA_KIND_PODCAST) /* Podcast */
|
||||
{
|
||||
if (mfi->album_artist)
|
||||
free(mfi->album_artist);
|
||||
mfi->album_artist = strdup("");
|
||||
mfi->album_artist_sort = strdup("");
|
||||
}
|
||||
else if (!mfi->album_artist) /* Regular media without album_artist */
|
||||
{
|
||||
mfi->album_artist = strdup(mfi->artist);
|
||||
}
|
||||
|
||||
if (!mfi->album_artist_sort && (strcmp(mfi->album_artist, mfi->artist) == 0))
|
||||
mfi->album_artist_sort = strdup(mfi->artist_sort);
|
||||
else
|
||||
sort_tag_create(&mfi->album_artist_sort, mfi->album_artist);
|
||||
|
||||
/* Composer is not one of our mandatory tags, so take extra care */
|
||||
if (mfi->composer_sort || mfi->composer)
|
||||
sort_tag_create(&mfi->composer_sort, mfi->composer);
|
||||
}
|
||||
|
||||
void
|
||||
library_add_media(struct media_file_info *mfi)
|
||||
{
|
||||
@ -398,8 +134,7 @@ library_add_media(struct media_file_info *mfi)
|
||||
mfi->media_kind = MEDIA_KIND_MUSIC; /* music */
|
||||
|
||||
unicode_fixup_mfi(mfi);
|
||||
|
||||
fixup_tags(mfi);
|
||||
fixup_tags_mfi(mfi);
|
||||
|
||||
if (mfi->id == 0)
|
||||
db_file_add(mfi);
|
||||
@ -408,45 +143,33 @@ library_add_media(struct media_file_info *mfi)
|
||||
}
|
||||
|
||||
int
|
||||
library_scan_media(const char *path, struct media_file_info *mfi)
|
||||
library_queue_add(const char *path)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_DBG, L_LIB, "Scan metadata for path '%s'\n", path);
|
||||
DPRINTF(E_DBG, L_LIB, "Add items for path '%s' to the queue\n", path);
|
||||
|
||||
ret = LIBRARY_PATH_INVALID;
|
||||
for (i = 0; sources[i] && ret == LIBRARY_PATH_INVALID; i++)
|
||||
{
|
||||
if (sources[i]->disabled || !sources[i]->scan_metadata)
|
||||
if (sources[i]->disabled || !sources[i]->queue_add)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support scan_metadata\n", sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_add\n", sources[i]->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = sources[i]->scan_metadata(path, mfi);
|
||||
ret = sources[i]->queue_add(path);
|
||||
|
||||
if (ret == LIBRARY_OK)
|
||||
DPRINTF(E_DBG, L_LIB, "Got metadata for path '%s' from library source '%s'\n", path, sources[i]->name);
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Items for path '%s' from library source '%s' added to the queue\n", path, sources[i]->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == LIBRARY_OK)
|
||||
{
|
||||
if (!mfi->virtual_path)
|
||||
mfi->virtual_path = strdup(mfi->path);
|
||||
if (!mfi->item_kind)
|
||||
mfi->item_kind = 2; /* music */
|
||||
if (!mfi->media_kind)
|
||||
mfi->media_kind = MEDIA_KIND_MUSIC; /* music */
|
||||
|
||||
unicode_fixup_mfi(mfi);
|
||||
|
||||
fixup_tags(mfi);
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_LOG, L_LIB, "Failed to read metadata for path '%s' (ret=%d)\n", path, ret);
|
||||
}
|
||||
if (ret != LIBRARY_OK)
|
||||
DPRINTF(E_LOG, L_LIB, "Failed to add items for path '%s' to the queue (%d)\n", path, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -519,42 +242,6 @@ library_add_playlist_info(const char *path, const char *title, const char *virtu
|
||||
return plid;
|
||||
}
|
||||
|
||||
int
|
||||
library_add_queue_item(struct media_file_info *mfi)
|
||||
{
|
||||
struct db_queue_item queue_item;
|
||||
|
||||
memset(&queue_item, 0, sizeof(struct db_queue_item));
|
||||
|
||||
if (mfi->id)
|
||||
queue_item.file_id = mfi->id;
|
||||
else
|
||||
queue_item.file_id = 9999999;
|
||||
|
||||
queue_item.title = mfi->title;
|
||||
queue_item.artist = mfi->artist;
|
||||
queue_item.album_artist = mfi->album_artist;
|
||||
queue_item.album = mfi->album;
|
||||
queue_item.genre = mfi->genre;
|
||||
queue_item.artist_sort = mfi->artist_sort;
|
||||
queue_item.album_artist_sort = mfi->album_artist_sort;
|
||||
queue_item.album_sort = mfi->album_sort;
|
||||
queue_item.path = mfi->path;
|
||||
queue_item.virtual_path = mfi->virtual_path;
|
||||
queue_item.data_kind = mfi->data_kind;
|
||||
queue_item.media_kind = mfi->media_kind;
|
||||
queue_item.song_length = mfi->song_length;
|
||||
queue_item.seek = mfi->seek;
|
||||
queue_item.songalbumid = mfi->songalbumid;
|
||||
queue_item.time_modified = mfi->time_modified;
|
||||
queue_item.year = mfi->year;
|
||||
queue_item.track = mfi->track;
|
||||
queue_item.disc = mfi->disc;
|
||||
//queue_item.artwork_url
|
||||
|
||||
return db_queue_add_item(&queue_item, 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
purge_cruft(time_t start)
|
||||
{
|
||||
|
@ -66,11 +66,6 @@ struct library_source
|
||||
*/
|
||||
int (*fullrescan)(void);
|
||||
|
||||
/*
|
||||
* Scans metadata for the media file with the given path into the given mfi
|
||||
*/
|
||||
int (*scan_metadata)(const char *path, struct media_file_info *mfi);
|
||||
|
||||
/*
|
||||
* Save queue as a new playlist under the given virtual path
|
||||
*/
|
||||
@ -85,8 +80,12 @@ struct library_source
|
||||
* Save queue as a new playlist under the given virtual path
|
||||
*/
|
||||
int (*queue_save)(const char *virtual_path);
|
||||
};
|
||||
|
||||
/*
|
||||
* Add item for the given path to the current queue
|
||||
*/
|
||||
int (*queue_add)(const char *path);
|
||||
};
|
||||
|
||||
void
|
||||
library_add_media(struct media_file_info *mfi);
|
||||
@ -95,10 +94,7 @@ int
|
||||
library_add_playlist_info(const char *path, const char *title, const char *virtual_path, enum pl_type type, int parent_pl_id, int dir_id);
|
||||
|
||||
int
|
||||
library_scan_media(const char *path, struct media_file_info *mfi);
|
||||
|
||||
int
|
||||
library_add_queue_item(struct media_file_info *mfi);
|
||||
library_queue_add(const char *path);
|
||||
|
||||
void
|
||||
library_rescan();
|
||||
|
@ -1643,39 +1643,102 @@ filescanner_fullrescan()
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scan_metadata(const char *path, struct media_file_info *mfi)
|
||||
static void
|
||||
map_media_file_to_queue_item(struct db_queue_item *queue_item, struct media_file_info *mfi)
|
||||
{
|
||||
memset(queue_item, 0, sizeof(struct db_queue_item));
|
||||
|
||||
if (mfi->id)
|
||||
queue_item->file_id = mfi->id;
|
||||
else
|
||||
queue_item->file_id = 9999999;
|
||||
|
||||
queue_item->title = safe_strdup(mfi->title);
|
||||
queue_item->artist = safe_strdup(mfi->artist);
|
||||
queue_item->album_artist = safe_strdup(mfi->album_artist);
|
||||
queue_item->album = safe_strdup(mfi->album);
|
||||
queue_item->genre = safe_strdup(mfi->genre);
|
||||
queue_item->artist_sort = safe_strdup(mfi->artist_sort);
|
||||
queue_item->album_artist_sort = safe_strdup(mfi->album_artist_sort);
|
||||
queue_item->album_sort = safe_strdup(mfi->album_sort);
|
||||
queue_item->path = safe_strdup(mfi->path);
|
||||
queue_item->virtual_path = safe_strdup(mfi->virtual_path);
|
||||
queue_item->data_kind = mfi->data_kind;
|
||||
queue_item->media_kind = mfi->media_kind;
|
||||
queue_item->song_length = mfi->song_length;
|
||||
queue_item->seek = mfi->seek;
|
||||
queue_item->songalbumid = mfi->songalbumid;
|
||||
queue_item->time_modified = mfi->time_modified;
|
||||
queue_item->year = mfi->year;
|
||||
queue_item->track = mfi->track;
|
||||
queue_item->disc = mfi->disc;
|
||||
//queue_item->artwork_url
|
||||
}
|
||||
|
||||
static int
|
||||
queue_add_stream(const char *path)
|
||||
{
|
||||
struct media_file_info mfi;
|
||||
char *pos;
|
||||
struct db_queue_item item;
|
||||
struct db_queue_add_info queue_add_info;
|
||||
int ret;
|
||||
|
||||
if (strncasecmp(path, "http://", strlen("http://")) == 0)
|
||||
memset(&mfi, 0, sizeof(struct media_file_info));
|
||||
mfi.path = strdup(path);
|
||||
mfi.virtual_path = safe_asprintf("/%s", mfi.path);
|
||||
|
||||
pos = strchr(path, '#');
|
||||
if (pos)
|
||||
mfi.fname = strdup(pos+1);
|
||||
else
|
||||
mfi.fname = strdup(filename_from_path(mfi.path));
|
||||
|
||||
mfi.data_kind = DATA_KIND_HTTP;
|
||||
mfi.directory_id = DIR_HTTP;
|
||||
|
||||
ret = scan_metadata_ffmpeg(path, &mfi);
|
||||
if (ret < 0)
|
||||
{
|
||||
memset(mfi, 0, sizeof(struct media_file_info));
|
||||
mfi->path = strdup(path);
|
||||
mfi->virtual_path = safe_asprintf("/%s", mfi->path);
|
||||
DPRINTF(E_LOG, L_SCAN, "Playlist URL '%s' is unavailable for probe/metadata, assuming MP3 encoding\n", path);
|
||||
mfi.type = strdup("mp3");
|
||||
mfi.codectype = strdup("mpeg");
|
||||
mfi.description = strdup("MPEG audio file");
|
||||
}
|
||||
|
||||
pos = strchr(path, '#');
|
||||
if (pos)
|
||||
mfi->fname = strdup(pos+1);
|
||||
else
|
||||
mfi->fname = strdup(filename_from_path(mfi->path));
|
||||
if (!mfi.title)
|
||||
mfi.title = strdup(mfi.fname);
|
||||
|
||||
mfi->data_kind = DATA_KIND_HTTP;
|
||||
mfi->directory_id = DIR_HTTP;
|
||||
if (!mfi.virtual_path)
|
||||
mfi.virtual_path = strdup(mfi.path);
|
||||
if (!mfi.item_kind)
|
||||
mfi.item_kind = 2; /* music */
|
||||
if (!mfi.media_kind)
|
||||
mfi.media_kind = MEDIA_KIND_MUSIC; /* music */
|
||||
|
||||
ret = scan_metadata_ffmpeg(path, mfi);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SCAN, "Playlist URL '%s' is unavailable for probe/metadata, assuming MP3 encoding\n", path);
|
||||
mfi->type = strdup("mp3");
|
||||
mfi->codectype = strdup("mpeg");
|
||||
mfi->description = strdup("MPEG audio file");
|
||||
}
|
||||
unicode_fixup_mfi(&mfi);
|
||||
|
||||
if (!mfi->title)
|
||||
mfi->title = strdup(mfi->fname);
|
||||
map_media_file_to_queue_item(&item, &mfi);
|
||||
|
||||
ret = db_queue_add_start(&queue_add_info);
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = db_queue_add_item(&queue_add_info, &item);
|
||||
db_queue_add_end(&queue_add_info, ret);
|
||||
}
|
||||
|
||||
free_queue_item(&item, 1);
|
||||
free_mfi(&mfi, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
queue_add(const char *uri)
|
||||
{
|
||||
if (strncasecmp(uri, "http://", strlen("http://")) == 0)
|
||||
{
|
||||
queue_add_stream(uri);
|
||||
return LIBRARY_OK;
|
||||
}
|
||||
|
||||
@ -2052,8 +2115,8 @@ struct library_source filescanner =
|
||||
.initscan = filescanner_initscan,
|
||||
.rescan = filescanner_rescan,
|
||||
.fullrescan = filescanner_fullrescan,
|
||||
.scan_metadata = scan_metadata,
|
||||
.playlist_add = playlist_add,
|
||||
.playlist_remove = playlist_remove,
|
||||
.queue_save = queue_save,
|
||||
.queue_add = queue_add,
|
||||
};
|
||||
|
12
src/mpd.c
12
src/mpd.c
@ -1685,7 +1685,6 @@ mpd_queue_add(char *path, bool exact_match)
|
||||
static int
|
||||
mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, struct mpd_client_ctx *ctx)
|
||||
{
|
||||
struct media_file_info mfi;
|
||||
int ret;
|
||||
|
||||
ret = mpd_queue_add(argv[1], false);
|
||||
@ -1699,15 +1698,12 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, st
|
||||
if (ret == 0)
|
||||
{
|
||||
// Given path is not in the library, check if it is possible to add as a non-library queue item
|
||||
ret = library_scan_media(argv[1], &mfi);
|
||||
ret = library_queue_add(argv[1]);
|
||||
if (ret != LIBRARY_OK)
|
||||
{
|
||||
*errmsg = safe_asprintf("Failed to add song '%s' to playlist (unkown path)", argv[1]);
|
||||
return ACK_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
library_add_queue_item(&mfi);
|
||||
free_mfi(&mfi, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1722,7 +1718,6 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, st
|
||||
static int
|
||||
mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, struct mpd_client_ctx *ctx)
|
||||
{
|
||||
struct media_file_info mfi;
|
||||
int to_pos = -1;
|
||||
int ret;
|
||||
|
||||
@ -1741,15 +1736,12 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg,
|
||||
if (ret == 0)
|
||||
{
|
||||
// Given path is not in the library, directly add it as a new queue item
|
||||
ret = library_scan_media(argv[1], &mfi);
|
||||
ret = library_queue_add(argv[1]);
|
||||
if (ret != LIBRARY_OK)
|
||||
{
|
||||
*errmsg = safe_asprintf("Failed to add song '%s' to playlist (unkown path)", argv[1]);
|
||||
return ACK_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
ret = library_add_queue_item(&mfi);
|
||||
free_mfi(&mfi, 1);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
|
Loading…
x
Reference in New Issue
Block a user