mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-29 08:33:23 -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
|
[dnl Check that SQLite3 has the unlock notify API built-in
|
||||||
AC_CHECK_FUNC([[sqlite3_unlock_notify]], [],
|
AC_CHECK_FUNC([[sqlite3_unlock_notify]], [],
|
||||||
[AC_MSG_ERROR([[SQLite3 was built without unlock notify support]])])
|
[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
|
dnl Check that SQLite3 has been built with threadsafe operations
|
||||||
AC_MSG_CHECKING([[if SQLite3 was built with threadsafe operations support]])
|
AC_MSG_CHECKING([[if SQLite3 was built with threadsafe operations support]])
|
||||||
AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <sqlite3.h>
|
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)
|
# (changing this setting only takes effect after rescan, see the README)
|
||||||
compilations = { "/Compilations" }
|
compilations = { "/Compilations" }
|
||||||
|
|
||||||
# Compilations usually have many artists, and if you don't want every
|
# Compilations usually have many artists, and sometimes no album artist.
|
||||||
# artist to be listed when artist browsing in Remote, you can set
|
# If you don't want every artist to be listed in artist views, you can
|
||||||
# a single name which will be used for all music in the compilation dir
|
# 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)
|
# (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
|
# 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
|
# 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);
|
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
|
static int
|
||||||
sqlext_daap_unicode_xcollation(void *notused, int llen, const void *left, int rlen, const void *right)
|
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;
|
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);
|
ret = sqlite3_create_collation(db, "DAAP", SQLITE_UTF8, NULL, sqlext_daap_unicode_xcollation);
|
||||||
if (ret != SQLITE_OK)
|
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 */
|
/* Note that fields marked as integers in the metadata map in filescanner_ffmpeg must be uint32_t here */
|
||||||
struct media_file_info {
|
struct media_file_info {
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
char *path;
|
char *path;
|
||||||
uint32_t index;
|
char *virtual_path;
|
||||||
char *fname;
|
char *fname;
|
||||||
|
uint32_t directory_id; /* Id of directory */
|
||||||
char *title;
|
char *title;
|
||||||
char *artist;
|
char *artist;
|
||||||
char *album;
|
char *album;
|
||||||
|
char *album_artist;
|
||||||
char *genre;
|
char *genre;
|
||||||
char *comment;
|
char *comment;
|
||||||
char *type; /* daap.songformat */
|
char *type; /* daap.songformat */
|
||||||
@ -160,6 +164,7 @@ struct media_file_info {
|
|||||||
uint32_t song_length;
|
uint32_t song_length;
|
||||||
int64_t file_size;
|
int64_t file_size;
|
||||||
uint32_t year; /* TDRC */
|
uint32_t year; /* TDRC */
|
||||||
|
uint32_t date_released;
|
||||||
|
|
||||||
uint32_t track; /* TRCK */
|
uint32_t track; /* TRCK */
|
||||||
uint32_t total_tracks;
|
uint32_t total_tracks;
|
||||||
@ -167,44 +172,44 @@ struct media_file_info {
|
|||||||
uint32_t disc; /* TPOS */
|
uint32_t disc; /* TPOS */
|
||||||
uint32_t total_discs;
|
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_added; /* FIXME: time_t */
|
||||||
uint32_t time_modified;
|
uint32_t time_modified;
|
||||||
uint32_t time_played;
|
uint32_t time_played;
|
||||||
|
uint32_t time_skipped;
|
||||||
uint32_t play_count;
|
|
||||||
uint32_t seek;
|
|
||||||
uint32_t rating;
|
|
||||||
uint32_t db_timestamp;
|
|
||||||
|
|
||||||
uint32_t disabled;
|
uint32_t disabled;
|
||||||
uint32_t bpm; /* TBPM */
|
|
||||||
|
|
||||||
uint32_t id;
|
uint64_t sample_count; //TODO [unused] sample count is never set and therefor always 0
|
||||||
|
|
||||||
char *description; /* daap.songdescription */
|
|
||||||
char *codectype; /* song.codectype, 4 chars max (32 bits) */
|
char *codectype; /* song.codectype, 4 chars max (32 bits) */
|
||||||
|
|
||||||
uint32_t item_kind; /* song or movie */
|
uint32_t idx;
|
||||||
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;
|
|
||||||
|
|
||||||
/* iTunes 5+ */
|
uint32_t has_video; /* iTunes 6.0.2 */
|
||||||
uint32_t contentrating;
|
uint32_t contentrating;/* iTunes 5+ */
|
||||||
|
|
||||||
/* iTunes 6.0.2 */
|
|
||||||
uint32_t has_video;
|
|
||||||
uint32_t bits_per_sample;
|
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_series_name;
|
||||||
char *tv_episode_num_str; /* com.apple.itunes.episode-num-str, used as a unique episode identifier */
|
char *tv_episode_num_str; /* com.apple.itunes.episode-num-str, used as a unique episode identifier */
|
||||||
char *tv_network_name;
|
char *tv_network_name;
|
||||||
|
uint32_t tv_episode_sort;
|
||||||
char *album_artist;
|
uint32_t tv_season_num;
|
||||||
|
|
||||||
int64_t songartistid;
|
int64_t songartistid;
|
||||||
int64_t songalbumid;
|
int64_t songalbumid;
|
||||||
@ -212,16 +217,8 @@ struct media_file_info {
|
|||||||
char *title_sort;
|
char *title_sort;
|
||||||
char *artist_sort;
|
char *artist_sort;
|
||||||
char *album_sort;
|
char *album_sort;
|
||||||
char *composer_sort;
|
|
||||||
char *album_artist_sort;
|
char *album_artist_sort;
|
||||||
|
char *composer_sort;
|
||||||
char *virtual_path;
|
|
||||||
|
|
||||||
uint32_t directory_id; /* Id of directory */
|
|
||||||
uint32_t date_released;
|
|
||||||
|
|
||||||
uint32_t skip_count;
|
|
||||||
uint32_t time_skipped;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define mfi_offsetof(field) offsetof(struct media_file_info, field)
|
#define mfi_offsetof(field) offsetof(struct media_file_info, field)
|
||||||
@ -308,10 +305,13 @@ struct db_group_info {
|
|||||||
struct db_media_file_info {
|
struct db_media_file_info {
|
||||||
char *id;
|
char *id;
|
||||||
char *path;
|
char *path;
|
||||||
|
char *virtual_path;
|
||||||
char *fname;
|
char *fname;
|
||||||
|
char *directory_id;
|
||||||
char *title;
|
char *title;
|
||||||
char *artist;
|
char *artist;
|
||||||
char *album;
|
char *album;
|
||||||
|
char *album_artist;
|
||||||
char *genre;
|
char *genre;
|
||||||
char *comment;
|
char *comment;
|
||||||
char *type;
|
char *type;
|
||||||
@ -325,6 +325,7 @@ struct db_media_file_info {
|
|||||||
char *song_length;
|
char *song_length;
|
||||||
char *file_size;
|
char *file_size;
|
||||||
char *year;
|
char *year;
|
||||||
|
char *date_released;
|
||||||
char *track;
|
char *track;
|
||||||
char *total_tracks;
|
char *total_tracks;
|
||||||
char *disc;
|
char *disc;
|
||||||
@ -334,14 +335,17 @@ struct db_media_file_info {
|
|||||||
char *artwork;
|
char *artwork;
|
||||||
char *rating;
|
char *rating;
|
||||||
char *play_count;
|
char *play_count;
|
||||||
|
char *skip_count;
|
||||||
char *seek;
|
char *seek;
|
||||||
char *data_kind;
|
char *data_kind;
|
||||||
|
char *media_kind;
|
||||||
char *item_kind;
|
char *item_kind;
|
||||||
char *description;
|
char *description;
|
||||||
|
char *db_timestamp;
|
||||||
char *time_added;
|
char *time_added;
|
||||||
char *time_modified;
|
char *time_modified;
|
||||||
char *time_played;
|
char *time_played;
|
||||||
char *db_timestamp;
|
char *time_skipped;
|
||||||
char *disabled;
|
char *disabled;
|
||||||
char *sample_count;
|
char *sample_count;
|
||||||
char *codectype;
|
char *codectype;
|
||||||
@ -349,8 +353,6 @@ struct db_media_file_info {
|
|||||||
char *has_video;
|
char *has_video;
|
||||||
char *contentrating;
|
char *contentrating;
|
||||||
char *bits_per_sample;
|
char *bits_per_sample;
|
||||||
char *album_artist;
|
|
||||||
char *media_kind;
|
|
||||||
char *tv_episode_sort;
|
char *tv_episode_sort;
|
||||||
char *tv_season_num;
|
char *tv_season_num;
|
||||||
char *tv_series_name;
|
char *tv_series_name;
|
||||||
@ -361,13 +363,8 @@ struct db_media_file_info {
|
|||||||
char *title_sort;
|
char *title_sort;
|
||||||
char *artist_sort;
|
char *artist_sort;
|
||||||
char *album_sort;
|
char *album_sort;
|
||||||
char *composer_sort;
|
|
||||||
char *album_artist_sort;
|
char *album_artist_sort;
|
||||||
char *virtual_path;
|
char *composer_sort;
|
||||||
char *directory_id;
|
|
||||||
char *date_released;
|
|
||||||
char *skip_count;
|
|
||||||
char *time_skipped;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field)
|
#define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field)
|
||||||
@ -425,8 +422,7 @@ struct directory_enum {
|
|||||||
void *stmt;
|
void *stmt;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct db_queue_item
|
struct db_queue_item {
|
||||||
{
|
|
||||||
/* A unique id for this queue item. If the same item appears multiple
|
/* 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. */
|
times in the queue each corresponding queue item has its own id. */
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
@ -434,18 +430,16 @@ struct db_queue_item
|
|||||||
/* Id of the file/item in the files database */
|
/* Id of the file/item in the files database */
|
||||||
uint32_t file_id;
|
uint32_t file_id;
|
||||||
|
|
||||||
/* Length of the item in ms */
|
uint32_t pos;
|
||||||
uint32_t song_length;
|
uint32_t shuffle_pos;
|
||||||
|
|
||||||
/* Data type of the item */
|
/* Data type of the item */
|
||||||
enum data_kind data_kind;
|
enum data_kind data_kind;
|
||||||
/* Media type of the item */
|
/* Media type of the item */
|
||||||
enum media_kind media_kind;
|
enum media_kind media_kind;
|
||||||
|
|
||||||
uint32_t seek;
|
/* Length of the item in ms */
|
||||||
|
uint32_t song_length;
|
||||||
uint32_t pos;
|
|
||||||
uint32_t shuffle_pos;
|
|
||||||
|
|
||||||
char *path;
|
char *path;
|
||||||
char *virtual_path;
|
char *virtual_path;
|
||||||
@ -453,7 +447,6 @@ struct db_queue_item
|
|||||||
char *title;
|
char *title;
|
||||||
char *artist;
|
char *artist;
|
||||||
char *album_artist;
|
char *album_artist;
|
||||||
char *composer;
|
|
||||||
char *album;
|
char *album;
|
||||||
char *genre;
|
char *genre;
|
||||||
|
|
||||||
@ -471,8 +464,15 @@ struct db_queue_item
|
|||||||
char *artwork_url;
|
char *artwork_url;
|
||||||
|
|
||||||
uint32_t queue_version;
|
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
|
struct db_queue_add_info
|
||||||
{
|
{
|
||||||
int queue_version;
|
int queue_version;
|
||||||
@ -510,12 +510,6 @@ free_query_params(struct query_params *qp, int content_only);
|
|||||||
void
|
void
|
||||||
free_queue_item(struct db_queue_item *queue_item, int content_only);
|
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 */
|
/* Maintenance and DB hygiene */
|
||||||
void
|
void
|
||||||
db_hook_post_scan(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 (" \
|
"CREATE TABLE IF NOT EXISTS files (" \
|
||||||
" id INTEGER PRIMARY KEY NOT NULL," \
|
" id INTEGER PRIMARY KEY NOT NULL," \
|
||||||
" path VARCHAR(4096) NOT NULL," \
|
" path VARCHAR(4096) NOT NULL," \
|
||||||
|
" virtual_path VARCHAR(4096) DEFAULT NULL," \
|
||||||
" fname VARCHAR(255) NOT NULL," \
|
" fname VARCHAR(255) NOT NULL," \
|
||||||
|
" directory_id INTEGER DEFAULT 0," \
|
||||||
" title VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
" title VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||||
" artist VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
" artist VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||||
" album VARCHAR(1024) NOT 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," \
|
" genre VARCHAR(255) DEFAULT NULL COLLATE DAAP," \
|
||||||
" comment VARCHAR(4096) DEFAULT NULL COLLATE DAAP," \
|
" comment VARCHAR(4096) DEFAULT NULL COLLATE DAAP," \
|
||||||
" type VARCHAR(255) DEFAULT NULL COLLATE DAAP," \
|
" type VARCHAR(255) DEFAULT NULL COLLATE DAAP," \
|
||||||
@ -53,6 +56,7 @@
|
|||||||
" song_length INTEGER DEFAULT 0," \
|
" song_length INTEGER DEFAULT 0," \
|
||||||
" file_size INTEGER DEFAULT 0," \
|
" file_size INTEGER DEFAULT 0," \
|
||||||
" year INTEGER DEFAULT 0," \
|
" year INTEGER DEFAULT 0," \
|
||||||
|
" date_released INTEGER DEFAULT 0," \
|
||||||
" track INTEGER DEFAULT 0," \
|
" track INTEGER DEFAULT 0," \
|
||||||
" total_tracks INTEGER DEFAULT 0," \
|
" total_tracks INTEGER DEFAULT 0," \
|
||||||
" disc INTEGER DEFAULT 0," \
|
" disc INTEGER DEFAULT 0," \
|
||||||
@ -62,14 +66,17 @@
|
|||||||
" artwork INTEGER DEFAULT 0," \
|
" artwork INTEGER DEFAULT 0," \
|
||||||
" rating INTEGER DEFAULT 0," \
|
" rating INTEGER DEFAULT 0," \
|
||||||
" play_count INTEGER DEFAULT 0," \
|
" play_count INTEGER DEFAULT 0," \
|
||||||
|
" skip_count INTEGER DEFAULT 0," \
|
||||||
" seek INTEGER DEFAULT 0," \
|
" seek INTEGER DEFAULT 0," \
|
||||||
" data_kind INTEGER DEFAULT 0," \
|
" data_kind INTEGER DEFAULT 0," \
|
||||||
|
" media_kind INTEGER DEFAULT 0," \
|
||||||
" item_kind INTEGER DEFAULT 0," \
|
" item_kind INTEGER DEFAULT 0," \
|
||||||
" description INTEGER DEFAULT 0," \
|
" description INTEGER DEFAULT 0," \
|
||||||
|
" db_timestamp INTEGER DEFAULT 0," \
|
||||||
" time_added INTEGER DEFAULT 0," \
|
" time_added INTEGER DEFAULT 0," \
|
||||||
" time_modified INTEGER DEFAULT 0," \
|
" time_modified INTEGER DEFAULT 0," \
|
||||||
" time_played INTEGER DEFAULT 0," \
|
" time_played INTEGER DEFAULT 0," \
|
||||||
" db_timestamp INTEGER DEFAULT 0," \
|
" time_skipped INTEGER DEFAULT 0," \
|
||||||
" disabled INTEGER DEFAULT 0," \
|
" disabled INTEGER DEFAULT 0," \
|
||||||
" sample_count INTEGER DEFAULT 0," \
|
" sample_count INTEGER DEFAULT 0," \
|
||||||
" codectype VARCHAR(5) DEFAULT NULL," \
|
" codectype VARCHAR(5) DEFAULT NULL," \
|
||||||
@ -77,25 +84,18 @@
|
|||||||
" has_video INTEGER DEFAULT 0," \
|
" has_video INTEGER DEFAULT 0," \
|
||||||
" contentrating INTEGER DEFAULT 0," \
|
" contentrating INTEGER DEFAULT 0," \
|
||||||
" bits_per_sample 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_series_name VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||||
" tv_episode_num_str 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_network_name VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||||
" tv_episode_sort INTEGER NOT NULL," \
|
" tv_episode_sort INTEGER NOT NULL," \
|
||||||
" tv_season_num INTEGER NOT NULL," \
|
" tv_season_num INTEGER NOT NULL," \
|
||||||
" songartistid INTEGER NOT NULL," \
|
" songartistid INTEGER DEFAULT 0," \
|
||||||
" songalbumid INTEGER NOT NULL," \
|
" songalbumid INTEGER DEFAULT 0," \
|
||||||
" title_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
" title_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||||
" artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
" artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||||
" album_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," \
|
" album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||||
" virtual_path VARCHAR(4096) DEFAULT NULL," \
|
" composer_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP" \
|
||||||
" directory_id INTEGER DEFAULT 0," \
|
|
||||||
" date_released INTEGER DEFAULT 0," \
|
|
||||||
" skip_count INTEGER DEFAULT 0," \
|
|
||||||
" time_skipped INTEGER DEFAULT 0" \
|
|
||||||
");"
|
");"
|
||||||
|
|
||||||
#define T_PL \
|
#define T_PL \
|
||||||
@ -194,20 +194,6 @@
|
|||||||
" composer VARCHAR(1024) DEFAULT NULL" \
|
" 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 \
|
#define Q_PL1 \
|
||||||
"INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \
|
"INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx, special_id)" \
|
||||||
" VALUES(1, 'Library', 0, '1 = 1', 0, '', 0, 0);"
|
" 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_DIRECTORIES, "create table directories" },
|
||||||
{ T_QUEUE, "create table queue" },
|
{ 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_PL1, "create default playlist" },
|
||||||
{ Q_PL2, "create default smart playlist 'Music'" },
|
{ Q_PL2, "create default smart playlist 'Music'" },
|
||||||
{ Q_PL3, "create default smart playlist 'Movies'" },
|
{ Q_PL3, "create default smart playlist 'Movies'" },
|
||||||
{ Q_PL4, "create default smart playlist 'TV Shows'" },
|
{ Q_PL4, "create default smart playlist 'TV Shows'" },
|
||||||
{ Q_PL5, "create default smart playlist 'Podcasts'" },
|
{ Q_PL5, "create default smart playlist 'Podcasts'" },
|
||||||
{ Q_PL6, "create default smart playlist 'Audiobooks'" },
|
{ Q_PL6, "create default smart playlist 'Audiobooks'" },
|
||||||
|
|
||||||
{ Q_DIR1, "create default root directory '/'" },
|
{ Q_DIR1, "create default root directory '/'" },
|
||||||
{ Q_DIR2, "create default base directory '/file:'" },
|
{ Q_DIR2, "create default base directory '/file:'" },
|
||||||
{ Q_DIR3, "create default base directory '/http:'" },
|
{ Q_DIR3, "create default base directory '/http:'" },
|
||||||
{ Q_DIR4, "create default base directory '/spotify:'" },
|
{ Q_DIR4, "create default base directory '/spotify:'" },
|
||||||
|
|
||||||
{ Q_QUEUE_VERSION, "initialize queue version" },
|
{ 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" },
|
{ 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
|
int
|
||||||
db_init_indices(sqlite3 *hdl)
|
db_init_indices(sqlite3 *hdl)
|
||||||
{
|
{
|
||||||
@ -435,6 +455,30 @@ db_init_indices(sqlite3 *hdl)
|
|||||||
return 0;
|
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
|
int
|
||||||
db_init_tables(sqlite3 *hdl)
|
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
|
* 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
|
* is a major upgrade. In other words minor version upgrades permit downgrading
|
||||||
* forked-daapd after the database was upgraded. */
|
* forked-daapd after the database was upgraded. */
|
||||||
#define SCHEMA_VERSION_MAJOR 19
|
#define SCHEMA_VERSION_MAJOR 20
|
||||||
#define SCHEMA_VERSION_MINOR 12
|
#define SCHEMA_VERSION_MINOR 00
|
||||||
|
|
||||||
int
|
int
|
||||||
db_init_indices(sqlite3 *hdl);
|
db_init_indices(sqlite3 *hdl);
|
||||||
|
|
||||||
|
int
|
||||||
|
db_init_triggers(sqlite3 *hdl);
|
||||||
|
|
||||||
int
|
int
|
||||||
db_init_tables(sqlite3 *hdl);
|
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);
|
DPRINTF(E_DBG, L_LASTFM, "LastFM response:\n%s\n", body);
|
||||||
|
|
||||||
if (errmsg)
|
if (errmsg)
|
||||||
*errmsg = trimwhitespace(mxmlGetOpaque(e_node));
|
*errmsg = atrim(mxmlGetOpaque(e_node));
|
||||||
|
|
||||||
mxmlDelete(tree);
|
mxmlDelete(tree);
|
||||||
return -1;
|
return -1;
|
||||||
@ -180,7 +180,7 @@ response_process(struct http_client_ctx *ctx, char **errmsg)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk = trimwhitespace(mxmlGetOpaque(s_node));
|
sk = atrim(mxmlGetOpaque(s_node));
|
||||||
if (sk)
|
if (sk)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_LASTFM, "Got session key from LastFM: %s\n", 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);
|
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)
|
if (mfi->id == 0)
|
||||||
db_file_add(mfi);
|
db_file_add(mfi);
|
||||||
else
|
else
|
||||||
|
@ -509,14 +509,18 @@ process_regular_file(const char *file, struct stat *sb, int type, int flags, int
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
mfi.data_kind = DATA_KIND_FILE;
|
mfi.data_kind = DATA_KIND_FILE;
|
||||||
|
mfi.file_size = sb->st_size;
|
||||||
|
|
||||||
if (type & F_SCAN_TYPE_AUDIOBOOK)
|
if (type & F_SCAN_TYPE_AUDIOBOOK)
|
||||||
mfi.media_kind = MEDIA_KIND_AUDIOBOOK;
|
mfi.media_kind = MEDIA_KIND_AUDIOBOOK;
|
||||||
else if (type & F_SCAN_TYPE_PODCAST)
|
else if (type & F_SCAN_TYPE_PODCAST)
|
||||||
mfi.media_kind = MEDIA_KIND_PODCAST;
|
mfi.media_kind = MEDIA_KIND_PODCAST;
|
||||||
|
|
||||||
mfi.compilation = (type & F_SCAN_TYPE_COMPILATION);
|
if (type & F_SCAN_TYPE_COMPILATION)
|
||||||
mfi.file_size = sb->st_size;
|
{
|
||||||
|
mfi.compilation = 1;
|
||||||
|
mfi.album_artist = safe_strdup(cfg_getstr(cfg_getsec(cfg, "library"), "compilation_artist"));
|
||||||
|
}
|
||||||
|
|
||||||
ret = scan_metadata_ffmpeg(file, &mfi);
|
ret = scan_metadata_ffmpeg(file, &mfi);
|
||||||
if (ret < 0)
|
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));
|
memset(&mfi, 0, sizeof(struct media_file_info));
|
||||||
|
|
||||||
scan_metadata_stream(path, &mfi);
|
scan_metadata_stream(path, &mfi);
|
||||||
unicode_fixup_mfi(&mfi);
|
|
||||||
|
|
||||||
map_media_file_to_queue_item(&item, &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);
|
mfi->album_artist = strdup(mfi->artist);
|
||||||
}
|
}
|
||||||
|
|
||||||
unicode_fixup_mfi(mfi);
|
|
||||||
db_file_update(mfi);
|
db_file_update(mfi);
|
||||||
|
|
||||||
free_mfi(mfi, 0);
|
free_mfi(mfi, 0);
|
||||||
|
89
src/misc.c
89
src/misc.c
@ -33,6 +33,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#ifndef CLOCK_REALTIME
|
#ifndef CLOCK_REALTIME
|
||||||
@ -341,6 +342,7 @@ safe_asprintf(const char *fmt, ...)
|
|||||||
{
|
{
|
||||||
char *ret = NULL;
|
char *ret = NULL;
|
||||||
va_list va;
|
va_list va;
|
||||||
|
|
||||||
va_start(va, fmt);
|
va_start(va, fmt);
|
||||||
if (vasprintf(&ret, fmt, va) < 0)
|
if (vasprintf(&ret, fmt, va) < 0)
|
||||||
{
|
{
|
||||||
@ -348,9 +350,34 @@ safe_asprintf(const char *fmt, ...)
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
va_end(va);
|
va_end(va);
|
||||||
|
|
||||||
return ret;
|
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 */
|
/* Key/value functions */
|
||||||
struct keyval *
|
struct keyval *
|
||||||
@ -586,7 +613,7 @@ m_readfile(const char *path, int num_lines)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
lines[i] = trimwhitespace(line);
|
lines[i] = atrim(line);
|
||||||
if (!lines[i] || (strlen(lines[i]) == 0))
|
if (!lines[i] || (strlen(lines[i]) == 0))
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MISC, "Line %d in '%s' is invalid\n", i+1, path);
|
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 *
|
char *
|
||||||
trimwhitespace(const char *str)
|
trim(char *str)
|
||||||
{
|
{
|
||||||
char *ptr;
|
size_t start; // Position of first non-space char
|
||||||
char *start;
|
size_t term; // Position of 0-terminator
|
||||||
char *out;
|
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// Find the beginning
|
start = 0;
|
||||||
while (isspace(*str))
|
term = strlen(str);
|
||||||
str++;
|
|
||||||
|
|
||||||
if (*str == 0) // All spaces?
|
while ((start < term) && isspace(str[start]))
|
||||||
return strdup("");
|
start++;
|
||||||
|
while ((term > start) && isspace(str[term - 1]))
|
||||||
|
term--;
|
||||||
|
|
||||||
// Make copy, because we will need to insert a null terminator
|
str[term] = '\0';
|
||||||
start = strdup(str);
|
|
||||||
if (!start)
|
// 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;
|
return NULL;
|
||||||
|
|
||||||
// Find the end
|
start = 0;
|
||||||
ptr = start + strlen(start) - 1;
|
term = strlen(str);
|
||||||
while (ptr > start && isspace(*ptr))
|
|
||||||
ptr--;
|
|
||||||
|
|
||||||
// Insert null terminator
|
while ((start < term) && isspace(str[start]))
|
||||||
*(ptr+1) = 0;
|
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
|
void
|
||||||
|
14
src/misc.h
14
src/misc.h
@ -58,6 +58,10 @@ safe_strdup(const char *str);
|
|||||||
char *
|
char *
|
||||||
safe_asprintf(const char *fmt, ...);
|
safe_asprintf(const char *fmt, ...);
|
||||||
|
|
||||||
|
int
|
||||||
|
safe_snprintf_cat(char *dst, size_t n, const char *fmt, ...);
|
||||||
|
|
||||||
|
|
||||||
/* Key/value functions */
|
/* Key/value functions */
|
||||||
struct keyval *
|
struct keyval *
|
||||||
keyval_alloc(void);
|
keyval_alloc(void);
|
||||||
@ -87,8 +91,13 @@ m_readfile(const char *path, int num_lines);
|
|||||||
char *
|
char *
|
||||||
unicode_fixup_string(char *str, const char *fromcode);
|
unicode_fixup_string(char *str, const char *fromcode);
|
||||||
|
|
||||||
|
// Modifies str so it is trimmed. Returns pointer to str.
|
||||||
char *
|
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
|
void
|
||||||
swap_pointers(char **a, char **b);
|
swap_pointers(char **a, char **b);
|
||||||
@ -105,8 +114,7 @@ b64_encode(const uint8_t *in, size_t len);
|
|||||||
uint64_t
|
uint64_t
|
||||||
murmur_hash64(const void *key, int len, uint32_t seed);
|
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
|
bool
|
||||||
peer_address_is_trusted(const char *addr);
|
peer_address_is_trusted(const char *addr);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user