mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 15:45:56 -05:00
[scan] Support for nested playlists
Also includes some refactoring of the playlist filescanner and some rearrangement of filescanner.c functions.
This commit is contained in:
parent
ba1c536ca3
commit
46a9114948
@ -165,57 +165,47 @@ static int
|
|||||||
filescanner_fullrescan();
|
filescanner_fullrescan();
|
||||||
|
|
||||||
|
|
||||||
const char *
|
/* ----------------------- Internal utility functions --------------------- */
|
||||||
filename_from_path(const char *path)
|
|
||||||
|
static int
|
||||||
|
virtual_path_make(char *virtual_path, int virtual_path_len, const char *path)
|
||||||
{
|
{
|
||||||
const char *filename;
|
int ret;
|
||||||
|
|
||||||
filename = strrchr(path, '/');
|
ret = snprintf(virtual_path, virtual_path_len, "/file:%s", path);
|
||||||
if ((!filename) || (strlen(filename) == 1))
|
if ((ret < 0) || (ret >= virtual_path_len))
|
||||||
filename = path;
|
|
||||||
else
|
|
||||||
filename++;
|
|
||||||
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
strip_extension(const char *path)
|
|
||||||
{
|
{
|
||||||
char *ptr;
|
DPRINTF(E_LOG, L_SCAN, "Virtual path '/file:%s', virtual_path_len exceeded (%d/%d)\n", path, ret, virtual_path_len);
|
||||||
char *result;
|
|
||||||
|
|
||||||
result = strdup(path);
|
|
||||||
ptr = strrchr(result, '.');
|
|
||||||
if (ptr)
|
|
||||||
*ptr = '\0';
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
parent_dir(const char **current, const char *path)
|
|
||||||
{
|
|
||||||
const char *ptr;
|
|
||||||
|
|
||||||
if (*current)
|
|
||||||
ptr = *current;
|
|
||||||
else
|
|
||||||
ptr = strrchr(path, '/');
|
|
||||||
|
|
||||||
if (!ptr || (ptr == path))
|
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
for (ptr--; (ptr > path) && (*ptr != '/'); ptr--)
|
|
||||||
;
|
|
||||||
|
|
||||||
*current = ptr;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
push_dir(struct stacked_dir **s, char *path, int parent_id)
|
get_parent_dir_id(const char *path)
|
||||||
|
{
|
||||||
|
char *pathcopy;
|
||||||
|
char *parent_dir;
|
||||||
|
char virtual_path[PATH_MAX];
|
||||||
|
int parent_id;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pathcopy = strdup(path);
|
||||||
|
parent_dir = dirname(pathcopy);
|
||||||
|
ret = virtual_path_make(virtual_path, sizeof(virtual_path), parent_dir);
|
||||||
|
if (ret == 0)
|
||||||
|
parent_id = db_directory_id_byvirtualpath(virtual_path);
|
||||||
|
else
|
||||||
|
parent_id = 0;
|
||||||
|
|
||||||
|
free(pathcopy);
|
||||||
|
|
||||||
|
return parent_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
push_dir(struct stacked_dir **s, const char *path, int parent_id)
|
||||||
{
|
{
|
||||||
struct stacked_dir *d;
|
struct stacked_dir *d;
|
||||||
|
|
||||||
@ -386,6 +376,104 @@ file_type_get(const char *path) {
|
|||||||
return FILE_REGULAR;
|
return FILE_REGULAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------- Utility functions used by the scanners --------------- */
|
||||||
|
|
||||||
|
const char *
|
||||||
|
filename_from_path(const char *path)
|
||||||
|
{
|
||||||
|
const char *filename;
|
||||||
|
|
||||||
|
filename = strrchr(path, '/');
|
||||||
|
if ((!filename) || (strlen(filename) == 1))
|
||||||
|
filename = path;
|
||||||
|
else
|
||||||
|
filename++;
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
strip_extension(const char *path)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
result = strdup(path);
|
||||||
|
ptr = strrchr(result, '.');
|
||||||
|
if (ptr)
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parent_dir(const char **current, const char *path)
|
||||||
|
{
|
||||||
|
const char *ptr;
|
||||||
|
|
||||||
|
if (*current)
|
||||||
|
ptr = *current;
|
||||||
|
else
|
||||||
|
ptr = strrchr(path, '/');
|
||||||
|
|
||||||
|
if (!ptr || (ptr == path))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (ptr--; (ptr > path) && (*ptr != '/'); ptr--)
|
||||||
|
;
|
||||||
|
|
||||||
|
*current = ptr;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
playlist_fill(struct playlist_info *pli, const char *path)
|
||||||
|
{
|
||||||
|
const char *filename;
|
||||||
|
char virtual_path[PATH_MAX];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
filename = filename_from_path(path);
|
||||||
|
|
||||||
|
ret = virtual_path_make(virtual_path, sizeof(virtual_path), path);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(pli, 0, sizeof(struct playlist_info));
|
||||||
|
|
||||||
|
pli->type = PL_PLAIN;
|
||||||
|
pli->path = strdup(path);
|
||||||
|
pli->title = strip_extension(filename); // Will alloc
|
||||||
|
pli->virtual_path = strip_extension(virtual_path); // Will alloc
|
||||||
|
|
||||||
|
pli->directory_id = get_parent_dir_id(path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
playlist_add(const char *path)
|
||||||
|
{
|
||||||
|
struct playlist_info pli;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = playlist_fill(&pli, path);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = library_playlist_save(&pli);
|
||||||
|
free_pli(&pli, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return db_pl_id_bypath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------- Processing procedures ---------------------- */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_playlist(char *file, time_t mtime, int dir_id)
|
process_playlist(char *file, time_t mtime, int dir_id)
|
||||||
{
|
{
|
||||||
@ -530,7 +618,7 @@ process_regular_file(const char *file, struct stat *sb, int type, int flags, int
|
|||||||
mfi.album_artist = safe_strdup(cfg_getstr(cfg_getsec(cfg, "library"), "compilation_artist"));
|
mfi.album_artist = safe_strdup(cfg_getstr(cfg_getsec(cfg, "library"), "compilation_artist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = scan_metadata_ffmpeg(file, &mfi);
|
ret = scan_metadata_ffmpeg(&mfi, file);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
free_mfi(&mfi, 1);
|
free_mfi(&mfi, 1);
|
||||||
@ -674,22 +762,6 @@ check_speciallib(char *path, const char *libtype)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Thread: scan */
|
|
||||||
static int
|
|
||||||
virtual_path_make(char *virtual_path, int virtual_path_len, const char *path)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = snprintf(virtual_path, virtual_path_len, "/file:%s", path);
|
|
||||||
if ((ret < 0) || (ret >= virtual_path_len))
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SCAN, "Virtual path '/file:%s', virtual_path_len exceeded (%d/%d)\n", path, ret, virtual_path_len);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns informations about the attributes of the file at the given 'path' in the structure
|
* Returns informations about the attributes of the file at the given 'path' in the structure
|
||||||
* pointed to by 'sb'.
|
* pointed to by 'sb'.
|
||||||
@ -1018,28 +1090,6 @@ bulk_scan(int flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
get_parent_dir_id(const char *path)
|
|
||||||
{
|
|
||||||
char *pathcopy;
|
|
||||||
char *parent_dir;
|
|
||||||
char virtual_path[PATH_MAX];
|
|
||||||
int parent_id;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
pathcopy = strdup(path);
|
|
||||||
parent_dir = dirname(pathcopy);
|
|
||||||
ret = virtual_path_make(virtual_path, sizeof(virtual_path), parent_dir);
|
|
||||||
if (ret == 0)
|
|
||||||
parent_id = db_directory_id_byvirtualpath(virtual_path);
|
|
||||||
else
|
|
||||||
parent_id = 0;
|
|
||||||
|
|
||||||
free(pathcopy);
|
|
||||||
|
|
||||||
return parent_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
watches_clear(uint32_t wd, char *path)
|
watches_clear(uint32_t wd, char *path)
|
||||||
{
|
{
|
||||||
@ -1724,7 +1774,7 @@ queue_item_stream_add(const char *path, int position, char reshuffle, uint32_t 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(&mfi, path);
|
||||||
|
|
||||||
map_media_file_to_queue_item(&item, &mfi);
|
map_media_file_to_queue_item(&item, &mfi);
|
||||||
|
|
||||||
@ -1924,7 +1974,7 @@ playlist_add_files(FILE *fp, int pl_id, const char *virtual_path)
|
|||||||
DPRINTF(E_DBG, L_SCAN, "Scan stream '%s' and add to playlist (id = %d)\n", path, pl_id);
|
DPRINTF(E_DBG, L_SCAN, "Scan stream '%s' and add to playlist (id = %d)\n", path, pl_id);
|
||||||
|
|
||||||
memset(&mfi, 0, sizeof(struct media_file_info));
|
memset(&mfi, 0, sizeof(struct media_file_info));
|
||||||
scan_metadata_stream(path, &mfi);
|
scan_metadata_stream(&mfi, path);
|
||||||
library_media_save(&mfi);
|
library_media_save(&mfi);
|
||||||
free_mfi(&mfi, 1);
|
free_mfi(&mfi, 1);
|
||||||
|
|
||||||
@ -1942,51 +1992,6 @@ playlist_add_files(FILE *fp, int pl_id, const char *virtual_path)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fills a basic pli template based on a path. Caller can then modify content
|
|
||||||
// before saving the playlist.
|
|
||||||
int
|
|
||||||
playlist_template_fill(struct playlist_info *pli, const char *path)
|
|
||||||
{
|
|
||||||
const char *filename;
|
|
||||||
char virtual_path[PATH_MAX];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
filename = filename_from_path(path);
|
|
||||||
|
|
||||||
memset(pli, 0, sizeof(struct playlist_info));
|
|
||||||
|
|
||||||
pli->type = PL_PLAIN;
|
|
||||||
pli->path = strdup(path);
|
|
||||||
pli->title = strip_extension(filename); // Will alloc
|
|
||||||
|
|
||||||
ret = virtual_path_make(virtual_path, sizeof(virtual_path), path);
|
|
||||||
if (ret < 0)
|
|
||||||
return -1;
|
|
||||||
pli->virtual_path = strip_extension(virtual_path); // Will alloc
|
|
||||||
|
|
||||||
pli->directory_id = get_parent_dir_id(path);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
playlist_create(const char *path)
|
|
||||||
{
|
|
||||||
struct playlist_info pli;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = playlist_template_fill(&pli, path);
|
|
||||||
if (ret < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ret = library_playlist_save(&pli);
|
|
||||||
free_pli(&pli, 1);
|
|
||||||
if (ret < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return db_pl_id_bypath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
playlist_item_add(const char *vp_playlist, const char *vp_item)
|
playlist_item_add(const char *vp_playlist, const char *vp_item)
|
||||||
{
|
{
|
||||||
@ -2009,7 +2014,7 @@ playlist_item_add(const char *vp_playlist, const char *vp_item)
|
|||||||
pl_id = db_pl_id_bypath(pl_path);
|
pl_id = db_pl_id_bypath(pl_path);
|
||||||
if (pl_id < 0)
|
if (pl_id < 0)
|
||||||
{
|
{
|
||||||
pl_id = playlist_create(pl_path);
|
pl_id = playlist_add(pl_path);
|
||||||
if (pl_id < 0)
|
if (pl_id < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -2098,7 +2103,7 @@ queue_save(const char *virtual_path)
|
|||||||
pl_id = db_pl_id_bypath(pl_path);
|
pl_id = db_pl_id_bypath(pl_path);
|
||||||
if (pl_id < 0)
|
if (pl_id < 0)
|
||||||
{
|
{
|
||||||
pl_id = playlist_create(pl_path);
|
pl_id = playlist_add(pl_path);
|
||||||
if (pl_id < 0)
|
if (pl_id < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -2127,7 +2132,7 @@ queue_save(const char *virtual_path)
|
|||||||
DPRINTF(E_DBG, L_SCAN, "Scan stream '%s' and add to playlist (id = %d)\n", queue_item.path, pl_id);
|
DPRINTF(E_DBG, L_SCAN, "Scan stream '%s' and add to playlist (id = %d)\n", queue_item.path, pl_id);
|
||||||
|
|
||||||
memset(&mfi, 0, sizeof(struct media_file_info));
|
memset(&mfi, 0, sizeof(struct media_file_info));
|
||||||
scan_metadata_stream(queue_item.path, &mfi);
|
scan_metadata_stream(&mfi, queue_item.path);
|
||||||
library_media_save(&mfi);
|
library_media_save(&mfi);
|
||||||
free_mfi(&mfi, 1);
|
free_mfi(&mfi, 1);
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
/* --------------------------- Actual scanners ---------------------------- */
|
/* --------------------------- Actual scanners ---------------------------- */
|
||||||
|
|
||||||
int
|
int
|
||||||
scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi);
|
scan_metadata_ffmpeg(struct media_file_info *mfi, const char *file);
|
||||||
|
|
||||||
void
|
void
|
||||||
scan_metadata_stream(const char *path, struct media_file_info *mfi);
|
scan_metadata_stream(struct media_file_info *mfi, const char *path);
|
||||||
|
|
||||||
void
|
void
|
||||||
scan_playlist(const char *file, time_t mtime, int dir_id);
|
scan_playlist(const char *file, time_t mtime, int dir_id);
|
||||||
@ -60,4 +60,23 @@ strip_extension(const char *path);
|
|||||||
int
|
int
|
||||||
parent_dir(const char **current, const char *path);
|
parent_dir(const char **current, const char *path);
|
||||||
|
|
||||||
|
/* Fills a playlist struct with default values based on path. The title will
|
||||||
|
* for instance be set to the base filename without file extension. Since
|
||||||
|
* the fields in the struct are alloc'ed, caller must free with free_pli().
|
||||||
|
*
|
||||||
|
* @out pli the playlist struct to be filled
|
||||||
|
* @in path the path to the playlist
|
||||||
|
* @return 0 if ok, negative on error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
playlist_fill(struct playlist_info *pli, const char *path);
|
||||||
|
|
||||||
|
/* Adds a playlist to the database with the fields set by playlist_fill()
|
||||||
|
*
|
||||||
|
* @in path the path to the playlist
|
||||||
|
* @return the id of the playlist (pli->id), negative on error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
playlist_add(const char *path);
|
||||||
|
|
||||||
#endif /* !__FILESCANNER_H__ */
|
#endif /* !__FILESCANNER_H__ */
|
||||||
|
@ -354,7 +354,7 @@ extract_metadata(struct media_file_info *mfi, AVFormatContext *ctx, AVStream *au
|
|||||||
* - fname: (filename) used as fallback for artist
|
* - fname: (filename) used as fallback for artist
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
scan_metadata_ffmpeg(const char *file, struct media_file_info *mfi)
|
scan_metadata_ffmpeg(struct media_file_info *mfi, const char *file)
|
||||||
{
|
{
|
||||||
AVFormatContext *ctx;
|
AVFormatContext *ctx;
|
||||||
AVDictionary *options;
|
AVDictionary *options;
|
||||||
|
@ -38,11 +38,32 @@
|
|||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
|
|
||||||
/* Formats we can read so far */
|
// Formats we can read so far
|
||||||
#define PLAYLIST_PLS 1
|
enum playlist_type
|
||||||
#define PLAYLIST_M3U 2
|
{
|
||||||
|
PLAYLIST_UNKNOWN = 0,
|
||||||
|
PLAYLIST_PLS,
|
||||||
|
PLAYLIST_M3U,
|
||||||
|
};
|
||||||
|
|
||||||
/* Get metadata from the EXTINF tag */
|
static enum playlist_type
|
||||||
|
playlist_type(const char *path)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
ptr = strrchr(path, '.');
|
||||||
|
if (!ptr)
|
||||||
|
return PLAYLIST_UNKNOWN;
|
||||||
|
|
||||||
|
if (strcasecmp(ptr, ".m3u") == 0)
|
||||||
|
return PLAYLIST_M3U;
|
||||||
|
else if (strcasecmp(ptr, ".pls") == 0)
|
||||||
|
return PLAYLIST_PLS;
|
||||||
|
else
|
||||||
|
return PLAYLIST_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get metadata from the EXTINF tag
|
||||||
static int
|
static int
|
||||||
extinf_get(char *string, struct media_file_info *mfi, int *extinf)
|
extinf_get(char *string, struct media_file_info *mfi, int *extinf)
|
||||||
{
|
{
|
||||||
@ -55,7 +76,7 @@ extinf_get(char *string, struct media_file_info *mfi, int *extinf)
|
|||||||
if (!ptr || strlen(ptr) < 2)
|
if (!ptr || strlen(ptr) < 2)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* New extinf found, so clear old data */
|
// New extinf found, so clear old data
|
||||||
free_mfi(mfi, 1);
|
free_mfi(mfi, 1);
|
||||||
|
|
||||||
*extinf = 1;
|
*extinf = 1;
|
||||||
@ -73,7 +94,7 @@ extinf_get(char *string, struct media_file_info *mfi, int *extinf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scan_metadata_stream(const char *path, struct media_file_info *mfi)
|
scan_metadata_stream(struct media_file_info *mfi, const char *path)
|
||||||
{
|
{
|
||||||
char *pos;
|
char *pos;
|
||||||
int ret;
|
int ret;
|
||||||
@ -91,7 +112,7 @@ scan_metadata_stream(const char *path, struct media_file_info *mfi)
|
|||||||
mfi->time_modified = time(NULL);
|
mfi->time_modified = time(NULL);
|
||||||
mfi->directory_id = DIR_HTTP;
|
mfi->directory_id = DIR_HTTP;
|
||||||
|
|
||||||
ret = scan_metadata_ffmpeg(path, mfi);
|
ret = scan_metadata_ffmpeg(mfi, path);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_SCAN, "Playlist URL '%s' is unavailable for probe/metadata, assuming MP3 encoding\n", path);
|
DPRINTF(E_LOG, L_SCAN, "Playlist URL '%s' is unavailable for probe/metadata, assuming MP3 encoding\n", path);
|
||||||
@ -104,12 +125,64 @@ scan_metadata_stream(const char *path, struct media_file_info *mfi)
|
|||||||
mfi->title = strdup(mfi->fname);
|
mfi->title = strdup(mfi->fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_nested_playlist(int parent_id, const char *path)
|
||||||
|
{
|
||||||
|
struct playlist_info *pli;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// First set the type of the parent playlist to folder
|
||||||
|
pli = db_pl_fetch_byid(parent_id);
|
||||||
|
if (!pli)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
pli->type = PL_FOLDER;
|
||||||
|
ret = db_pl_update(pli);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
free_pli(pli, 0);
|
||||||
|
|
||||||
|
// Do we already have the playlist in the database?
|
||||||
|
pli = db_pl_fetch_bypath(path);
|
||||||
|
if (!pli)
|
||||||
|
{
|
||||||
|
pli = calloc(1, sizeof(struct playlist_info));
|
||||||
|
ret = playlist_fill(pli, path);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pli->parent_id = parent_id;
|
||||||
|
|
||||||
|
ret = library_playlist_save(pli);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
free_pli(pli, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
error:
|
||||||
|
DPRINTF(E_LOG, L_SCAN, "Error processing nested playlist '%s' in playlist %d\n", path, parent_id);
|
||||||
|
free_pli(pli, 0);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_url(int pl_id, const char *path, struct media_file_info *mfi)
|
process_url(int pl_id, const char *path, struct media_file_info *mfi)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
mfi->id = db_file_id_bypath(path);
|
mfi->id = db_file_id_bypath(path);
|
||||||
scan_metadata_stream(path, mfi);
|
|
||||||
library_media_save(mfi);
|
scan_metadata_stream(mfi, path);
|
||||||
|
|
||||||
|
ret = library_media_save(mfi);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return db_pl_add_item_bypath(pl_id, path);
|
return db_pl_add_item_bypath(pl_id, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,42 +271,28 @@ process_regular_file(int pl_id, char *path)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static int
|
||||||
scan_playlist(const char *file, time_t mtime, int dir_id)
|
playlist_prepare(const char *path, time_t mtime)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
|
||||||
struct media_file_info mfi;
|
|
||||||
struct playlist_info *pli;
|
struct playlist_info *pli;
|
||||||
struct stat sb;
|
|
||||||
char buf[PATH_MAX];
|
|
||||||
char *path;
|
|
||||||
const char *filename;
|
|
||||||
char *ptr;
|
|
||||||
size_t len;
|
|
||||||
int extinf;
|
|
||||||
int pl_id;
|
int pl_id;
|
||||||
int pl_format;
|
|
||||||
int ntracks;
|
|
||||||
int nadded;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ptr = strrchr(file, '.');
|
pli = db_pl_fetch_bypath(path);
|
||||||
if (!ptr)
|
if (!pli)
|
||||||
return;
|
|
||||||
|
|
||||||
if (strcasecmp(ptr, ".m3u") == 0)
|
|
||||||
pl_format = PLAYLIST_M3U;
|
|
||||||
else if (strcasecmp(ptr, ".pls") == 0)
|
|
||||||
pl_format = PLAYLIST_PLS;
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
|
|
||||||
filename = filename_from_path(file);
|
|
||||||
|
|
||||||
/* Fetch or create playlist */
|
|
||||||
pli = db_pl_fetch_bypath(file);
|
|
||||||
if (pli)
|
|
||||||
{
|
{
|
||||||
|
DPRINTF(E_LOG, L_SCAN, "New playlist found, processing '%s'\n", path);
|
||||||
|
|
||||||
|
pl_id = playlist_add(path);
|
||||||
|
if (pl_id < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_SCAN, "Error adding playlist '%s'\n", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(E_INFO, L_SCAN, "Added new playlist as id %d\n", pl_id);
|
||||||
|
return pl_id;
|
||||||
|
}
|
||||||
|
|
||||||
db_pl_ping(pli->id);
|
db_pl_ping(pli->id);
|
||||||
|
|
||||||
// mtime == db_timestamp is also treated as a modification because some editors do
|
// mtime == db_timestamp is also treated as a modification because some editors do
|
||||||
@ -242,50 +301,49 @@ scan_playlist(const char *file, time_t mtime, int dir_id)
|
|||||||
// is equal to the newly updated db_timestamp)
|
// is equal to the newly updated db_timestamp)
|
||||||
if (mtime && (pli->db_timestamp > mtime))
|
if (mtime && (pli->db_timestamp > mtime))
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_SCAN, "Unchanged playlist found, not processing '%s'\n", file);
|
DPRINTF(E_LOG, L_SCAN, "Unchanged playlist found, not processing '%s'\n", path);
|
||||||
|
|
||||||
// Protect this playlist's radio stations from purge after scan
|
// Protect this playlist's radio stations from purge after scan
|
||||||
db_pl_ping_items_bymatch("http://", pli->id);
|
db_pl_ping_items_bymatch("http://", pli->id);
|
||||||
db_pl_ping_items_bymatch("https://", pli->id);
|
db_pl_ping_items_bymatch("https://", pli->id);
|
||||||
free_pli(pli, 0);
|
free_pli(pli, 0);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_LOG, L_SCAN, "Modified playlist found, processing '%s'\n", file);
|
DPRINTF(E_LOG, L_SCAN, "Modified playlist found, processing '%s'\n", path);
|
||||||
|
|
||||||
pl_id = pli->id;
|
pl_id = pli->id;
|
||||||
|
free_pli(pli, 0);
|
||||||
|
|
||||||
db_pl_clear_items(pl_id);
|
db_pl_clear_items(pl_id);
|
||||||
|
|
||||||
|
return pl_id;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
void
|
||||||
|
scan_playlist(const char *file, time_t mtime, int dir_id)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_SCAN, "New playlist found, processing '%s'\n", file);
|
FILE *fp;
|
||||||
|
struct media_file_info mfi;
|
||||||
|
struct stat sb;
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
char *path;
|
||||||
|
size_t len;
|
||||||
|
int extinf;
|
||||||
|
int pl_id;
|
||||||
|
int pl_format;
|
||||||
|
int ntracks;
|
||||||
|
int nadded;
|
||||||
|
int ret;
|
||||||
|
|
||||||
CHECK_NULL(L_SCAN, pli = calloc(1, sizeof(struct playlist_info)));
|
pl_format = playlist_type(file);
|
||||||
|
if (pl_format == PLAYLIST_UNKNOWN)
|
||||||
pli->type = PL_PLAIN;
|
|
||||||
|
|
||||||
/* Get only the basename, to be used as the playlist title */
|
|
||||||
pli->title = strip_extension(filename);
|
|
||||||
|
|
||||||
pli->path = strdup(file);
|
|
||||||
snprintf(buf, sizeof(buf), "/file:%s", file);
|
|
||||||
pli->virtual_path = strip_extension(buf);
|
|
||||||
|
|
||||||
pli->directory_id = dir_id;
|
|
||||||
|
|
||||||
ret = db_pl_add(pli, &pl_id);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SCAN, "Error adding playlist '%s'\n", file);
|
|
||||||
|
|
||||||
free_pli(pli, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF(E_INFO, L_SCAN, "Added new playlist as id %d\n", pl_id);
|
// Will create or update the playlist entry in the database
|
||||||
}
|
pl_id = playlist_prepare(file, mtime);
|
||||||
|
if (pl_id < 0)
|
||||||
free_pli(pli, 0);
|
return; // Not necessarily an error, could also be that the playlist hasn't changed
|
||||||
|
|
||||||
ret = stat(file, &sb);
|
ret = stat(file, &sb);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -312,7 +370,7 @@ scan_playlist(const char *file, time_t mtime, int dir_id)
|
|||||||
{
|
{
|
||||||
len = strlen(buf);
|
len = strlen(buf);
|
||||||
|
|
||||||
/* rtrim and check that length is sane (ignore blank lines) */
|
// rtrim and check that length is sane (ignore blank lines)
|
||||||
while ((len > 0) && isspace(buf[len - 1]))
|
while ((len > 0) && isspace(buf[len - 1]))
|
||||||
{
|
{
|
||||||
len--;
|
len--;
|
||||||
@ -321,11 +379,11 @@ scan_playlist(const char *file, time_t mtime, int dir_id)
|
|||||||
if (len < 1)
|
if (len < 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Saves metadata in mfi if EXTINF metadata line */
|
// Saves metadata in mfi if EXTINF metadata line
|
||||||
if ((pl_format == PLAYLIST_M3U) && extinf_get(buf, &mfi, &extinf))
|
if ((pl_format == PLAYLIST_M3U) && extinf_get(buf, &mfi, &extinf))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* For pls files we are only interested in the part after the FileX= entry */
|
// For pls files we are only interested in the part after the FileX= entry
|
||||||
path = NULL;
|
path = NULL;
|
||||||
if ((pl_format == PLAYLIST_PLS) && (strncasecmp(buf, "file", strlen("file")) == 0) && (path = strchr(buf, '=')))
|
if ((pl_format == PLAYLIST_PLS) && (strncasecmp(buf, "file", strlen("file")) == 0) && (path = strchr(buf, '=')))
|
||||||
path++;
|
path++;
|
||||||
@ -335,12 +393,14 @@ scan_playlist(const char *file, time_t mtime, int dir_id)
|
|||||||
if (!path)
|
if (!path)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Check that first char is sane for a path */
|
// Check that first char is sane for a path
|
||||||
if ((!isalnum(path[0])) && (path[0] != '/') && (path[0] != '.'))
|
if ((!isalnum(path[0])) && (path[0] != '/') && (path[0] != '.'))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Check if line is an URL, will be added to library, otherwise it should already be there */
|
// URLs and playlists will be added to library, tracks should already be there
|
||||||
if (strncasecmp(path, "http://", 7) == 0 || strncasecmp(path, "https://", 8) == 0)
|
if (playlist_type(path) != PLAYLIST_UNKNOWN)
|
||||||
|
ret = process_nested_playlist(pl_id, path);
|
||||||
|
else if (strncasecmp(path, "http://", 7) == 0 || strncasecmp(path, "https://", 8) == 0)
|
||||||
ret = process_url(pl_id, path, &mfi);
|
ret = process_url(pl_id, path, &mfi);
|
||||||
else
|
else
|
||||||
ret = process_regular_file(pl_id, path);
|
ret = process_regular_file(pl_id, path);
|
||||||
@ -356,14 +416,14 @@ scan_playlist(const char *file, time_t mtime, int dir_id)
|
|||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
nadded++;
|
nadded++;
|
||||||
|
|
||||||
/* Clean up in preparation for next item */
|
// Clean up in preparation for next item
|
||||||
extinf = 0;
|
extinf = 0;
|
||||||
free_mfi(&mfi, 1);
|
free_mfi(&mfi, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
db_transaction_end();
|
db_transaction_end();
|
||||||
|
|
||||||
/* We had some extinf that we never got to use, free it now */
|
// We had some extinf that we never got to use, free it now
|
||||||
if (extinf)
|
if (extinf)
|
||||||
free_mfi(&mfi, 1);
|
free_mfi(&mfi, 1);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user