mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 15:45:56 -05:00
Merge branch 'db_refactor1'
This commit is contained in:
commit
333c46318d
@ -122,6 +122,8 @@ FORK_MODULES_CHECK([COMMON], [SQLITE3], [sqlite3 >= 3.5.0],
|
||||
[dnl Check that SQLite3 has the unlock notify API built-in
|
||||
AC_CHECK_FUNC([[sqlite3_unlock_notify]], [],
|
||||
[AC_MSG_ERROR([[SQLite3 was built without unlock notify support]])])
|
||||
dnl Check for sqlite3_expanded_sql (optional)
|
||||
AC_CHECK_FUNCS([sqlite3_expanded_sql])
|
||||
dnl Check that SQLite3 has been built with threadsafe operations
|
||||
AC_MSG_CHECKING([[if SQLite3 was built with threadsafe operations support]])
|
||||
AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <sqlite3.h>
|
||||
|
@ -95,11 +95,13 @@ library {
|
||||
# (changing this setting only takes effect after rescan, see the README)
|
||||
compilations = { "/Compilations" }
|
||||
|
||||
# Compilations usually have many artists, and if you don't want every
|
||||
# artist to be listed when artist browsing in Remote, you can set
|
||||
# a single name which will be used for all music in the compilation dir
|
||||
# Compilations usually have many artists, and sometimes no album artist.
|
||||
# If you don't want every artist to be listed in artist views, you can
|
||||
# set a single name which will be used for all compilation tracks
|
||||
# without an album artist, and for all tracks in the compilation
|
||||
# directories.
|
||||
# (changing this setting only takes effect after rescan, see the README)
|
||||
compilation_artist = "Various artists"
|
||||
compilation_artist = "Various Artists"
|
||||
|
||||
# If your album and artist lists are cluttered, you can choose to hide
|
||||
# albums and artists with only one track. The tracks will still be
|
||||
|
@ -208,6 +208,34 @@ sqlext_daap_songalbumid_xfunc(sqlite3_context *pv, int n, sqlite3_value **ppv)
|
||||
sqlite3_result_int64(pv, result);
|
||||
}
|
||||
|
||||
static void
|
||||
sqlext_daap_no_zero_xfunc(sqlite3_context *pv, int n, sqlite3_value **ppv)
|
||||
{
|
||||
sqlite3_int64 new_value;
|
||||
sqlite3_int64 old_value;
|
||||
|
||||
if (n != 2)
|
||||
{
|
||||
sqlite3_result_error(pv, "daap_no_zero() requires 2 parameters, new_value and old_value", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sqlite3_value_type(ppv[0]) != SQLITE_INTEGER)
|
||||
|| (sqlite3_value_type(ppv[1]) != SQLITE_INTEGER))
|
||||
{
|
||||
sqlite3_result_error(pv, "daap_no_zero() requires 2 integer parameters", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
new_value = sqlite3_value_int64(ppv[0]);
|
||||
old_value = sqlite3_value_int64(ppv[1]);
|
||||
|
||||
if (new_value != 0)
|
||||
sqlite3_result_int64(pv, new_value);
|
||||
else
|
||||
sqlite3_result_int64(pv, old_value);
|
||||
}
|
||||
|
||||
static int
|
||||
sqlext_daap_unicode_xcollation(void *notused, int llen, const void *left, int rlen, const void *right)
|
||||
{
|
||||
@ -259,6 +287,15 @@ sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sqlite3_create_function(db, "daap_no_zero", 2, SQLITE_UTF8, NULL, sqlext_daap_no_zero_xfunc, NULL, NULL);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
if (pzErrMsg)
|
||||
*pzErrMsg = sqlite3_mprintf("Could not create daap_no_zero function: %s\n", sqlite3_errmsg(db));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sqlite3_create_collation(db, "DAAP", SQLITE_UTF8, NULL, sqlext_daap_unicode_xcollation);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
|
108
src/db.h
108
src/db.h
@ -140,12 +140,16 @@ db_data_kind_label(enum data_kind data_kind);
|
||||
|
||||
/* Note that fields marked as integers in the metadata map in filescanner_ffmpeg must be uint32_t here */
|
||||
struct media_file_info {
|
||||
uint32_t id;
|
||||
|
||||
char *path;
|
||||
uint32_t index;
|
||||
char *virtual_path;
|
||||
char *fname;
|
||||
uint32_t directory_id; /* Id of directory */
|
||||
char *title;
|
||||
char *artist;
|
||||
char *album;
|
||||
char *album_artist;
|
||||
char *genre;
|
||||
char *comment;
|
||||
char *type; /* daap.songformat */
|
||||
@ -160,6 +164,7 @@ struct media_file_info {
|
||||
uint32_t song_length;
|
||||
int64_t file_size;
|
||||
uint32_t year; /* TDRC */
|
||||
uint32_t date_released;
|
||||
|
||||
uint32_t track; /* TRCK */
|
||||
uint32_t total_tracks;
|
||||
@ -167,44 +172,44 @@ struct media_file_info {
|
||||
uint32_t disc; /* TPOS */
|
||||
uint32_t total_discs;
|
||||
|
||||
uint32_t bpm; /* TBPM */
|
||||
uint32_t compilation;
|
||||
char artwork;
|
||||
uint32_t rating;
|
||||
|
||||
uint32_t play_count;
|
||||
uint32_t skip_count;
|
||||
uint32_t seek;
|
||||
|
||||
uint32_t data_kind; /* dmap.datakind (asdk) */
|
||||
uint32_t media_kind;
|
||||
uint32_t item_kind; /* song or movie */
|
||||
|
||||
char *description; /* daap.songdescription */
|
||||
|
||||
uint32_t db_timestamp;
|
||||
uint32_t time_added; /* FIXME: time_t */
|
||||
uint32_t time_modified;
|
||||
uint32_t time_played;
|
||||
|
||||
uint32_t play_count;
|
||||
uint32_t seek;
|
||||
uint32_t rating;
|
||||
uint32_t db_timestamp;
|
||||
uint32_t time_skipped;
|
||||
|
||||
uint32_t disabled;
|
||||
uint32_t bpm; /* TBPM */
|
||||
|
||||
uint32_t id;
|
||||
|
||||
char *description; /* daap.songdescription */
|
||||
uint64_t sample_count; //TODO [unused] sample count is never set and therefor always 0
|
||||
char *codectype; /* song.codectype, 4 chars max (32 bits) */
|
||||
|
||||
uint32_t item_kind; /* song or movie */
|
||||
uint32_t data_kind; /* dmap.datakind (asdk) */
|
||||
uint64_t sample_count; //TODO [unused] sample count is never set and therefor always 0
|
||||
uint32_t compilation;
|
||||
char artwork;
|
||||
uint32_t idx;
|
||||
|
||||
/* iTunes 5+ */
|
||||
uint32_t contentrating;
|
||||
uint32_t has_video; /* iTunes 6.0.2 */
|
||||
uint32_t contentrating;/* iTunes 5+ */
|
||||
|
||||
/* iTunes 6.0.2 */
|
||||
uint32_t has_video;
|
||||
uint32_t bits_per_sample;
|
||||
|
||||
uint32_t media_kind;
|
||||
uint32_t tv_episode_sort;
|
||||
uint32_t tv_season_num;
|
||||
char *tv_series_name;
|
||||
char *tv_episode_num_str; /* com.apple.itunes.episode-num-str, used as a unique episode identifier */
|
||||
char *tv_network_name;
|
||||
|
||||
char *album_artist;
|
||||
uint32_t tv_episode_sort;
|
||||
uint32_t tv_season_num;
|
||||
|
||||
int64_t songartistid;
|
||||
int64_t songalbumid;
|
||||
@ -212,16 +217,8 @@ struct media_file_info {
|
||||
char *title_sort;
|
||||
char *artist_sort;
|
||||
char *album_sort;
|
||||
char *composer_sort;
|
||||
char *album_artist_sort;
|
||||
|
||||
char *virtual_path;
|
||||
|
||||
uint32_t directory_id; /* Id of directory */
|
||||
uint32_t date_released;
|
||||
|
||||
uint32_t skip_count;
|
||||
uint32_t time_skipped;
|
||||
char *composer_sort;
|
||||
};
|
||||
|
||||
#define mfi_offsetof(field) offsetof(struct media_file_info, field)
|
||||
@ -308,10 +305,13 @@ struct db_group_info {
|
||||
struct db_media_file_info {
|
||||
char *id;
|
||||
char *path;
|
||||
char *virtual_path;
|
||||
char *fname;
|
||||
char *directory_id;
|
||||
char *title;
|
||||
char *artist;
|
||||
char *album;
|
||||
char *album_artist;
|
||||
char *genre;
|
||||
char *comment;
|
||||
char *type;
|
||||
@ -325,6 +325,7 @@ struct db_media_file_info {
|
||||
char *song_length;
|
||||
char *file_size;
|
||||
char *year;
|
||||
char *date_released;
|
||||
char *track;
|
||||
char *total_tracks;
|
||||
char *disc;
|
||||
@ -334,14 +335,17 @@ struct db_media_file_info {
|
||||
char *artwork;
|
||||
char *rating;
|
||||
char *play_count;
|
||||
char *skip_count;
|
||||
char *seek;
|
||||
char *data_kind;
|
||||
char *media_kind;
|
||||
char *item_kind;
|
||||
char *description;
|
||||
char *db_timestamp;
|
||||
char *time_added;
|
||||
char *time_modified;
|
||||
char *time_played;
|
||||
char *db_timestamp;
|
||||
char *time_skipped;
|
||||
char *disabled;
|
||||
char *sample_count;
|
||||
char *codectype;
|
||||
@ -349,8 +353,6 @@ struct db_media_file_info {
|
||||
char *has_video;
|
||||
char *contentrating;
|
||||
char *bits_per_sample;
|
||||
char *album_artist;
|
||||
char *media_kind;
|
||||
char *tv_episode_sort;
|
||||
char *tv_season_num;
|
||||
char *tv_series_name;
|
||||
@ -361,13 +363,8 @@ struct db_media_file_info {
|
||||
char *title_sort;
|
||||
char *artist_sort;
|
||||
char *album_sort;
|
||||
char *composer_sort;
|
||||
char *album_artist_sort;
|
||||
char *virtual_path;
|
||||
char *directory_id;
|
||||
char *date_released;
|
||||
char *skip_count;
|
||||
char *time_skipped;
|
||||
char *composer_sort;
|
||||
};
|
||||
|
||||
#define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field)
|
||||
@ -425,8 +422,7 @@ struct directory_enum {
|
||||
void *stmt;
|
||||
};
|
||||
|
||||
struct db_queue_item
|
||||
{
|
||||
struct db_queue_item {
|
||||
/* A unique id for this queue item. If the same item appears multiple
|
||||
times in the queue each corresponding queue item has its own id. */
|
||||
uint32_t id;
|
||||
@ -434,18 +430,16 @@ struct db_queue_item
|
||||
/* Id of the file/item in the files database */
|
||||
uint32_t file_id;
|
||||
|
||||
/* Length of the item in ms */
|
||||
uint32_t song_length;
|
||||
uint32_t pos;
|
||||
uint32_t shuffle_pos;
|
||||
|
||||
/* Data type of the item */
|
||||
enum data_kind data_kind;
|
||||
/* Media type of the item */
|
||||
enum media_kind media_kind;
|
||||
|
||||
uint32_t seek;
|
||||
|
||||
uint32_t pos;
|
||||
uint32_t shuffle_pos;
|
||||
/* Length of the item in ms */
|
||||
uint32_t song_length;
|
||||
|
||||
char *path;
|
||||
char *virtual_path;
|
||||
@ -453,7 +447,6 @@ struct db_queue_item
|
||||
char *title;
|
||||
char *artist;
|
||||
char *album_artist;
|
||||
char *composer;
|
||||
char *album;
|
||||
char *genre;
|
||||
|
||||
@ -471,8 +464,15 @@ struct db_queue_item
|
||||
char *artwork_url;
|
||||
|
||||
uint32_t queue_version;
|
||||
|
||||
char *composer;
|
||||
|
||||
/* Not saved in queue table */
|
||||
uint32_t seek;
|
||||
};
|
||||
|
||||
#define qi_offsetof(field) offsetof(struct db_queue_item, field)
|
||||
|
||||
struct db_queue_add_info
|
||||
{
|
||||
int queue_version;
|
||||
@ -510,12 +510,6 @@ free_query_params(struct query_params *qp, int content_only);
|
||||
void
|
||||
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);
|
||||
|
100
src/db_init.c
100
src/db_init.c
@ -36,10 +36,13 @@
|
||||
"CREATE TABLE IF NOT EXISTS files (" \
|
||||
" id INTEGER PRIMARY KEY NOT NULL," \
|
||||
" path VARCHAR(4096) NOT NULL," \
|
||||
" virtual_path VARCHAR(4096) DEFAULT NULL," \
|
||||
" fname VARCHAR(255) NOT NULL," \
|
||||
" directory_id INTEGER DEFAULT 0," \
|
||||
" title VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" artist VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" album VARCHAR(1024) NOT NULL COLLATE DAAP," \
|
||||
" album_artist VARCHAR(1024) NOT NULL COLLATE DAAP," \
|
||||
" genre VARCHAR(255) DEFAULT NULL COLLATE DAAP," \
|
||||
" comment VARCHAR(4096) DEFAULT NULL COLLATE DAAP," \
|
||||
" type VARCHAR(255) DEFAULT NULL COLLATE DAAP," \
|
||||
@ -53,6 +56,7 @@
|
||||
" song_length INTEGER DEFAULT 0," \
|
||||
" file_size INTEGER DEFAULT 0," \
|
||||
" year INTEGER DEFAULT 0," \
|
||||
" date_released INTEGER DEFAULT 0," \
|
||||
" track INTEGER DEFAULT 0," \
|
||||
" total_tracks INTEGER DEFAULT 0," \
|
||||
" disc INTEGER DEFAULT 0," \
|
||||
@ -62,14 +66,17 @@
|
||||
" artwork INTEGER DEFAULT 0," \
|
||||
" rating INTEGER DEFAULT 0," \
|
||||
" play_count INTEGER DEFAULT 0," \
|
||||
" skip_count INTEGER DEFAULT 0," \
|
||||
" seek INTEGER DEFAULT 0," \
|
||||
" data_kind INTEGER DEFAULT 0," \
|
||||
" media_kind INTEGER DEFAULT 0," \
|
||||
" item_kind INTEGER DEFAULT 0," \
|
||||
" description INTEGER DEFAULT 0," \
|
||||
" db_timestamp INTEGER DEFAULT 0," \
|
||||
" time_added INTEGER DEFAULT 0," \
|
||||
" time_modified INTEGER DEFAULT 0," \
|
||||
" time_played INTEGER DEFAULT 0," \
|
||||
" db_timestamp INTEGER DEFAULT 0," \
|
||||
" time_skipped INTEGER DEFAULT 0," \
|
||||
" disabled INTEGER DEFAULT 0," \
|
||||
" sample_count INTEGER DEFAULT 0," \
|
||||
" codectype VARCHAR(5) DEFAULT NULL," \
|
||||
@ -77,25 +84,18 @@
|
||||
" has_video INTEGER DEFAULT 0," \
|
||||
" contentrating INTEGER DEFAULT 0," \
|
||||
" bits_per_sample INTEGER DEFAULT 0," \
|
||||
" album_artist VARCHAR(1024) NOT NULL COLLATE DAAP," \
|
||||
" media_kind INTEGER NOT NULL," \
|
||||
" tv_series_name VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" tv_episode_num_str VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" tv_network_name VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" tv_episode_sort INTEGER NOT NULL," \
|
||||
" tv_season_num INTEGER NOT NULL," \
|
||||
" songartistid INTEGER NOT NULL," \
|
||||
" songalbumid INTEGER NOT NULL," \
|
||||
" songartistid INTEGER DEFAULT 0," \
|
||||
" songalbumid INTEGER DEFAULT 0," \
|
||||
" title_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" album_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" composer_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" virtual_path VARCHAR(4096) DEFAULT NULL," \
|
||||
" directory_id INTEGER DEFAULT 0," \
|
||||
" date_released INTEGER DEFAULT 0," \
|
||||
" skip_count INTEGER DEFAULT 0," \
|
||||
" time_skipped INTEGER DEFAULT 0" \
|
||||
" composer_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP" \
|
||||
");"
|
||||
|
||||
#define T_PL \
|
||||
@ -194,20 +194,6 @@
|
||||
" composer VARCHAR(1024) DEFAULT NULL" \
|
||||
");"
|
||||
|
||||
#define TRG_GROUPS_INSERT_FILES \
|
||||
"CREATE TRIGGER update_groups_new_file AFTER INSERT ON files FOR EACH ROW" \
|
||||
" BEGIN" \
|
||||
" INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (1, NEW.album, NEW.songalbumid);" \
|
||||
" INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (2, NEW.album_artist, NEW.songartistid);" \
|
||||
" END;"
|
||||
|
||||
#define TRG_GROUPS_UPDATE_FILES \
|
||||
"CREATE TRIGGER update_groups_update_file AFTER UPDATE OF songalbumid ON files FOR EACH ROW" \
|
||||
" BEGIN" \
|
||||
" INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (1, NEW.album, NEW.songalbumid);" \
|
||||
" INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (2, NEW.album_artist, NEW.songartistid);" \
|
||||
" END;"
|
||||
|
||||
#define Q_PL1 \
|
||||
"INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \
|
||||
" VALUES(1, 'Library', 0, '1 = 1', 0, '', 0, 0);"
|
||||
@ -278,19 +264,18 @@ static const struct db_init_query db_init_table_queries[] =
|
||||
{ T_DIRECTORIES, "create table directories" },
|
||||
{ T_QUEUE, "create table queue" },
|
||||
|
||||
{ TRG_GROUPS_INSERT_FILES, "create trigger update_groups_new_file" },
|
||||
{ TRG_GROUPS_UPDATE_FILES, "create trigger update_groups_update_file" },
|
||||
|
||||
{ Q_PL1, "create default playlist" },
|
||||
{ Q_PL2, "create default smart playlist 'Music'" },
|
||||
{ Q_PL3, "create default smart playlist 'Movies'" },
|
||||
{ Q_PL4, "create default smart playlist 'TV Shows'" },
|
||||
{ Q_PL5, "create default smart playlist 'Podcasts'" },
|
||||
{ Q_PL6, "create default smart playlist 'Audiobooks'" },
|
||||
|
||||
{ Q_DIR1, "create default root directory '/'" },
|
||||
{ Q_DIR2, "create default base directory '/file:'" },
|
||||
{ Q_DIR3, "create default base directory '/http:'" },
|
||||
{ Q_DIR4, "create default base directory '/spotify:'" },
|
||||
|
||||
{ Q_QUEUE_VERSION, "initialize queue version" },
|
||||
};
|
||||
|
||||
@ -411,6 +396,41 @@ static const struct db_init_query db_init_index_queries[] =
|
||||
{ I_QUEUE_SHUFFLEPOS, "create queue shuffle pos index" },
|
||||
};
|
||||
|
||||
|
||||
/* Triggers must be prefixed with trg_ for db_drop_triggers() to id them */
|
||||
|
||||
#define TRG_FILES_INSERT_SONGIDS \
|
||||
"CREATE TRIGGER trg_files_insert_songids AFTER INSERT ON files FOR EACH ROW" \
|
||||
" BEGIN" \
|
||||
" UPDATE files SET songartistid = daap_songalbumid(LOWER(NEW.album_artist), ''), " \
|
||||
" songalbumid = daap_songalbumid(LOWER(NEW.album_artist), LOWER(NEW.album))" \
|
||||
" WHERE id = NEW.id;" \
|
||||
" END;"
|
||||
|
||||
#define TRG_FILES_UPDATE_SONGIDS \
|
||||
"CREATE TRIGGER trg_files_update_songids AFTER UPDATE OF album_artist, album ON files FOR EACH ROW" \
|
||||
" BEGIN" \
|
||||
" UPDATE files SET songartistid = daap_songalbumid(LOWER(NEW.album_artist), ''), " \
|
||||
" songalbumid = daap_songalbumid(LOWER(NEW.album_artist), LOWER(NEW.album))" \
|
||||
" WHERE id = NEW.id;" \
|
||||
" END;"
|
||||
|
||||
#define TRG_GROUPS_UPDATE \
|
||||
"CREATE TRIGGER trg_groups_update AFTER UPDATE OF songartistid, songalbumid ON files FOR EACH ROW" \
|
||||
" WHEN (NEW.songartistid != 0 AND NEW.songalbumid != 0)" \
|
||||
" BEGIN" \
|
||||
" INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (1, NEW.album, NEW.songalbumid);" \
|
||||
" INSERT OR IGNORE INTO groups (type, name, persistentid) VALUES (2, NEW.album_artist, NEW.songartistid);" \
|
||||
" END;"
|
||||
|
||||
static const struct db_init_query db_init_trigger_queries[] =
|
||||
{
|
||||
{ TRG_FILES_INSERT_SONGIDS, "create trigger trg_files_insert_songids" },
|
||||
{ TRG_FILES_UPDATE_SONGIDS, "create trigger trg_files_update_songids" },
|
||||
{ TRG_GROUPS_UPDATE, "create trigger trg_groups_update" },
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
db_init_indices(sqlite3 *hdl)
|
||||
{
|
||||
@ -435,6 +455,30 @@ db_init_indices(sqlite3 *hdl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
db_init_triggers(sqlite3 *hdl)
|
||||
{
|
||||
char *errmsg;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < (sizeof(db_init_trigger_queries) / sizeof(db_init_trigger_queries[0])); i++)
|
||||
{
|
||||
DPRINTF(E_DBG, L_DB, "DB init trigger query: %s\n", db_init_trigger_queries[i].desc);
|
||||
|
||||
ret = sqlite3_exec(hdl, db_init_trigger_queries[i].query, NULL, NULL, &errmsg);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_DB, "DB init error: %s\n", errmsg);
|
||||
|
||||
sqlite3_free(errmsg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
db_init_tables(sqlite3 *hdl)
|
||||
{
|
||||
|
@ -25,12 +25,15 @@
|
||||
* version of the database? If yes, then it is a minor upgrade, if no, then it
|
||||
* is a major upgrade. In other words minor version upgrades permit downgrading
|
||||
* forked-daapd after the database was upgraded. */
|
||||
#define SCHEMA_VERSION_MAJOR 19
|
||||
#define SCHEMA_VERSION_MINOR 12
|
||||
#define SCHEMA_VERSION_MAJOR 20
|
||||
#define SCHEMA_VERSION_MINOR 00
|
||||
|
||||
int
|
||||
db_init_indices(sqlite3 *hdl);
|
||||
|
||||
int
|
||||
db_init_triggers(sqlite3 *hdl);
|
||||
|
||||
int
|
||||
db_init_tables(sqlite3 *hdl);
|
||||
|
||||
|
1278
src/db_upgrade.c
1278
src/db_upgrade.c
File diff suppressed because it is too large
Load Diff
@ -154,7 +154,7 @@ response_process(struct http_client_ctx *ctx, char **errmsg)
|
||||
DPRINTF(E_DBG, L_LASTFM, "LastFM response:\n%s\n", body);
|
||||
|
||||
if (errmsg)
|
||||
*errmsg = trimwhitespace(mxmlGetOpaque(e_node));
|
||||
*errmsg = atrim(mxmlGetOpaque(e_node));
|
||||
|
||||
mxmlDelete(tree);
|
||||
return -1;
|
||||
@ -180,7 +180,7 @@ response_process(struct http_client_ctx *ctx, char **errmsg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
sk = trimwhitespace(mxmlGetOpaque(s_node));
|
||||
sk = atrim(mxmlGetOpaque(s_node));
|
||||
if (sk)
|
||||
{
|
||||
DPRINTF(E_LOG, L_LASTFM, "Got session key from LastFM: %s\n", sk);
|
||||
|
@ -128,14 +128,6 @@ library_add_media(struct media_file_info *mfi)
|
||||
mfi->path, mfi->directory_id, mfi->virtual_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(mfi);
|
||||
|
||||
if (mfi->id == 0)
|
||||
db_file_add(mfi);
|
||||
else
|
||||
|
@ -509,14 +509,18 @@ process_regular_file(const char *file, struct stat *sb, int type, int flags, int
|
||||
else
|
||||
{
|
||||
mfi.data_kind = DATA_KIND_FILE;
|
||||
mfi.file_size = sb->st_size;
|
||||
|
||||
if (type & F_SCAN_TYPE_AUDIOBOOK)
|
||||
mfi.media_kind = MEDIA_KIND_AUDIOBOOK;
|
||||
else if (type & F_SCAN_TYPE_PODCAST)
|
||||
mfi.media_kind = MEDIA_KIND_PODCAST;
|
||||
|
||||
mfi.compilation = (type & F_SCAN_TYPE_COMPILATION);
|
||||
mfi.file_size = sb->st_size;
|
||||
if (type & F_SCAN_TYPE_COMPILATION)
|
||||
{
|
||||
mfi.compilation = 1;
|
||||
mfi.album_artist = safe_strdup(cfg_getstr(cfg_getsec(cfg, "library"), "compilation_artist"));
|
||||
}
|
||||
|
||||
ret = scan_metadata_ffmpeg(file, &mfi);
|
||||
if (ret < 0)
|
||||
@ -1686,7 +1690,6 @@ queue_add_stream(const char *path, int position, char reshuffle, uint32_t item_i
|
||||
memset(&mfi, 0, sizeof(struct media_file_info));
|
||||
|
||||
scan_metadata_stream(path, &mfi);
|
||||
unicode_fixup_mfi(&mfi);
|
||||
|
||||
map_media_file_to_queue_item(&item, &mfi);
|
||||
|
||||
|
@ -548,7 +548,6 @@ process_track_file(plist_t trk)
|
||||
mfi->album_artist = strdup(mfi->artist);
|
||||
}
|
||||
|
||||
unicode_fixup_mfi(mfi);
|
||||
db_file_update(mfi);
|
||||
|
||||
free_mfi(mfi, 0);
|
||||
|
89
src/misc.c
89
src/misc.c
@ -33,6 +33,7 @@
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <sys/param.h>
|
||||
#ifndef CLOCK_REALTIME
|
||||
@ -341,6 +342,7 @@ safe_asprintf(const char *fmt, ...)
|
||||
{
|
||||
char *ret = NULL;
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
if (vasprintf(&ret, fmt, va) < 0)
|
||||
{
|
||||
@ -348,9 +350,34 @@ safe_asprintf(const char *fmt, ...)
|
||||
abort();
|
||||
}
|
||||
va_end(va);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
safe_snprintf_cat(char *dst, size_t n, const char *fmt, ...)
|
||||
{
|
||||
size_t dstlen;
|
||||
va_list va;
|
||||
int ret;
|
||||
|
||||
if (!dst || !fmt)
|
||||
return -1;
|
||||
|
||||
dstlen = strlen(dst);
|
||||
if (n < dstlen)
|
||||
return -1;
|
||||
|
||||
va_start(va, fmt);
|
||||
ret = vsnprintf(dst + dstlen, n - dstlen, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
if (ret >= 0 && ret < n - dstlen)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Key/value functions */
|
||||
struct keyval *
|
||||
@ -586,7 +613,7 @@ m_readfile(const char *path, int num_lines)
|
||||
goto error;
|
||||
}
|
||||
|
||||
lines[i] = trimwhitespace(line);
|
||||
lines[i] = atrim(line);
|
||||
if (!lines[i] || (strlen(lines[i]) == 0))
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Line %d in '%s' is invalid\n", i+1, path);
|
||||
@ -643,40 +670,58 @@ unicode_fixup_string(char *str, const char *fromcode)
|
||||
}
|
||||
|
||||
char *
|
||||
trimwhitespace(const char *str)
|
||||
trim(char *str)
|
||||
{
|
||||
char *ptr;
|
||||
char *start;
|
||||
char *out;
|
||||
size_t start; // Position of first non-space char
|
||||
size_t term; // Position of 0-terminator
|
||||
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
// Find the beginning
|
||||
while (isspace(*str))
|
||||
str++;
|
||||
start = 0;
|
||||
term = strlen(str);
|
||||
|
||||
if (*str == 0) // All spaces?
|
||||
return strdup("");
|
||||
while ((start < term) && isspace(str[start]))
|
||||
start++;
|
||||
while ((term > start) && isspace(str[term - 1]))
|
||||
term--;
|
||||
|
||||
// Make copy, because we will need to insert a null terminator
|
||||
start = strdup(str);
|
||||
if (!start)
|
||||
str[term] = '\0';
|
||||
|
||||
// Shift chars incl. terminator
|
||||
if (start)
|
||||
memmove(str, str + start, term - start + 1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *
|
||||
atrim(const char *str)
|
||||
{
|
||||
size_t start; // Position of first non-space char
|
||||
size_t term; // Position of 0-terminator
|
||||
size_t size;
|
||||
char *result;
|
||||
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
// Find the end
|
||||
ptr = start + strlen(start) - 1;
|
||||
while (ptr > start && isspace(*ptr))
|
||||
ptr--;
|
||||
start = 0;
|
||||
term = strlen(str);
|
||||
|
||||
// Insert null terminator
|
||||
*(ptr+1) = 0;
|
||||
while ((start < term) && isspace(str[start]))
|
||||
start++;
|
||||
while ((term > start) && isspace(str[term - 1]))
|
||||
term--;
|
||||
|
||||
out = strdup(start);
|
||||
size = term - start + 1;
|
||||
|
||||
free(start);
|
||||
result = malloc(size);
|
||||
|
||||
return out;
|
||||
memcpy(result, str + start, size);
|
||||
result[size - 1] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
|
14
src/misc.h
14
src/misc.h
@ -58,6 +58,10 @@ safe_strdup(const char *str);
|
||||
char *
|
||||
safe_asprintf(const char *fmt, ...);
|
||||
|
||||
int
|
||||
safe_snprintf_cat(char *dst, size_t n, const char *fmt, ...);
|
||||
|
||||
|
||||
/* Key/value functions */
|
||||
struct keyval *
|
||||
keyval_alloc(void);
|
||||
@ -87,8 +91,13 @@ m_readfile(const char *path, int num_lines);
|
||||
char *
|
||||
unicode_fixup_string(char *str, const char *fromcode);
|
||||
|
||||
// Modifies str so it is trimmed. Returns pointer to str.
|
||||
char *
|
||||
trimwhitespace(const char *str);
|
||||
trim(char *str);
|
||||
|
||||
// Copies the trimmed part of str to a newly allocated string (caller must free)
|
||||
char *
|
||||
atrim(const char *str);
|
||||
|
||||
void
|
||||
swap_pointers(char **a, char **b);
|
||||
@ -105,8 +114,7 @@ b64_encode(const uint8_t *in, size_t len);
|
||||
uint64_t
|
||||
murmur_hash64(const void *key, int len, uint32_t seed);
|
||||
|
||||
|
||||
/* Checks if the address is in a network that is configured as trusted */
|
||||
// Checks if the address is in a network that is configured as trusted
|
||||
bool
|
||||
peer_address_is_trusted(const char *addr);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user