mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 15:45:56 -05:00
[db,library] Add "scan_kind" field to playlists, directories and files
(db upgrade to v22.00) `scan_kind` identifies the library "scanner" component that created the item and is responsible to keep it up to date (rescan). The library update now supports passing a `scan_kind` to update only the items of one particular "scanner". This allows e. g. to only update the item from the Spotify library or only update the RSS feeds. The OwnTone database is upgraded to v22.00 and the `scan_kind` columns in `files`, `playlists`, `directories` are identified by: 1. Check if item is part of a RSS playlist (podcast RSS feed), they belong to the "rssscanner" 2. Check if item has a Spotify `virtual_path`, they belong to the "spotifyscanner" 3. Remaining items belong to the "filescanner"
This commit is contained in:
parent
31e90070ff
commit
1d2e4dc7a8
150
src/db.c
150
src/db.c
@ -228,6 +228,7 @@ static const struct col_type_map mfi_cols_map[] =
|
||||
{ "composer_sort", mfi_offsetof(composer_sort), DB_TYPE_STRING, DB_FIXUP_COMPOSER_SORT },
|
||||
{ "channels", mfi_offsetof(channels), DB_TYPE_INT },
|
||||
{ "usermark", mfi_offsetof(usermark), DB_TYPE_INT },
|
||||
{ "scan_kind", mfi_offsetof(scan_kind), DB_TYPE_INT },
|
||||
};
|
||||
|
||||
/* This list must be kept in sync with
|
||||
@ -252,6 +253,7 @@ static const struct col_type_map pli_cols_map[] =
|
||||
{ "query_limit", pli_offsetof(query_limit), DB_TYPE_INT },
|
||||
{ "media_kind", pli_offsetof(media_kind), DB_TYPE_INT, DB_FIXUP_MEDIA_KIND },
|
||||
{ "artwork_url", pli_offsetof(artwork_url), DB_TYPE_STRING, DB_FIXUP_NO_SANITIZE },
|
||||
{ "scan_kind", pli_offsetof(scan_kind), DB_TYPE_INT },
|
||||
|
||||
// Not in the database, but returned via the query's COUNT()/SUM()
|
||||
{ "items", pli_offsetof(items), DB_TYPE_INT, DB_FIXUP_STANDARD, DB_FLAG_NO_BIND },
|
||||
@ -367,6 +369,7 @@ static const ssize_t dbmfi_cols_map[] =
|
||||
dbmfi_offsetof(composer_sort),
|
||||
dbmfi_offsetof(channels),
|
||||
dbmfi_offsetof(usermark),
|
||||
dbmfi_offsetof(scan_kind),
|
||||
};
|
||||
|
||||
/* This list must be kept in sync with
|
||||
@ -391,6 +394,7 @@ static const ssize_t dbpli_cols_map[] =
|
||||
dbpli_offsetof(query_limit),
|
||||
dbpli_offsetof(media_kind),
|
||||
dbpli_offsetof(artwork_url),
|
||||
dbpli_offsetof(scan_kind),
|
||||
|
||||
dbpli_offsetof(items),
|
||||
dbpli_offsetof(streams),
|
||||
@ -528,14 +532,14 @@ static const struct browse_clause browse_clause[] =
|
||||
};
|
||||
|
||||
|
||||
struct media_kind_label {
|
||||
enum media_kind type;
|
||||
struct enum_label {
|
||||
int type;
|
||||
const char *label;
|
||||
};
|
||||
|
||||
|
||||
/* Keep in sync with enum media_kind */
|
||||
static const struct media_kind_label media_kind_labels[] =
|
||||
static const struct enum_label media_kind_labels[] =
|
||||
{
|
||||
{ MEDIA_KIND_MUSIC, "music" },
|
||||
{ MEDIA_KIND_MOVIE, "movie" },
|
||||
@ -590,6 +594,43 @@ db_data_kind_label(enum data_kind data_kind)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Keep in sync with enum scan_kind */
|
||||
static const struct enum_label scan_kind_labels[] =
|
||||
{
|
||||
{ SCAN_KIND_UNKNOWN, "unknown" },
|
||||
{ SCAN_KIND_FILES, "files" },
|
||||
{ SCAN_KIND_SPOTIFY, "spotify" },
|
||||
{ SCAN_KIND_RSS, "rss" },
|
||||
};
|
||||
|
||||
const char *
|
||||
db_scan_kind_label(enum scan_kind scan_kind)
|
||||
{
|
||||
if (scan_kind < ARRAY_SIZE(scan_kind_labels))
|
||||
{
|
||||
return scan_kind_labels[scan_kind].label;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum scan_kind
|
||||
db_scan_kind_enum(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!name)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(scan_kind_labels); i++)
|
||||
{
|
||||
if (strcmp(name, scan_kind_labels[i].label) == 0)
|
||||
return scan_kind_labels[i].type;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep in sync with enum pl_type */
|
||||
static char *pl_type_label[] = { "special", "folder", "smart", "plain", "rss" };
|
||||
|
||||
@ -767,6 +808,7 @@ free_di(struct directory_info *di, int content_only)
|
||||
if (!di)
|
||||
return;
|
||||
|
||||
free(di->path);
|
||||
free(di->virtual_path);
|
||||
|
||||
if (!content_only)
|
||||
@ -1675,6 +1717,59 @@ db_purge_cruft(time_t ref)
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
void
|
||||
db_purge_cruft_bysource(time_t ref, enum scan_kind scan_kind)
|
||||
{
|
||||
#define Q_TMPL "DELETE FROM directories WHERE id >= %d AND db_timestamp < %" PRIi64 " AND scan_kind = %d;"
|
||||
int i;
|
||||
int ret;
|
||||
char *query;
|
||||
char *queries_tmpl[4] =
|
||||
{
|
||||
"DELETE FROM playlistitems WHERE playlistid IN (SELECT p.id FROM playlists p WHERE p.type <> %d AND p.db_timestamp < %" PRIi64 " AND scan_kind = %d);",
|
||||
"DELETE FROM playlistitems WHERE filepath IN (SELECT f.path FROM files f WHERE -1 <> %d AND f.db_timestamp < %" PRIi64 " AND scan_kind = %d);",
|
||||
"DELETE FROM playlists WHERE type <> %d AND db_timestamp < %" PRIi64 " AND scan_kind = %d;",
|
||||
"DELETE FROM files WHERE -1 <> %d AND db_timestamp < %" PRIi64 " AND scan_kind = %d;",
|
||||
};
|
||||
|
||||
db_transaction_begin();
|
||||
|
||||
for (i = 0; i < (sizeof(queries_tmpl) / sizeof(queries_tmpl[0])); i++)
|
||||
{
|
||||
query = sqlite3_mprintf(queries_tmpl[i], PL_SPECIAL, (int64_t)ref, scan_kind);
|
||||
if (!query)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
||||
db_transaction_end();
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG, L_DB, "Running purge query '%s'\n", query);
|
||||
|
||||
ret = db_query_run(query, 1, 0);
|
||||
if (ret == 0)
|
||||
DPRINTF(E_DBG, L_DB, "Purged %d rows\n", sqlite3_changes(hdl));
|
||||
}
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL, DIR_MAX, (int64_t)ref, scan_kind);
|
||||
if (!query)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Out of memory for query string\n");
|
||||
db_transaction_end();
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG, L_DB, "Running purge query '%s'\n", query);
|
||||
|
||||
ret = db_query_run(query, 1, LISTENER_DATABASE);
|
||||
if (ret == 0)
|
||||
DPRINTF(E_DBG, L_DB, "Purged %d rows\n", sqlite3_changes(hdl));
|
||||
|
||||
db_transaction_end();
|
||||
|
||||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
void
|
||||
db_purge_all(void)
|
||||
{
|
||||
@ -4218,6 +4313,7 @@ db_directory_enum_fetch(struct directory_enum *de, struct directory_info *di)
|
||||
di->disabled = sqlite3_column_int64(de->stmt, 3);
|
||||
di->parent_id = sqlite3_column_int(de->stmt, 4);
|
||||
di->path = (char *)sqlite3_column_text(de->stmt, 5);
|
||||
di->scan_kind = sqlite3_column_int(de->stmt, 6);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4232,11 +4328,11 @@ db_directory_enum_end(struct directory_enum *de)
|
||||
de->stmt = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
db_directory_add(struct directory_info *di, int *id)
|
||||
{
|
||||
#define QADD_TMPL "INSERT INTO directories (virtual_path, db_timestamp, disabled, parent_id, path)" \
|
||||
" VALUES (TRIM(%Q), %d, %" PRIi64 ", %d, TRIM(%Q));"
|
||||
#define QADD_TMPL "INSERT INTO directories (virtual_path, db_timestamp, disabled, parent_id, path, scan_kind)" \
|
||||
" VALUES (TRIM(%Q), %d, %" PRIi64 ", %d, TRIM(%Q), %d);"
|
||||
|
||||
char *query;
|
||||
char *errmsg;
|
||||
@ -4251,7 +4347,7 @@ db_directory_add(struct directory_info *di, int *id)
|
||||
DPRINTF(E_LOG, L_DB, "Directory name ends with space: '%s'\n", di->virtual_path);
|
||||
}
|
||||
|
||||
query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id, di->path);
|
||||
query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id, di->path, di->scan_kind);
|
||||
|
||||
if (!query)
|
||||
{
|
||||
@ -4287,17 +4383,17 @@ db_directory_add(struct directory_info *di, int *id)
|
||||
#undef QADD_TMPL
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
db_directory_update(struct directory_info *di)
|
||||
{
|
||||
#define QADD_TMPL "UPDATE directories SET virtual_path = TRIM(%Q), db_timestamp = %d, disabled = %" PRIi64 ", parent_id = %d, path = TRIM(%Q)" \
|
||||
#define QADD_TMPL "UPDATE directories SET virtual_path = TRIM(%Q), db_timestamp = %d, disabled = %" PRIi64 ", parent_id = %d, path = TRIM(%Q), scan_kind = %d" \
|
||||
" WHERE id = %d;"
|
||||
char *query;
|
||||
char *errmsg;
|
||||
int ret;
|
||||
|
||||
/* Add */
|
||||
query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id, di->path, di->id);
|
||||
query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id, di->path, di->scan_kind, di->id);
|
||||
|
||||
if (!query)
|
||||
{
|
||||
@ -4326,36 +4422,6 @@ db_directory_update(struct directory_info *di)
|
||||
#undef QADD_TMPL
|
||||
}
|
||||
|
||||
int
|
||||
db_directory_addorupdate(char *virtual_path, char *path, int disabled, int parent_id)
|
||||
{
|
||||
struct directory_info di;
|
||||
int id;
|
||||
int ret;
|
||||
|
||||
id = db_directory_id_byvirtualpath(virtual_path);
|
||||
|
||||
di.id = id;
|
||||
di.parent_id = parent_id;
|
||||
di.virtual_path = virtual_path;
|
||||
di.path = path;
|
||||
di.disabled = disabled;
|
||||
di.db_timestamp = (uint64_t)time(NULL);
|
||||
|
||||
if (di.id == 0)
|
||||
ret = db_directory_add(&di, &id);
|
||||
else
|
||||
ret = db_directory_update(&di);
|
||||
|
||||
if (ret < 0 || id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Insert or update of directory failed '%s'\n", virtual_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
db_directory_ping_bymatch(char *virtual_path)
|
||||
{
|
||||
@ -4369,7 +4435,7 @@ db_directory_ping_bymatch(char *virtual_path)
|
||||
}
|
||||
|
||||
void
|
||||
db_directory_disable_bymatch(char *path, enum strip_type strip, uint32_t cookie)
|
||||
db_directory_disable_bymatch(const char *path, enum strip_type strip, uint32_t cookie)
|
||||
{
|
||||
#define Q_TMPL "UPDATE directories SET virtual_path = substr(virtual_path, %d)," \
|
||||
" disabled = %" PRIi64 " WHERE virtual_path = '/file:%q' OR virtual_path LIKE '/file:%q/%%';"
|
||||
@ -4388,7 +4454,7 @@ db_directory_disable_bymatch(char *path, enum strip_type strip, uint32_t cookie)
|
||||
}
|
||||
|
||||
int
|
||||
db_directory_enable_bycookie(uint32_t cookie, char *path)
|
||||
db_directory_enable_bycookie(uint32_t cookie, const char *path)
|
||||
{
|
||||
#define Q_TMPL "UPDATE directories SET virtual_path = ('/file:%q' || virtual_path)," \
|
||||
" disabled = 0 WHERE disabled = %" PRIi64 ";"
|
||||
|
30
src/db.h
30
src/db.h
@ -141,6 +141,18 @@ enum data_kind {
|
||||
const char *
|
||||
db_data_kind_label(enum data_kind data_kind);
|
||||
|
||||
enum scan_kind {
|
||||
SCAN_KIND_UNKNOWN = 0,
|
||||
SCAN_KIND_FILES = 1,
|
||||
SCAN_KIND_SPOTIFY = 2,
|
||||
SCAN_KIND_RSS = 3,
|
||||
};
|
||||
|
||||
const char *
|
||||
db_scan_kind_label(enum scan_kind scan_kind);
|
||||
|
||||
enum scan_kind
|
||||
db_scan_kind_enum(const char *label);
|
||||
|
||||
/* Indicates user marked status on a track - values can be bitwise enumerated */
|
||||
enum usermark {
|
||||
@ -234,6 +246,8 @@ struct media_file_info {
|
||||
char *album_sort;
|
||||
char *album_artist_sort;
|
||||
char *composer_sort;
|
||||
|
||||
uint32_t scan_kind; /* Identifies the library_source that created/updates this item */
|
||||
};
|
||||
|
||||
#define mfi_offsetof(field) offsetof(struct media_file_info, field)
|
||||
@ -269,6 +283,7 @@ struct playlist_info {
|
||||
uint32_t query_limit; /* limit, used by e.g. smart playlists */
|
||||
uint32_t media_kind;
|
||||
char *artwork_url; /* optional artwork */
|
||||
uint32_t scan_kind; /* Identifies the library_source that created/updates this item */
|
||||
uint32_t items; /* number of items (mimc) */
|
||||
uint32_t streams; /* number of internet streams */
|
||||
};
|
||||
@ -292,6 +307,7 @@ struct db_playlist_info {
|
||||
char *query_limit;
|
||||
char *media_kind;
|
||||
char *artwork_url;
|
||||
char *scan_kind;
|
||||
char *items;
|
||||
char *streams;
|
||||
};
|
||||
@ -405,6 +421,7 @@ struct db_media_file_info {
|
||||
char *composer_sort;
|
||||
char *channels;
|
||||
char *usermark;
|
||||
char *scan_kind;
|
||||
};
|
||||
|
||||
#define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field)
|
||||
@ -475,6 +492,7 @@ struct directory_info {
|
||||
uint32_t db_timestamp;
|
||||
int64_t disabled;
|
||||
uint32_t parent_id;
|
||||
uint32_t scan_kind; /* Identifies the library_source that created/updates this item */
|
||||
};
|
||||
|
||||
struct directory_enum {
|
||||
@ -589,6 +607,9 @@ db_hook_post_scan(void);
|
||||
void
|
||||
db_purge_cruft(time_t ref);
|
||||
|
||||
void
|
||||
db_purge_cruft_bysource(time_t ref, enum scan_kind scan_kind);
|
||||
|
||||
void
|
||||
db_purge_all(void);
|
||||
|
||||
@ -798,16 +819,19 @@ void
|
||||
db_directory_enum_end(struct directory_enum *de);
|
||||
|
||||
int
|
||||
db_directory_addorupdate(char *virtual_path, char *path, int disabled, int parent_id);
|
||||
db_directory_add(struct directory_info *di, int *id);
|
||||
|
||||
int
|
||||
db_directory_update(struct directory_info *di);
|
||||
|
||||
void
|
||||
db_directory_ping_bymatch(char *virtual_path);
|
||||
|
||||
void
|
||||
db_directory_disable_bymatch(char *path, enum strip_type strip, uint32_t cookie);
|
||||
db_directory_disable_bymatch(const char *path, enum strip_type strip, uint32_t cookie);
|
||||
|
||||
int
|
||||
db_directory_enable_bycookie(uint32_t cookie, char *path);
|
||||
db_directory_enable_bycookie(uint32_t cookie, const char *path);
|
||||
|
||||
int
|
||||
db_directory_enable_bypath(char *path);
|
||||
|
@ -97,7 +97,8 @@
|
||||
" album_artist_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" composer_sort VARCHAR(1024) DEFAULT NULL COLLATE DAAP," \
|
||||
" channels INTEGER DEFAULT 0," \
|
||||
" usermark INTEGER DEFAULT 0" \
|
||||
" usermark INTEGER DEFAULT 0," \
|
||||
" scan_kind INTEGER DEFAULT 0" \
|
||||
");"
|
||||
|
||||
#define T_PL \
|
||||
@ -117,7 +118,8 @@
|
||||
" query_order VARCHAR(1024)," \
|
||||
" query_limit INTEGER DEFAULT 0," \
|
||||
" media_kind INTEGER DEFAULT 1," \
|
||||
" artwork_url VARCHAR(4096) DEFAULT NULL" \
|
||||
" artwork_url VARCHAR(4096) DEFAULT NULL," \
|
||||
" scan_kind INTEGER DEFAULT 0" \
|
||||
");"
|
||||
|
||||
#define T_PLITEMS \
|
||||
@ -166,7 +168,8 @@
|
||||
" db_timestamp INTEGER DEFAULT 0," \
|
||||
" disabled INTEGER DEFAULT 0," \
|
||||
" parent_id INTEGER DEFAULT 0," \
|
||||
" path VARCHAR(4096) DEFAULT NULL" \
|
||||
" path VARCHAR(4096) DEFAULT NULL," \
|
||||
" scan_kind INTEGER DEFAULT 0" \
|
||||
");"
|
||||
|
||||
#define T_QUEUE \
|
||||
|
@ -25,8 +25,8 @@
|
||||
* 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
|
||||
* the server after the database was upgraded. */
|
||||
#define SCHEMA_VERSION_MAJOR 21
|
||||
#define SCHEMA_VERSION_MINOR 07
|
||||
#define SCHEMA_VERSION_MAJOR 22
|
||||
#define SCHEMA_VERSION_MINOR 0
|
||||
|
||||
int
|
||||
db_init_indices(sqlite3 *hdl);
|
||||
|
@ -1174,6 +1174,59 @@ static const struct db_upgrade_query db_upgrade_v2107_queries[] =
|
||||
};
|
||||
|
||||
|
||||
/* ---------------------------- 21.07 -> 22.00 ------------------------------ */
|
||||
|
||||
#define U_v2200_ALTER_FILES_ADD_SCAN_KIND \
|
||||
"ALTER TABLE files ADD COLUMN scan_kind INTEGER DEFAULT 0;"
|
||||
#define U_v2200_ALTER_PLAYLISTS_ADD_SCAN_KIND \
|
||||
"ALTER TABLE playlists ADD COLUMN scan_kind INTEGER DEFAULT 0;"
|
||||
#define U_v2200_ALTER_DIR_ADD_SCAN_KIND \
|
||||
"ALTER TABLE directories ADD COLUMN scan_kind INTEGER DEFAULT 0;"
|
||||
|
||||
#define U_v2200_FILES_SET_SCAN_KIND_RSS \
|
||||
"UPDATE files SET scan_kind = 3 WHERE path in (" \
|
||||
" SELECT i.filepath from playlists p, playlistitems i WHERE p.id = i.playlistid AND p.type = 4);"
|
||||
#define U_v2200_FILES_SET_SCAN_KIND_SPOTIFY \
|
||||
"UPDATE files SET scan_kind = 2 WHERE virtual_path like '/spotify:/%';"
|
||||
#define U_v2200_FILES_SET_SOURCE_FILE_SCANNER \
|
||||
"UPDATE files SET scan_kind = 1 WHERE scan_kind = 0;"
|
||||
|
||||
#define U_v2200_PL_SET_SCAN_KIND_RSS \
|
||||
"UPDATE playlists SET scan_kind = 3 WHERE type = 4;" // PL_RSS = 4
|
||||
#define U_v2200_PL_SET_SCAN_KIND_SPOTIFY \
|
||||
"UPDATE playlists SET scan_kind = 2 WHERE virtual_path like '/spotify:/%';"
|
||||
#define U_v2200_PL_SET_SCAN_KIND_FILES \
|
||||
"UPDATE playlists SET scan_kind = 1 WHERE scan_kind = 0;"
|
||||
|
||||
// Note: RSS feed items do not have their own directory structure (they use "http:/")
|
||||
#define U_v2200_DIR_SET_SCAN_KIND_SPOTIFY \
|
||||
"UPDATE directories SET scan_kind = 2 WHERE virtual_path like '/spotify:/%';"
|
||||
#define U_v2200_DIR_SET_SCAN_KIND_FILES \
|
||||
"UPDATE directories SET scan_kind = 1 WHERE virtual_path like '/file:/%';"
|
||||
|
||||
#define U_v2200_SCVER_MAJOR \
|
||||
"UPDATE admin SET value = '22' WHERE key = 'schema_version_major';"
|
||||
#define U_v2200_SCVER_MINOR \
|
||||
"UPDATE admin SET value = '00' WHERE key = 'schema_version_minor';"
|
||||
|
||||
static const struct db_upgrade_query db_upgrade_v2200_queries[] =
|
||||
{
|
||||
{ U_v2200_ALTER_FILES_ADD_SCAN_KIND, "alter table files add column scan_kind" },
|
||||
{ U_v2200_ALTER_PLAYLISTS_ADD_SCAN_KIND, "alter table playlists add column scan_kind" },
|
||||
{ U_v2200_ALTER_DIR_ADD_SCAN_KIND, "alter table directories add column scan_kind" },
|
||||
{ U_v2200_FILES_SET_SCAN_KIND_RSS, "update table files set scan_kind rss" },
|
||||
{ U_v2200_FILES_SET_SCAN_KIND_SPOTIFY, "update table files set scan_kind spotify" },
|
||||
{ U_v2200_FILES_SET_SOURCE_FILE_SCANNER, "update table files set scan_kind files" },
|
||||
{ U_v2200_PL_SET_SCAN_KIND_RSS, "update table playlists set scan_kind rss" },
|
||||
{ U_v2200_PL_SET_SCAN_KIND_SPOTIFY, "update table playlists set scan_kind spotify" },
|
||||
{ U_v2200_PL_SET_SCAN_KIND_FILES, "update table playlists set scan_kind files" },
|
||||
{ U_v2200_DIR_SET_SCAN_KIND_SPOTIFY, "update table directories set scan_kind spotify" },
|
||||
{ U_v2200_DIR_SET_SCAN_KIND_FILES , "update table directories set scan_kind files" },
|
||||
|
||||
{ U_v2200_SCVER_MAJOR, "set schema_version_major to 22" },
|
||||
{ U_v2200_SCVER_MINOR, "set schema_version_minor to 00" },
|
||||
};
|
||||
|
||||
/* -------------------------- Main upgrade handler -------------------------- */
|
||||
|
||||
int
|
||||
@ -1377,6 +1430,13 @@ db_upgrade(sqlite3 *hdl, int db_ver)
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case 2107:
|
||||
ret = db_generic_upgrade(hdl, db_upgrade_v2200_queries, ARRAY_SIZE(db_upgrade_v2200_queries));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
/* Last case statement is the only one that ends with a break statement! */
|
||||
break;
|
||||
|
@ -1181,6 +1181,10 @@ jsonapi_reply_library(struct httpd_request *hreq)
|
||||
json_object *jreply;
|
||||
int ret;
|
||||
char *s;
|
||||
int i;
|
||||
struct library_source **sources;
|
||||
json_object *jscanners;
|
||||
json_object *jsource;
|
||||
|
||||
|
||||
CHECK_NULL(L_WEB, jreply = json_object_new_object());
|
||||
@ -1216,6 +1220,19 @@ jsonapi_reply_library(struct httpd_request *hreq)
|
||||
|
||||
json_object_object_add(jreply, "updating", json_object_new_boolean(library_is_scanning()));
|
||||
|
||||
jscanners = json_object_new_array();
|
||||
json_object_object_add(jreply, "scanners", jscanners);
|
||||
sources = library_sources();
|
||||
for (i = 0; sources[i]; i++)
|
||||
{
|
||||
if (!sources[i]->disabled)
|
||||
{
|
||||
jsource = json_object_new_object();
|
||||
safe_json_add_string(jsource, "name", db_scan_kind_label(sources[i]->scan_kind));
|
||||
json_object_array_add(jscanners, jsource);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply)));
|
||||
jparse_free(jreply);
|
||||
|
||||
@ -1228,14 +1245,22 @@ jsonapi_reply_library(struct httpd_request *hreq)
|
||||
static int
|
||||
jsonapi_reply_update(struct httpd_request *hreq)
|
||||
{
|
||||
library_rescan();
|
||||
const char *param;
|
||||
|
||||
param = evhttp_find_header(hreq->query, "scan_kind");
|
||||
|
||||
library_rescan(db_scan_kind_enum(param));
|
||||
return HTTP_NOCONTENT;
|
||||
}
|
||||
|
||||
static int
|
||||
jsonapi_reply_meta_rescan(struct httpd_request *hreq)
|
||||
{
|
||||
library_metarescan();
|
||||
const char *param;
|
||||
|
||||
param = evhttp_find_header(hreq->query, "scan_kind");
|
||||
|
||||
library_metarescan(db_scan_kind_enum(param));
|
||||
return HTTP_NOCONTENT;
|
||||
}
|
||||
|
||||
|
154
src/library.c
154
src/library.c
@ -122,10 +122,10 @@ static struct library_callback_register library_cb_register[LIBRARY_MAX_CALLBACK
|
||||
int
|
||||
library_media_save(struct media_file_info *mfi)
|
||||
{
|
||||
if (!mfi->path || !mfi->fname)
|
||||
if (!mfi->path || !mfi->fname || !mfi->scan_kind)
|
||||
{
|
||||
DPRINTF(E_LOG, L_LIB, "Ignoring media file with missing values (path='%s', fname='%s', data_kind='%d')\n",
|
||||
mfi->path, mfi->fname, mfi->data_kind);
|
||||
DPRINTF(E_LOG, L_LIB, "Ignoring media file with missing values (path='%s', fname='%s', scan_kind='%d', data_kind='%d')\n",
|
||||
mfi->path, mfi->fname, mfi->scan_kind, mfi->data_kind);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -145,9 +145,10 @@ library_media_save(struct media_file_info *mfi)
|
||||
int
|
||||
library_playlist_save(struct playlist_info *pli)
|
||||
{
|
||||
if (!pli->path)
|
||||
if (!pli->path || !pli->scan_kind)
|
||||
{
|
||||
DPRINTF(E_LOG, L_LIB, "Ignoring playlist file with missing path\n");
|
||||
DPRINTF(E_LOG, L_LIB, "Ignoring playlist with missing values (path='%s', scan_kind='%d')\n",
|
||||
pli->path, pli->scan_kind);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -164,6 +165,39 @@ library_playlist_save(struct playlist_info *pli)
|
||||
return db_pl_update(pli);
|
||||
}
|
||||
|
||||
int
|
||||
library_directory_save(char *virtual_path, char *path, int disabled, int parent_id, enum scan_kind scan_kind)
|
||||
{
|
||||
struct directory_info di = { 0 };
|
||||
int id;
|
||||
int ret;
|
||||
|
||||
id = db_directory_id_byvirtualpath(virtual_path);
|
||||
|
||||
di.id = id;
|
||||
di.parent_id = parent_id;
|
||||
di.virtual_path = safe_strdup(virtual_path);
|
||||
di.path = safe_strdup(path);
|
||||
di.disabled = disabled;
|
||||
di.db_timestamp = (uint64_t)time(NULL);
|
||||
di.scan_kind = scan_kind;
|
||||
|
||||
if (di.id == 0)
|
||||
ret = db_directory_add(&di, &id);
|
||||
else
|
||||
ret = db_directory_update(&di);
|
||||
|
||||
free_di(&di, 1);
|
||||
|
||||
if (ret < 0 || id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_DB, "Insert or update of directory failed '%s'\n", virtual_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void
|
||||
scheduled_cb(int fd, short what, void *arg)
|
||||
{
|
||||
@ -259,20 +293,27 @@ handle_deferred_update_notifications(void)
|
||||
}
|
||||
|
||||
static void
|
||||
purge_cruft(time_t start)
|
||||
purge_cruft(time_t start, enum scan_kind scan_kind)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Purging old library content\n");
|
||||
db_purge_cruft(start);
|
||||
if (scan_kind > 0)
|
||||
db_purge_cruft_bysource(start, scan_kind);
|
||||
else
|
||||
db_purge_cruft(start);
|
||||
db_groups_cleanup();
|
||||
db_queue_cleanup();
|
||||
|
||||
DPRINTF(E_DBG, L_LIB, "Purging old artwork content\n");
|
||||
cache_artwork_purge_cruft(start);
|
||||
if (scan_kind <= 0)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Purging old artwork content\n");
|
||||
cache_artwork_purge_cruft(start);
|
||||
}
|
||||
}
|
||||
|
||||
static enum command_state
|
||||
rescan(void *arg, int *ret)
|
||||
{
|
||||
enum scan_kind *scan_kind;
|
||||
time_t starttime;
|
||||
time_t endtime;
|
||||
int i;
|
||||
@ -281,20 +322,29 @@ rescan(void *arg, int *ret)
|
||||
listener_notify(LISTENER_UPDATE);
|
||||
starttime = time(NULL);
|
||||
|
||||
scan_kind = arg;
|
||||
|
||||
for (i = 0; sources[i]; i++)
|
||||
{
|
||||
if (!sources[i]->disabled && sources[i]->rescan)
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Rescan library source '%s'\n", sources[i]->name);
|
||||
sources[i]->rescan();
|
||||
if (*scan_kind > 0 && *scan_kind != sources[i]->scan_kind)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Skipping library source '%s'\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Rescan library source '%s'\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
sources[i]->rescan();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Library source '%s' is disabled\n", sources[i]->name);
|
||||
DPRINTF(E_INFO, L_LIB, "Library source '%s' is disabled\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
}
|
||||
}
|
||||
|
||||
purge_cruft(starttime);
|
||||
purge_cruft(starttime, *scan_kind);
|
||||
|
||||
DPRINTF(E_DBG, L_LIB, "Running post library scan jobs\n");
|
||||
db_hook_post_scan();
|
||||
@ -315,6 +365,7 @@ rescan(void *arg, int *ret)
|
||||
static enum command_state
|
||||
metarescan(void *arg, int *ret)
|
||||
{
|
||||
enum scan_kind *scan_kind;
|
||||
time_t starttime;
|
||||
time_t endtime;
|
||||
int i;
|
||||
@ -323,20 +374,29 @@ metarescan(void *arg, int *ret)
|
||||
listener_notify(LISTENER_UPDATE);
|
||||
starttime = time(NULL);
|
||||
|
||||
scan_kind = arg;
|
||||
|
||||
for (i = 0; sources[i]; i++)
|
||||
{
|
||||
if (!sources[i]->disabled && sources[i]->metarescan)
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Meta rescan library source '%s'\n", sources[i]->name);
|
||||
sources[i]->metarescan();
|
||||
if (*scan_kind > 0 && *scan_kind != sources[i]->scan_kind)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Skipping library source '%s'\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Meta rescan library source '%s'\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
sources[i]->metarescan();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Library source '%s' is disabled\n", sources[i]->name);
|
||||
DPRINTF(E_INFO, L_LIB, "Library source '%s' is disabled\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
}
|
||||
}
|
||||
|
||||
purge_cruft(starttime);
|
||||
purge_cruft(starttime, *scan_kind);
|
||||
|
||||
DPRINTF(E_DBG, L_LIB, "Running post library scan jobs\n");
|
||||
db_hook_post_scan();
|
||||
@ -374,12 +434,12 @@ fullrescan(void *arg, int *ret)
|
||||
{
|
||||
if (!sources[i]->disabled && sources[i]->fullrescan)
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Full-rescan library source '%s'\n", sources[i]->name);
|
||||
DPRINTF(E_INFO, L_LIB, "Full-rescan library source '%s'\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
sources[i]->fullrescan();
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Library source '%s' is disabled\n", sources[i]->name);
|
||||
DPRINTF(E_INFO, L_LIB, "Library source '%s' is disabled\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,7 +469,7 @@ playlist_item_add(void *arg, int *retval)
|
||||
{
|
||||
if (sources[i]->disabled || !sources[i]->playlist_item_add)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_item_add\n", sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_item_add\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -417,7 +477,7 @@ playlist_item_add(void *arg, int *retval)
|
||||
|
||||
if (ret == LIBRARY_OK)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Adding item '%s' to playlist '%s' with library source '%s'\n", param->vp_item, param->vp_playlist, sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Adding item '%s' to playlist '%s' with library source '%s'\n", param->vp_item, param->vp_playlist, db_scan_kind_label(sources[i]->scan_kind));
|
||||
listener_notify(LISTENER_STORED_PLAYLIST);
|
||||
break;
|
||||
}
|
||||
@ -440,7 +500,7 @@ playlist_remove(void *arg, int *retval)
|
||||
{
|
||||
if (sources[i]->disabled || !sources[i]->playlist_remove)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_remove\n", sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_remove\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -448,7 +508,7 @@ playlist_remove(void *arg, int *retval)
|
||||
|
||||
if (ret == LIBRARY_OK)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Removing playlist '%s' with library source '%s'\n", virtual_path, sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Removing playlist '%s' with library source '%s'\n", virtual_path, db_scan_kind_label(sources[i]->scan_kind));
|
||||
listener_notify(LISTENER_STORED_PLAYLIST);
|
||||
break;
|
||||
}
|
||||
@ -472,7 +532,7 @@ queue_item_add(void *arg, int *retval)
|
||||
{
|
||||
if (sources[i]->disabled || !sources[i]->queue_item_add)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_add\n", sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_add\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -480,7 +540,7 @@ queue_item_add(void *arg, int *retval)
|
||||
|
||||
if (ret == LIBRARY_OK)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Items for path '%s' from library source '%s' added to the queue\n", param->path, sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Items for path '%s' from library source '%s' added to the queue\n", param->path, db_scan_kind_label(sources[i]->scan_kind));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -505,7 +565,7 @@ queue_save(void *arg, int *retval)
|
||||
{
|
||||
if (sources[i]->disabled || !sources[i]->queue_save)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_save\n", sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_save\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -513,7 +573,7 @@ queue_save(void *arg, int *retval)
|
||||
|
||||
if (ret == LIBRARY_OK)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Saving queue to path '%s' with library source '%s'\n", virtual_path, sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Saving queue to path '%s' with library source '%s'\n", virtual_path, db_scan_kind_label(sources[i]->scan_kind));
|
||||
listener_notify(LISTENER_STORED_PLAYLIST);
|
||||
break;
|
||||
}
|
||||
@ -536,7 +596,7 @@ item_add(void *arg, int *retval)
|
||||
{
|
||||
if (sources[i]->disabled || !sources[i]->item_add)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support add_item\n", sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support add_item\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -544,7 +604,7 @@ item_add(void *arg, int *retval)
|
||||
|
||||
if (ret == LIBRARY_OK)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Add item to path '%s' with library source '%s'\n", path, sources[i]->name);
|
||||
DPRINTF(E_DBG, L_LIB, "Add item to path '%s' with library source '%s'\n", path, db_scan_kind_label(sources[i]->scan_kind));
|
||||
listener_notify(LISTENER_DATABASE);
|
||||
break;
|
||||
}
|
||||
@ -597,29 +657,39 @@ update_trigger(void *arg, int *retval)
|
||||
/* ----------------------- LIBRARY EXTERNAL INTERFACE ---------------------- */
|
||||
|
||||
void
|
||||
library_rescan()
|
||||
library_rescan(enum scan_kind scan_kind)
|
||||
{
|
||||
int *param;
|
||||
|
||||
if (scanning)
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Scan already running, ignoring request to trigger a new init scan\n");
|
||||
return;
|
||||
}
|
||||
|
||||
scanning = true; // TODO Guard "scanning" with a mutex
|
||||
commands_exec_async(cmdbase, rescan, NULL);
|
||||
scanning = true;
|
||||
param = malloc(sizeof(int));
|
||||
*param = scan_kind;
|
||||
|
||||
commands_exec_async(cmdbase, rescan, param);
|
||||
}
|
||||
|
||||
void
|
||||
library_metarescan()
|
||||
library_metarescan(enum scan_kind scan_kind)
|
||||
{
|
||||
int *param;
|
||||
|
||||
if (scanning)
|
||||
{
|
||||
DPRINTF(E_INFO, L_LIB, "Scan already running, ignoring request to trigger metadata scan\n");
|
||||
return;
|
||||
}
|
||||
|
||||
scanning = true; // TODO Guard "scanning" with a mutex
|
||||
commands_exec_async(cmdbase, metarescan, NULL);
|
||||
scanning = true;
|
||||
param = malloc(sizeof(int));
|
||||
*param = scan_kind;
|
||||
|
||||
commands_exec_async(cmdbase, metarescan, param);
|
||||
}
|
||||
|
||||
void
|
||||
@ -631,7 +701,7 @@ library_fullrescan()
|
||||
return;
|
||||
}
|
||||
|
||||
scanning = true; // TODO Guard "scanning" with a mutex
|
||||
scanning = true;
|
||||
commands_exec_async(cmdbase, fullrescan, NULL);
|
||||
}
|
||||
|
||||
@ -662,7 +732,7 @@ initscan()
|
||||
|
||||
if (! (cfg_getbool(cfg_getsec(cfg, "library"), "filescan_disable")))
|
||||
{
|
||||
purge_cruft(starttime);
|
||||
purge_cruft(starttime, 0);
|
||||
|
||||
DPRINTF(E_DBG, L_LIB, "Running post library scan jobs\n");
|
||||
db_hook_post_scan();
|
||||
@ -794,11 +864,17 @@ library_item_add(const char *path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
scanning = true; // TODO Guard "scanning" with a mutex
|
||||
scanning = true;
|
||||
|
||||
return commands_exec_sync(cmdbase, item_add, NULL, (char *)path);
|
||||
}
|
||||
|
||||
struct library_source **
|
||||
library_sources(void)
|
||||
{
|
||||
return sources;
|
||||
}
|
||||
|
||||
int
|
||||
library_exec_async(command_function func, void *arg)
|
||||
{
|
||||
@ -863,7 +939,7 @@ library_init(void)
|
||||
{
|
||||
if (!sources[i]->initscan || !sources[i]->rescan || !sources[i]->metarescan || !sources[i]->fullrescan)
|
||||
{
|
||||
DPRINTF(E_FATAL, L_LIB, "BUG: library source '%s' is missing a scanning method\n", sources[i]->name);
|
||||
DPRINTF(E_FATAL, L_LIB, "BUG: library source '%s' is missing a scanning method\n", db_scan_kind_label(sources[i]->scan_kind));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ enum library_cb_action
|
||||
*/
|
||||
struct library_source
|
||||
{
|
||||
char *name;
|
||||
enum scan_kind scan_kind;
|
||||
int disabled;
|
||||
|
||||
/*
|
||||
@ -134,6 +134,9 @@ library_media_save(struct media_file_info *mfi);
|
||||
int
|
||||
library_playlist_save(struct playlist_info *pli);
|
||||
|
||||
int
|
||||
library_directory_save(char *virtual_path, char *path, int disabled, int parent_id, enum scan_kind library_source);
|
||||
|
||||
/*
|
||||
* @param cb Callback to call
|
||||
* @param arg Argument to call back with
|
||||
@ -153,12 +156,27 @@ library_is_exiting();
|
||||
|
||||
/* ------------------------ Library external interface --------------------- */
|
||||
|
||||
/*
|
||||
* Rescan library: find new, remove deleted and update modified tracks and playlists
|
||||
* If a "source_name" is given, only tracks / playlists belonging to that source are
|
||||
* updated.
|
||||
*
|
||||
* Update is done asynchronously in the library thread.
|
||||
*
|
||||
* @param library_source 0 to update everything, one of LIBRARY_SOURCE_xxx to only update specific source
|
||||
*/
|
||||
void
|
||||
library_rescan();
|
||||
library_rescan(enum scan_kind library_source);
|
||||
|
||||
/*
|
||||
* Same as library_rescan but also updates unmodified tracks and playlists
|
||||
*/
|
||||
void
|
||||
library_metarescan();
|
||||
library_metarescan(enum scan_kind library_source);
|
||||
|
||||
/*
|
||||
* Wipe library and do a full rescan of all library sources
|
||||
*/
|
||||
void
|
||||
library_fullrescan();
|
||||
|
||||
@ -201,6 +219,8 @@ library_queue_item_add(const char *path, int position, char reshuffle, uint32_t
|
||||
int
|
||||
library_item_add(const char *path);
|
||||
|
||||
struct library_source **
|
||||
library_sources(void);
|
||||
|
||||
/*
|
||||
* Execute the function 'func' with the given argument 'arg' in the library thread.
|
||||
|
@ -438,6 +438,7 @@ playlist_fill(struct playlist_info *pli, const char *path)
|
||||
pli->path = strdup(path);
|
||||
pli->title = strip_extension(filename); // Will alloc
|
||||
pli->virtual_path = strip_extension(virtual_path); // Will alloc
|
||||
pli->scan_kind = SCAN_KIND_FILES;
|
||||
|
||||
pli->directory_id = get_parent_dir_id(path);
|
||||
|
||||
@ -586,6 +587,7 @@ process_regular_file(const char *file, struct stat *sb, int type, int flags, int
|
||||
mfi.virtual_path = strdup(virtual_path);
|
||||
|
||||
mfi.directory_id = dir_id;
|
||||
mfi.scan_kind = SCAN_KIND_FILES;
|
||||
|
||||
if (S_ISFIFO(sb->st_mode))
|
||||
{
|
||||
@ -699,7 +701,7 @@ process_file(char *file, struct stat *sb, enum file_type file_type, int scan_typ
|
||||
|
||||
DPRINTF(E_LOG, L_SCAN, "Startup rescan triggered, found init-rescan file: %s\n", file);
|
||||
|
||||
library_rescan();
|
||||
library_rescan(0);
|
||||
break;
|
||||
|
||||
case FILE_CTRL_METASCAN:
|
||||
@ -708,7 +710,7 @@ process_file(char *file, struct stat *sb, enum file_type file_type, int scan_typ
|
||||
|
||||
DPRINTF(E_LOG, L_SCAN, "Meta rescan triggered, found meta-rescan file: %s\n", file);
|
||||
|
||||
library_metarescan();
|
||||
library_metarescan(0);
|
||||
break;
|
||||
|
||||
case FILE_CTRL_FULLSCAN:
|
||||
@ -828,7 +830,7 @@ process_directory(char *path, int parent_id, int flags)
|
||||
return;
|
||||
}
|
||||
|
||||
dir_id = db_directory_addorupdate(virtual_path, path, 0, parent_id);
|
||||
dir_id = library_directory_save(virtual_path, path, 0, parent_id, SCAN_KIND_FILES);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SCAN, "Insert or update of directory failed '%s'\n", virtual_path);
|
||||
@ -956,7 +958,7 @@ process_parent_directories(char *path)
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
dir_id = db_directory_addorupdate(virtual_path, buf, 0, dir_id);
|
||||
dir_id = library_directory_save(virtual_path, buf, 0, dir_id, SCAN_KIND_FILES);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SCAN, "Insert or update of directory failed '%s'\n", virtual_path);
|
||||
@ -2154,7 +2156,7 @@ filescanner_deinit(void)
|
||||
|
||||
struct library_source filescanner =
|
||||
{
|
||||
.name = "filescanner",
|
||||
.scan_kind = SCAN_KIND_FILES,
|
||||
.disabled = 0,
|
||||
.init = filescanner_init,
|
||||
.deinit = filescanner_deinit,
|
||||
|
@ -159,6 +159,7 @@ scan_metadata_stream(struct media_file_info *mfi, const char *path)
|
||||
mfi->data_kind = DATA_KIND_HTTP;
|
||||
mfi->time_modified = time(NULL);
|
||||
mfi->directory_id = DIR_HTTP;
|
||||
mfi->scan_kind = SCAN_KIND_FILES;
|
||||
|
||||
ret = scan_metadata_ffmpeg(mfi, path);
|
||||
if (ret < 0)
|
||||
@ -186,6 +187,7 @@ process_nested_playlist(int parent_id, const char *path)
|
||||
goto error;
|
||||
|
||||
pli->type = PL_FOLDER;
|
||||
pli->scan_kind = SCAN_KIND_FILES;
|
||||
ret = library_playlist_save(pli);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
@ -217,6 +217,7 @@ playlist_fetch(bool *is_new, const char *path)
|
||||
pli->directory_id = DIR_HTTP;
|
||||
pli->type = PL_RSS;
|
||||
pli->query_limit = RSS_LIMIT_DEFAULT;
|
||||
pli->scan_kind = SCAN_KIND_RSS;
|
||||
|
||||
ret = library_playlist_save(pli);
|
||||
if (ret < 0)
|
||||
@ -487,6 +488,7 @@ rss_save(struct playlist_info *pli, int *count, enum rss_scan_type scan_type)
|
||||
}
|
||||
|
||||
scan_metadata_stream(&mfi, ri.url);
|
||||
mfi.scan_kind = SCAN_KIND_RSS;
|
||||
|
||||
mfi_metadata_fixup(&mfi, &ri, feed_title, feed_author, time_added);
|
||||
|
||||
@ -641,7 +643,7 @@ rss_add(const char *path)
|
||||
|
||||
struct library_source rssscanner =
|
||||
{
|
||||
.name = "RSS feeds",
|
||||
.scan_kind = SCAN_KIND_RSS,
|
||||
.disabled = 0,
|
||||
.initscan = rss_rescan,
|
||||
.rescan = rss_rescan,
|
||||
|
@ -1465,7 +1465,7 @@ prepare_directories(const char *artist, const char *album)
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Virtual path exceeds PATH_MAX (/spotify:/%s)\n", artist);
|
||||
return -1;
|
||||
}
|
||||
dir_id = db_directory_addorupdate(virtual_path, NULL, 0, DIR_SPOTIFY);
|
||||
dir_id = library_directory_save(virtual_path, NULL, 0, DIR_SPOTIFY, SCAN_KIND_SPOTIFY);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not add or update directory '%s'\n", virtual_path);
|
||||
@ -1477,7 +1477,7 @@ prepare_directories(const char *artist, const char *album)
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Virtual path exceeds PATH_MAX (/spotify:/%s/%s)\n", artist, album);
|
||||
return -1;
|
||||
}
|
||||
dir_id = db_directory_addorupdate(virtual_path, NULL, 0, dir_id);
|
||||
dir_id = library_directory_save(virtual_path, NULL, 0, dir_id, SCAN_KIND_SPOTIFY);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not add or update directory '%s'\n", virtual_path);
|
||||
@ -1580,6 +1580,7 @@ map_track_to_mfi(struct media_file_info *mfi, const struct spotify_track *track,
|
||||
}
|
||||
snprintf(virtual_path, PATH_MAX, "/spotify:/%s/%s/%s", mfi->album_artist, mfi->album, mfi->title);
|
||||
mfi->virtual_path = strdup(virtual_path);
|
||||
mfi->scan_kind = SCAN_KIND_SPOTIFY;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1873,8 +1874,9 @@ map_playlist_to_pli(struct playlist_info *pli, struct spotify_playlist *playlist
|
||||
pli->path = strdup(playlist->uri);
|
||||
pli->title = safe_strdup(playlist->name);
|
||||
|
||||
pli->parent_id = spotify_base_plid;
|
||||
pli->directory_id = DIR_SPOTIFY;
|
||||
pli->parent_id = spotify_base_plid;
|
||||
pli->directory_id = DIR_SPOTIFY;
|
||||
pli->scan_kind = SCAN_KIND_SPOTIFY;
|
||||
|
||||
if (playlist->owner)
|
||||
pli->virtual_path = safe_asprintf("/spotify:/%s (%s)", playlist->name, playlist->owner);
|
||||
@ -1945,6 +1947,7 @@ create_saved_tracks_playlist(void)
|
||||
.type = PL_PLAIN,
|
||||
.parent_id = spotify_base_plid,
|
||||
.directory_id = DIR_SPOTIFY,
|
||||
.scan_kind = SCAN_KIND_SPOTIFY,
|
||||
};
|
||||
|
||||
spotify_saved_plid = playlist_add_or_update(&pli);
|
||||
@ -1969,6 +1972,7 @@ create_base_playlist(void)
|
||||
.path = strdup("spotify:playlistfolder"),
|
||||
.title = strdup("Spotify"),
|
||||
.type = PL_FOLDER,
|
||||
.scan_kind = SCAN_KIND_SPOTIFY,
|
||||
};
|
||||
|
||||
spotify_base_plid = 0;
|
||||
@ -2330,7 +2334,7 @@ spotifywebapi_deinit()
|
||||
|
||||
struct library_source spotifyscanner =
|
||||
{
|
||||
.name = "spotifyscanner",
|
||||
.scan_kind = SCAN_KIND_SPOTIFY,
|
||||
.disabled = 0,
|
||||
.init = spotifywebapi_init,
|
||||
.deinit = spotifywebapi_deinit,
|
||||
|
Loading…
Reference in New Issue
Block a user