mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-13 16:03:23 -05:00
commit
c4c60488de
269
sqlext/sqlext.c
269
sqlext/sqlext.c
@ -244,257 +244,6 @@ sqlext_daap_unicode_xcollation(void *notused, int llen, const void *left, int rl
|
||||
return rpp;
|
||||
}
|
||||
|
||||
|
||||
/* Taken from "extension-functions.c" by Liam Healy (2010-02-06 15:45:07)
|
||||
http://www.sqlite.org/contrib/download/extension-functions.c?get=25 */
|
||||
/* LMH from sqlite3 3.3.13 */
|
||||
/*
|
||||
** This table maps from the first byte of a UTF-8 character to the number
|
||||
** of trailing bytes expected. A value '4' indicates that the table key
|
||||
** is not a legal first byte for a UTF-8 character.
|
||||
*/
|
||||
static const uint8_t xtra_utf8_bytes[256] = {
|
||||
/* 0xxxxxxx */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
/* 10wwwwww */
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
|
||||
/* 110yyyyy */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
|
||||
/* 1110zzzz */
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
|
||||
/* 11110yyy */
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** This table maps from the number of trailing bytes in a UTF-8 character
|
||||
** to an integer constant that is effectively calculated for each character
|
||||
** read by a naive implementation of a UTF-8 character reader. The code
|
||||
** in the READ_UTF8 macro explains things best.
|
||||
*/
|
||||
static const int xtra_utf8_bits[] = {
|
||||
0,
|
||||
12416, /* (0xC0 << 6) + (0x80) */
|
||||
925824, /* (0xE0 << 12) + (0x80 << 6) + (0x80) */
|
||||
63447168 /* (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */
|
||||
};
|
||||
|
||||
/*
|
||||
** If a UTF-8 character contains N bytes extra bytes (N bytes follow
|
||||
** the initial byte so that the total character length is N+1) then
|
||||
** masking the character with utf8_mask[N] must produce a non-zero
|
||||
** result. Otherwise, we have an (illegal) overlong encoding.
|
||||
*/
|
||||
static const int utf_mask[] = {
|
||||
0x00000000,
|
||||
0xffffff80,
|
||||
0xfffff800,
|
||||
0xffff0000,
|
||||
};
|
||||
|
||||
/* LMH salvaged from sqlite3 3.3.13 source code src/utf.c */
|
||||
#define READ_UTF8(zIn, c) { \
|
||||
int xtra; \
|
||||
c = *(zIn)++; \
|
||||
xtra = xtra_utf8_bytes[c]; \
|
||||
switch( xtra ){ \
|
||||
case 4: c = (int)0xFFFD; break; \
|
||||
case 3: c = (c<<6) + *(zIn)++; \
|
||||
case 2: c = (c<<6) + *(zIn)++; \
|
||||
case 1: c = (c<<6) + *(zIn)++; \
|
||||
c -= xtra_utf8_bits[xtra]; \
|
||||
if( (utf_mask[xtra]&c)==0 \
|
||||
|| (c&0xFFFFF800)==0xD800 \
|
||||
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
|
||||
} \
|
||||
}
|
||||
|
||||
static int sqlite3ReadUtf8(const unsigned char *z)
|
||||
{
|
||||
int c;
|
||||
READ_UTF8(z, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* X is a pointer to the first byte of a UTF-8 character. Increment
|
||||
* X so that it points to the next character. This only works right
|
||||
* if X points to a well-formed UTF-8 string.
|
||||
*/
|
||||
#define sqliteNextChar(X) while( (0xc0&*++(X))==0x80 ){}
|
||||
#define sqliteCharVal(X) sqlite3ReadUtf8(X)
|
||||
|
||||
/*
|
||||
* Given a string z1, retutns the (0 based) index of it's first occurence
|
||||
* in z2 after the first s characters.
|
||||
* Returns -1 when there isn't a match.
|
||||
* updates p to point to the character where the match occured.
|
||||
* This is an auxiliary function.
|
||||
*/
|
||||
static int _substr(const char* z1, const char* z2, int s, const char** p)
|
||||
{
|
||||
int c = 0;
|
||||
int rVal = -1;
|
||||
const char* zt1;
|
||||
const char* zt2;
|
||||
int c1, c2;
|
||||
|
||||
if ('\0' == *z1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((sqliteCharVal((unsigned char *)z2) != 0) && (c++) < s)
|
||||
{
|
||||
sqliteNextChar(z2);
|
||||
}
|
||||
|
||||
c = 0;
|
||||
while ((sqliteCharVal((unsigned char * )z2)) != 0)
|
||||
{
|
||||
zt1 = z1;
|
||||
zt2 = z2;
|
||||
|
||||
do
|
||||
{
|
||||
c1 = sqliteCharVal((unsigned char * )zt1);
|
||||
c2 = sqliteCharVal((unsigned char * )zt2);
|
||||
sqliteNextChar(zt1);
|
||||
sqliteNextChar(zt2);
|
||||
} while (c1 == c2 && c1 != 0 && c2 != 0);
|
||||
|
||||
if (c1 == 0)
|
||||
{
|
||||
rVal = c;
|
||||
break;
|
||||
}
|
||||
|
||||
sqliteNextChar(z2);
|
||||
++c;
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
*p = z2;
|
||||
}
|
||||
return rVal >= 0 ? rVal + s : rVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from "extension-functions.c" (function charindexFunc) by Liam Healy (2010-02-06 15:45:07)
|
||||
* http://www.sqlite.org/contrib/download/extension-functions.c?get=25
|
||||
*
|
||||
* Given 2 input strings (s1,s2) and an integer (n) searches from the nth character
|
||||
* for the string s1. Returns the position where the match occured.
|
||||
* Characters are counted from 1.
|
||||
* 0 is returned when no match occurs.
|
||||
*/
|
||||
static void sqlext_daap_charindex_xfunc(sqlite3_context *context, int argc, sqlite3_value **argv)
|
||||
{
|
||||
const uint8_t *z1; /* s1 string */
|
||||
uint8_t *z2; /* s2 string */
|
||||
int s = 0;
|
||||
int rVal = 0;
|
||||
|
||||
//assert(argc == 3 || argc == 2);
|
||||
if (argc != 2 && argc != 3)
|
||||
{
|
||||
sqlite3_result_error(context, "daap_charindex() requires 2 or 3 parameters", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( SQLITE_NULL == sqlite3_value_type(argv[0]) || SQLITE_NULL == sqlite3_value_type(argv[1]))
|
||||
{
|
||||
sqlite3_result_null(context);
|
||||
return;
|
||||
}
|
||||
|
||||
z1 = sqlite3_value_text(argv[0]);
|
||||
if (z1 == 0)
|
||||
return;
|
||||
z2 = (uint8_t*) sqlite3_value_text(argv[1]);
|
||||
if (argc == 3)
|
||||
{
|
||||
s = sqlite3_value_int(argv[2]) - 1;
|
||||
if (s < 0)
|
||||
{
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s = 0;
|
||||
}
|
||||
|
||||
rVal = _substr((char *) z1, (char *) z2, s, NULL);
|
||||
sqlite3_result_int(context, rVal + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from "extension-functions.c" (function leftFunc) by Liam Healy (2010-02-06 15:45:07)
|
||||
* http://www.sqlite.org/contrib/download/extension-functions.c?get=25
|
||||
*
|
||||
* Given a string (s) and an integer (n) returns the n leftmost (UTF-8) characters
|
||||
* if the string has a length<=n or is NULL this function is NOP
|
||||
*/
|
||||
static void sqlext_daap_leftstr_xfunc(sqlite3_context *context, int argc, sqlite3_value **argv)
|
||||
{
|
||||
int c = 0;
|
||||
int cc = 0;
|
||||
int l = 0;
|
||||
const unsigned char *z; /* input string */
|
||||
const unsigned char *zt;
|
||||
unsigned char *rz; /* output string */
|
||||
|
||||
//assert( argc==2);
|
||||
if (argc != 2 && argc != 3)
|
||||
{
|
||||
sqlite3_result_error(context, "daap_leftstr() requires 2 parameters", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( SQLITE_NULL == sqlite3_value_type(argv[0]) || SQLITE_NULL == sqlite3_value_type(argv[1]))
|
||||
{
|
||||
sqlite3_result_null(context);
|
||||
return;
|
||||
}
|
||||
|
||||
z = sqlite3_value_text(argv[0]);
|
||||
l = sqlite3_value_int(argv[1]);
|
||||
zt = z;
|
||||
|
||||
while ( sqliteCharVal(zt) && c++ < l)
|
||||
sqliteNextChar(zt);
|
||||
|
||||
cc = zt - z;
|
||||
|
||||
rz = sqlite3_malloc(zt - z + 1);
|
||||
if (!rz)
|
||||
{
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
strncpy((char*) rz, (char*) z, zt - z);
|
||||
*(rz + cc) = '\0';
|
||||
sqlite3_result_text(context, (char*) rz, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_free(rz);
|
||||
}
|
||||
|
||||
int
|
||||
sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi)
|
||||
{
|
||||
@ -519,23 +268,5 @@ sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sqlite3_create_function(db, "daap_leftstr", 2, SQLITE_UTF8, NULL, sqlext_daap_leftstr_xfunc, NULL, NULL);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
if (pzErrMsg)
|
||||
*pzErrMsg = sqlite3_mprintf("Could not create daap_leftstr function: %s\n", sqlite3_errmsg(db));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sqlite3_create_function(db, "daap_charindex", 3, SQLITE_UTF8, NULL, sqlext_daap_charindex_xfunc, NULL, NULL);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
if (pzErrMsg)
|
||||
*pzErrMsg = sqlite3_mprintf("Could not create daap_charindex function: %s\n", sqlite3_errmsg(db));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ forked_daapd_LDADD = -lrt \
|
||||
|
||||
forked_daapd_SOURCES = main.c \
|
||||
db.c db.h \
|
||||
db_upgrade.c db_upgrade.h \
|
||||
logger.c logger.h \
|
||||
conffile.c conffile.h \
|
||||
cache.c cache.h \
|
||||
|
79
src/db.h
79
src/db.h
@ -61,12 +61,6 @@ enum query_type {
|
||||
#define ARTWORK_SPOTIFY 6
|
||||
#define ARTWORK_HTTP 7
|
||||
|
||||
enum filelistitem_type {
|
||||
F_PLAYLIST = 1,
|
||||
F_DIR = 2,
|
||||
F_FILE = 3,
|
||||
};
|
||||
|
||||
struct query_params {
|
||||
/* Query parameters, filled in by caller */
|
||||
enum query_type type;
|
||||
@ -188,6 +182,9 @@ struct media_file_info {
|
||||
char *album_artist_sort;
|
||||
|
||||
char *virtual_path;
|
||||
|
||||
uint32_t directory_id; /* Id of directory */
|
||||
uint32_t date_released;
|
||||
};
|
||||
|
||||
#define mfi_offsetof(field) offsetof(struct media_file_info, field)
|
||||
@ -215,6 +212,7 @@ struct playlist_info {
|
||||
uint32_t special_id; /* iTunes identifies certain 'special' playlists with special meaning */
|
||||
char *virtual_path; /* virtual path of underlying playlist */
|
||||
uint32_t parent_id; /* Id of parent playlist if the playlist is nested */
|
||||
uint32_t directory_id; /* Id of directory */
|
||||
};
|
||||
|
||||
#define pli_offsetof(field) offsetof(struct playlist_info, field)
|
||||
@ -233,6 +231,7 @@ struct db_playlist_info {
|
||||
char *special_id;
|
||||
char *virtual_path;
|
||||
char *parent_id;
|
||||
char *directory_id;
|
||||
};
|
||||
|
||||
#define dbpli_offsetof(field) offsetof(struct db_playlist_info, field)
|
||||
@ -322,16 +321,12 @@ struct db_media_file_info {
|
||||
char *composer_sort;
|
||||
char *album_artist_sort;
|
||||
char *virtual_path;
|
||||
char *directory_id;
|
||||
char *date_released;
|
||||
};
|
||||
|
||||
#define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field)
|
||||
|
||||
struct filelist_info {
|
||||
char *virtual_path;
|
||||
uint32_t time_modified;
|
||||
enum filelistitem_type type;
|
||||
};
|
||||
|
||||
struct watch_info {
|
||||
int wd;
|
||||
char *path;
|
||||
@ -353,15 +348,35 @@ struct filecount_info {
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
/* Directory ids must be in sync with the ids in Q_DIR* in db.c */
|
||||
enum directory_ids {
|
||||
DIR_ROOT = 1,
|
||||
DIR_FILE = 2,
|
||||
DIR_HTTP = 3,
|
||||
DIR_SPOTIFY = 4,
|
||||
};
|
||||
|
||||
struct directory_info {
|
||||
uint32_t id;
|
||||
char *virtual_path;
|
||||
uint32_t db_timestamp;
|
||||
uint32_t disabled;
|
||||
uint32_t parent_id;
|
||||
};
|
||||
|
||||
struct directory_enum {
|
||||
int parent_id;
|
||||
|
||||
/* Private enum context, keep out */
|
||||
sqlite3_stmt *stmt;
|
||||
};
|
||||
|
||||
char *
|
||||
db_escape_string(const char *str);
|
||||
|
||||
void
|
||||
free_pi(struct pairing_info *pi, int content_only);
|
||||
|
||||
void
|
||||
free_fi(struct filelist_info *fi, int content_only);
|
||||
|
||||
void
|
||||
free_mfi(struct media_file_info *mfi, int content_only);
|
||||
|
||||
@ -371,6 +386,9 @@ unicode_fixup_mfi(struct media_file_info *mfi);
|
||||
void
|
||||
free_pli(struct playlist_info *pli, int content_only);
|
||||
|
||||
void
|
||||
free_di(struct directory_info *di, int content_only);
|
||||
|
||||
/* Maintenance and DB hygiene */
|
||||
void
|
||||
db_hook_post_scan(void);
|
||||
@ -495,6 +513,9 @@ db_file_disable_bymatch(char *path, char *strip, uint32_t cookie);
|
||||
int
|
||||
db_file_enable_bycookie(uint32_t cookie, char *path);
|
||||
|
||||
int
|
||||
db_file_update_directoryid(char *path, int dir_id);
|
||||
|
||||
/* Playlists */
|
||||
int
|
||||
db_pl_get_count(void);
|
||||
@ -551,12 +572,34 @@ db_groups_clear(void);
|
||||
int
|
||||
db_group_persistentid_byid(int id, int64_t *persistentid);
|
||||
|
||||
/* Filelist */
|
||||
|
||||
/* Directories */
|
||||
int
|
||||
db_mpd_start_query_filelist(struct query_params *qp, char *path);
|
||||
db_directory_id_byvirtualpath(char *virtual_path);
|
||||
|
||||
int
|
||||
db_mpd_query_fetch_filelist(struct query_params *qp, struct filelist_info *fi);
|
||||
db_directory_enum_start(struct directory_enum *de);
|
||||
|
||||
int
|
||||
db_directory_enum_fetch(struct directory_enum *de, struct directory_info *di);
|
||||
|
||||
void
|
||||
db_directory_enum_end(struct directory_enum *de);
|
||||
|
||||
int
|
||||
db_directory_addorupdate(char *virtual_path, int disabled, int parent_id);
|
||||
|
||||
void
|
||||
db_directory_ping_bymatch(char *path);
|
||||
|
||||
void
|
||||
db_directory_disable_bymatch(char *path, char *strip, uint32_t cookie);
|
||||
|
||||
int
|
||||
db_directory_enable_bycookie(uint32_t cookie, char *path);
|
||||
|
||||
int
|
||||
db_directory_enable_bypath(char *path);
|
||||
|
||||
/* Remotes */
|
||||
int
|
||||
|
1563
src/db_upgrade.c
Normal file
1563
src/db_upgrade.c
Normal file
File diff suppressed because it is too large
Load Diff
27
src/db_upgrade.h
Normal file
27
src/db_upgrade.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Christian Meffert <christian.meffert@googlemail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef SRC_DB_UPGRADE_H_
|
||||
#define SRC_DB_UPGRADE_H_
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
int
|
||||
db_upgrade(sqlite3 *hdl, int db_ver);
|
||||
|
||||
#endif /* SRC_DB_UPGRADE_H_ */
|
@ -23,6 +23,7 @@
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@ -112,14 +113,15 @@ struct deferred_pl {
|
||||
char *path;
|
||||
time_t mtime;
|
||||
struct deferred_pl *next;
|
||||
int directory_id;
|
||||
};
|
||||
|
||||
struct stacked_dir {
|
||||
char *path;
|
||||
int parent_id;
|
||||
struct stacked_dir *next;
|
||||
};
|
||||
|
||||
|
||||
static int cmd_pipe[2];
|
||||
static int exit_pipe[2];
|
||||
static int scan_exit;
|
||||
@ -197,7 +199,7 @@ nonblock_command(struct filescanner_command *cmd)
|
||||
}
|
||||
|
||||
static int
|
||||
push_dir(struct stacked_dir **s, char *path)
|
||||
push_dir(struct stacked_dir **s, char *path, int parent_id)
|
||||
{
|
||||
struct stacked_dir *d;
|
||||
|
||||
@ -215,28 +217,26 @@ push_dir(struct stacked_dir **s, char *path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
d->parent_id = parent_id;
|
||||
|
||||
d->next = *s;
|
||||
*s = d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
static struct stacked_dir *
|
||||
pop_dir(struct stacked_dir **s)
|
||||
{
|
||||
struct stacked_dir *d;
|
||||
char *ret;
|
||||
|
||||
if (!*s)
|
||||
return NULL;
|
||||
|
||||
d = *s;
|
||||
*s = d->next;
|
||||
ret = d->path;
|
||||
|
||||
free(d);
|
||||
|
||||
return ret;
|
||||
return d;
|
||||
}
|
||||
|
||||
#ifdef HAVE_REGEX_H
|
||||
@ -633,7 +633,7 @@ fixup_tags(struct media_file_info *mfi)
|
||||
|
||||
|
||||
void
|
||||
filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct media_file_info *external_mfi)
|
||||
filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct media_file_info *external_mfi, int dir_id)
|
||||
{
|
||||
struct media_file_info *mfi;
|
||||
char *filename;
|
||||
@ -765,6 +765,8 @@ filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct
|
||||
mfi->virtual_path = strdup(virtual_path);
|
||||
}
|
||||
|
||||
mfi->directory_id = dir_id;
|
||||
|
||||
if (mfi->id == 0)
|
||||
db_file_add(mfi);
|
||||
else
|
||||
@ -776,13 +778,13 @@ filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct
|
||||
}
|
||||
|
||||
static void
|
||||
process_playlist(char *file, time_t mtime)
|
||||
process_playlist(char *file, time_t mtime, int dir_id)
|
||||
{
|
||||
enum file_type ft;
|
||||
|
||||
ft = file_type_get(file);
|
||||
if (ft == FILE_PLAYLIST)
|
||||
scan_playlist(file, mtime);
|
||||
scan_playlist(file, mtime, dir_id);
|
||||
#ifdef ITUNES
|
||||
else if (ft == FILE_ITUNES)
|
||||
scan_itunes_itml(file);
|
||||
@ -791,7 +793,7 @@ process_playlist(char *file, time_t mtime)
|
||||
|
||||
/* Thread: scan */
|
||||
static void
|
||||
defer_playlist(char *path, time_t mtime)
|
||||
defer_playlist(char *path, time_t mtime, int dir_id)
|
||||
{
|
||||
struct deferred_pl *pl;
|
||||
|
||||
@ -815,6 +817,7 @@ defer_playlist(char *path, time_t mtime)
|
||||
}
|
||||
|
||||
pl->mtime = mtime;
|
||||
pl->directory_id = dir_id;
|
||||
pl->next = playlists;
|
||||
playlists = pl;
|
||||
|
||||
@ -831,7 +834,7 @@ process_deferred_playlists(void)
|
||||
{
|
||||
playlists = pl->next;
|
||||
|
||||
process_playlist(pl->path, pl->mtime);
|
||||
process_playlist(pl->path, pl->mtime, pl->directory_id);
|
||||
|
||||
free(pl->path);
|
||||
free(pl);
|
||||
@ -843,7 +846,7 @@ process_deferred_playlists(void)
|
||||
|
||||
/* Thread: scan */
|
||||
static void
|
||||
process_file(char *file, time_t mtime, off_t size, int type, int flags)
|
||||
process_file(char *file, time_t mtime, off_t size, int type, int flags, int dir_id)
|
||||
{
|
||||
int is_bulkscan;
|
||||
|
||||
@ -852,7 +855,7 @@ process_file(char *file, time_t mtime, off_t size, int type, int flags)
|
||||
switch (file_type_get(file))
|
||||
{
|
||||
case FILE_REGULAR:
|
||||
filescanner_process_media(file, mtime, size, type, NULL);
|
||||
filescanner_process_media(file, mtime, size, type, NULL, dir_id);
|
||||
|
||||
cache_artwork_ping(file, mtime, !is_bulkscan);
|
||||
// TODO [artworkcache] If entry in artwork cache exists for no artwork available, delete the entry if media file has embedded artwork
|
||||
@ -871,14 +874,14 @@ process_file(char *file, time_t mtime, off_t size, int type, int flags)
|
||||
case FILE_PLAYLIST:
|
||||
case FILE_ITUNES:
|
||||
if (flags & F_SCAN_BULK)
|
||||
defer_playlist(file, mtime);
|
||||
defer_playlist(file, mtime, dir_id);
|
||||
else
|
||||
process_playlist(file, mtime);
|
||||
process_playlist(file, mtime, dir_id);
|
||||
break;
|
||||
|
||||
case FILE_SMARTPL:
|
||||
DPRINTF(E_DBG, L_SCAN, "Smart playlist file: %s\n", file);
|
||||
scan_smartpl(file, mtime);
|
||||
scan_smartpl(file, mtime, dir_id);
|
||||
break;
|
||||
|
||||
case FILE_ARTWORK:
|
||||
@ -948,8 +951,22 @@ check_speciallib(char *path, const char *libtype)
|
||||
}
|
||||
|
||||
/* Thread: scan */
|
||||
static int
|
||||
create_virtual_path(char *path, char *virtual_path, int virtual_path_len)
|
||||
{
|
||||
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, PATH_MAX exceeded\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
process_directory(char *path, int flags)
|
||||
process_directory(char *path, int flags, int parent_id)
|
||||
{
|
||||
DIR *dirp;
|
||||
struct dirent buf;
|
||||
@ -962,6 +979,8 @@ process_directory(char *path, int flags)
|
||||
struct kevent kev;
|
||||
#endif
|
||||
int type;
|
||||
char virtual_path[PATH_MAX];
|
||||
int dir_id;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_DBG, L_SCAN, "Processing directory %s (flags = 0x%x)\n", path, flags);
|
||||
@ -974,6 +993,18 @@ process_directory(char *path, int flags)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add/update directories table */
|
||||
|
||||
ret = create_virtual_path(path, virtual_path, sizeof(virtual_path));
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
dir_id = db_directory_addorupdate(virtual_path, 0, parent_id);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SCAN, "Insert or update of directory failed '%s'\n", virtual_path);
|
||||
}
|
||||
|
||||
/* Check if compilation and/or podcast directory */
|
||||
type = 0;
|
||||
if (check_speciallib(path, "compilations"))
|
||||
@ -1050,15 +1081,15 @@ process_directory(char *path, int flags)
|
||||
if (S_ISREG(sb.st_mode))
|
||||
{
|
||||
if (!(flags & F_SCAN_FAST))
|
||||
process_file(entry, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, flags);
|
||||
process_file(entry, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, flags, dir_id);
|
||||
}
|
||||
else if (S_ISFIFO(sb.st_mode))
|
||||
{
|
||||
if (!(flags & F_SCAN_FAST))
|
||||
process_file(entry, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, flags);
|
||||
process_file(entry, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, flags, dir_id);
|
||||
}
|
||||
else if (S_ISDIR(sb.st_mode))
|
||||
push_dir(&dirstack, entry);
|
||||
push_dir(&dirstack, entry, dir_id);
|
||||
else
|
||||
DPRINTF(E_LOG, L_SCAN, "Skipping %s, not a directory, symlink, pipe nor regular file\n", entry);
|
||||
}
|
||||
@ -1116,21 +1147,58 @@ process_directory(char *path, int flags)
|
||||
}
|
||||
|
||||
/* Thread: scan */
|
||||
static void
|
||||
process_directories(char *root, int flags)
|
||||
{
|
||||
char *path;
|
||||
|
||||
process_directory(root, flags);
|
||||
static int
|
||||
process_parent_directories(char *path)
|
||||
{
|
||||
char *ptr;
|
||||
int dir_id;
|
||||
char buf[PATH_MAX];
|
||||
char virtual_path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
dir_id = DIR_FILE;
|
||||
|
||||
ptr = path + 1;
|
||||
while (ptr && (ptr = strchr(ptr, '/')))
|
||||
{
|
||||
strncpy(buf, path, (ptr - path));
|
||||
buf[(ptr - path)] = '\0';
|
||||
|
||||
ret = create_virtual_path(buf, virtual_path, sizeof(virtual_path));
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
dir_id = db_directory_addorupdate(virtual_path, 0, dir_id);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SCAN, "Insert or update of directory failed '%s'\n", virtual_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return dir_id;
|
||||
}
|
||||
|
||||
static void
|
||||
process_directories(char *root, int parent_id, int flags)
|
||||
{
|
||||
struct stacked_dir *dir;
|
||||
|
||||
process_directory(root, flags, parent_id);
|
||||
|
||||
if (scan_exit)
|
||||
return;
|
||||
|
||||
while ((path = pop_dir(&dirstack)))
|
||||
while ((dir = pop_dir(&dirstack)))
|
||||
{
|
||||
process_directory(path, flags);
|
||||
process_directory(dir->path, flags, dir->parent_id);
|
||||
|
||||
free(path);
|
||||
free(dir->path);
|
||||
free(dir);
|
||||
|
||||
if (scan_exit)
|
||||
return;
|
||||
@ -1148,6 +1216,7 @@ bulk_scan(int flags)
|
||||
char *deref;
|
||||
time_t start;
|
||||
time_t end;
|
||||
int parent_id;
|
||||
int i;
|
||||
|
||||
// Set global flag to avoid queued scan requests
|
||||
@ -1165,6 +1234,8 @@ bulk_scan(int flags)
|
||||
{
|
||||
path = cfg_getnstr(lib, "directories", i);
|
||||
|
||||
parent_id = process_parent_directories(path);
|
||||
|
||||
deref = m_realpath(path);
|
||||
if (!deref)
|
||||
{
|
||||
@ -1173,16 +1244,19 @@ bulk_scan(int flags)
|
||||
/* Assume dir is mistakenly not mounted, so just disable everything and update timestamps */
|
||||
db_file_disable_bymatch(path, "", 0);
|
||||
db_pl_disable_bymatch(path, "", 0);
|
||||
db_directory_disable_bymatch(path, "", 0);
|
||||
|
||||
db_file_ping_bymatch(path, 1);
|
||||
db_pl_ping_bymatch(path, 1);
|
||||
db_directory_ping_bymatch(path);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
counter = 0;
|
||||
db_transaction_begin();
|
||||
process_directories(deref, flags);
|
||||
|
||||
process_directories(deref, parent_id, flags);
|
||||
db_transaction_end();
|
||||
|
||||
free(deref);
|
||||
@ -1306,6 +1380,28 @@ filescanner(void *arg)
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
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 = create_virtual_path(parent_dir, virtual_path, sizeof(virtual_path));
|
||||
if (ret == 0)
|
||||
parent_id = db_directory_id_byvirtualpath(virtual_path);
|
||||
else
|
||||
parent_id = 0;
|
||||
|
||||
free(pathcopy);
|
||||
|
||||
return parent_id;
|
||||
}
|
||||
|
||||
|
||||
#if defined(__linux__)
|
||||
static int
|
||||
@ -1347,6 +1443,7 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie)
|
||||
char *s;
|
||||
int flags = 0;
|
||||
int ret;
|
||||
int parent_id;
|
||||
|
||||
DPRINTF(E_SPAM, L_SCAN, "Directory event: 0x%x, cookie 0x%x, wd %d\n", ie->mask, ie->cookie, wi->wd);
|
||||
|
||||
@ -1354,6 +1451,7 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie)
|
||||
{
|
||||
db_file_disable_bymatch(path, "", 0);
|
||||
db_pl_disable_bymatch(path, "", 0);
|
||||
db_directory_disable_bymatch(path, "", 0);
|
||||
}
|
||||
|
||||
if (ie->mask & IN_MOVE_SELF)
|
||||
@ -1408,6 +1506,7 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie)
|
||||
db_watch_mark_bymatch(path, path, ie->cookie);
|
||||
db_file_disable_bymatch(path, path, ie->cookie);
|
||||
db_pl_disable_bymatch(path, path, ie->cookie);
|
||||
db_directory_disable_bymatch(path, path, ie->cookie);
|
||||
}
|
||||
|
||||
if (ie->mask & IN_MOVED_TO)
|
||||
@ -1417,6 +1516,7 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie)
|
||||
db_watch_move_bycookie(ie->cookie, path);
|
||||
db_file_enable_bycookie(ie->cookie, path);
|
||||
db_pl_enable_bycookie(ie->cookie, path);
|
||||
db_directory_enable_bycookie(ie->cookie, path);
|
||||
|
||||
/* We'll rescan the directory tree to update playlists */
|
||||
flags |= F_SCAN_MOVED;
|
||||
@ -1450,6 +1550,7 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie)
|
||||
|
||||
db_file_disable_bymatch(path, "", 0);
|
||||
db_pl_disable_bymatch(path, "", 0);
|
||||
db_directory_disable_bymatch(path, "", 0);
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
@ -1465,7 +1566,8 @@ process_inotify_dir(struct watch_info *wi, char *path, struct inotify_event *ie)
|
||||
|
||||
if (ie->mask & IN_CREATE)
|
||||
{
|
||||
process_directories(path, flags);
|
||||
parent_id = get_parent_dir_id(path);
|
||||
process_directories(path, parent_id, flags);
|
||||
|
||||
if (dirstack)
|
||||
DPRINTF(E_LOG, L_SCAN, "WARNING: unhandled leftover directories\n");
|
||||
@ -1480,8 +1582,12 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie
|
||||
uint32_t path_hash;
|
||||
char *deref = NULL;
|
||||
char *file = path;
|
||||
char *dir;
|
||||
char dir_vpath[PATH_MAX];
|
||||
int type;
|
||||
int i;
|
||||
int dir_id;
|
||||
char *ptr;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_SPAM, L_SCAN, "File event: 0x%x, cookie 0x%x, wd %d\n", ie->mask, ie->cookie, wi->wd);
|
||||
@ -1541,7 +1647,28 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie
|
||||
|
||||
ret = db_file_enable_bycookie(ie->cookie, path);
|
||||
|
||||
if (ret <= 0)
|
||||
if (ret > 0)
|
||||
{
|
||||
// If file was successfully enabled, update the directory id
|
||||
dir = strdup(path);
|
||||
ptr = strrchr(dir, '/');
|
||||
dir[(ptr - dir)] = '\0';
|
||||
|
||||
ret = create_virtual_path(dir, dir_vpath, sizeof(dir_vpath));
|
||||
if (ret >= 0)
|
||||
{
|
||||
dir_id = db_directory_id_byvirtualpath(dir_vpath);
|
||||
if (dir_id > 0)
|
||||
{
|
||||
ret = db_file_update_directoryid(path, dir_id);
|
||||
if (ret < 0)
|
||||
DPRINTF(E_LOG, L_SCAN, "Error updating directory id for file: %s\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
free(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's not a known media file, so it's either a new file
|
||||
* or a playlist, known or not.
|
||||
@ -1636,10 +1763,14 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie
|
||||
if (check_speciallib(path, "audiobooks"))
|
||||
type |= F_SCAN_TYPE_AUDIOBOOK;
|
||||
|
||||
dir_id = get_parent_dir_id(file);
|
||||
|
||||
if (S_ISREG(sb.st_mode))
|
||||
process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, 0);
|
||||
{
|
||||
process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, 0, dir_id);
|
||||
}
|
||||
else if (S_ISFIFO(sb.st_mode))
|
||||
process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, 0);
|
||||
process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, 0, dir_id);
|
||||
|
||||
if (deref)
|
||||
free(deref);
|
||||
@ -1777,6 +1908,7 @@ kqueue_cb(int fd, short event, void *arg)
|
||||
int w_len;
|
||||
int need_rescan;
|
||||
int ret;
|
||||
int parent_id;
|
||||
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
@ -1839,6 +1971,7 @@ kqueue_cb(int fd, short event, void *arg)
|
||||
/* Disable files */
|
||||
db_file_disable_bymatch(wi.path, "", 0);
|
||||
db_pl_disable_bymatch(wi.path, "", 0);
|
||||
db_directory_disable_bymatch(wi.path, "", 0);
|
||||
|
||||
if (kev.flags & EV_ERROR)
|
||||
{
|
||||
@ -1921,17 +2054,21 @@ kqueue_cb(int fd, short event, void *arg)
|
||||
}
|
||||
|
||||
if (need_rescan)
|
||||
push_dir(&rescan, wi.path);
|
||||
{
|
||||
parent_id = get_parent_dir_id(wi.path);
|
||||
push_dir(&rescan, wi.path, parent_id);
|
||||
}
|
||||
}
|
||||
|
||||
free(wi.path);
|
||||
}
|
||||
|
||||
while ((path = pop_dir(&rescan)))
|
||||
while ((d = pop_dir(&rescan)))
|
||||
{
|
||||
process_directories(path, 0);
|
||||
process_directories(d->path, 0, d->parent_id);
|
||||
|
||||
free(path);
|
||||
free(d->path);
|
||||
free(d);
|
||||
|
||||
if (rescan)
|
||||
DPRINTF(E_LOG, L_SCAN, "WARNING: unhandled leftover directories\n");
|
||||
|
@ -19,7 +19,7 @@ void
|
||||
filescanner_deinit(void);
|
||||
|
||||
void
|
||||
filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct media_file_info *external_mfi);
|
||||
filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct media_file_info *external_mfi, int dir_id);
|
||||
|
||||
/* Actual scanners */
|
||||
int
|
||||
@ -29,10 +29,10 @@ int
|
||||
scan_metadata_icy(char *url, struct media_file_info *mfi);
|
||||
|
||||
void
|
||||
scan_playlist(char *file, time_t mtime);
|
||||
scan_playlist(char *file, time_t mtime, int dir_id);
|
||||
|
||||
void
|
||||
scan_smartpl(char *file, time_t mtime);
|
||||
scan_smartpl(char *file, time_t mtime, int dir_id);
|
||||
|
||||
#ifdef ITUNES
|
||||
void
|
||||
|
@ -74,7 +74,7 @@ extinf_get(char *string, struct media_file_info *mfi, int *extinf)
|
||||
}
|
||||
|
||||
void
|
||||
scan_playlist(char *file, time_t mtime)
|
||||
scan_playlist(char *file, time_t mtime, int dir_id)
|
||||
{
|
||||
FILE *fp;
|
||||
struct media_file_info mfi;
|
||||
@ -172,6 +172,8 @@ scan_playlist(char *file, time_t mtime)
|
||||
*ptr = '\0';
|
||||
pli->virtual_path = strdup(virtual_path);
|
||||
|
||||
pli->directory_id = dir_id;
|
||||
|
||||
ret = db_pl_add(pli, &pl_id);
|
||||
if (ret < 0)
|
||||
{
|
||||
@ -236,7 +238,7 @@ scan_playlist(char *file, time_t mtime)
|
||||
if (extinf)
|
||||
DPRINTF(E_INFO, L_SCAN, "Playlist has EXTINF metadata, artist is '%s', title is '%s'\n", mfi.artist, mfi.title);
|
||||
|
||||
filescanner_process_media(filename, mtime, 0, F_SCAN_TYPE_URL, &mfi);
|
||||
filescanner_process_media(filename, mtime, 0, F_SCAN_TYPE_URL, &mfi, DIR_HTTP);
|
||||
}
|
||||
/* Regular file, should already be in library */
|
||||
else
|
||||
|
@ -171,7 +171,7 @@ smartpl_parse_file(const char *file, struct playlist_info *pli)
|
||||
}
|
||||
|
||||
void
|
||||
scan_smartpl(char *file, time_t mtime)
|
||||
scan_smartpl(char *file, time_t mtime, int dir_id)
|
||||
{
|
||||
struct playlist_info *pli;
|
||||
int pl_id;
|
||||
@ -203,6 +203,8 @@ scan_smartpl(char *file, time_t mtime)
|
||||
else
|
||||
pl_id = pli->id;
|
||||
|
||||
pli->directory_id = dir_id;
|
||||
|
||||
ret = smartpl_parse_file(file, pli);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
342
src/mpd.c
342
src/mpd.c
@ -1557,13 +1557,13 @@ mpd_queueitem_make(char *path, int recursive)
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q%%'", path);
|
||||
qp.filter = sqlite3_mprintf("f.disabled = 0 AND f.virtual_path LIKE '/%q%%'", path);
|
||||
if (!qp.filter)
|
||||
DPRINTF(E_DBG, L_PLAYER, "Out of memory\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q'", path);
|
||||
qp.filter = sqlite3_mprintf("f.disabled = 0 AND f.virtual_path LIKE '/%q'", path);
|
||||
if (!qp.filter)
|
||||
DPRINTF(E_DBG, L_PLAYER, "Out of memory\n");
|
||||
}
|
||||
@ -2685,6 +2685,214 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mpd_add_directory(struct evbuffer *evbuf, int directory_id, int listall, int listinfo, char **errmsg)
|
||||
{
|
||||
struct directory_info subdir;
|
||||
struct query_params qp;
|
||||
struct directory_enum dir_enum;
|
||||
struct db_playlist_info dbpli;
|
||||
char modified[32];
|
||||
uint32_t time_modified;
|
||||
struct db_media_file_info dbmfi;
|
||||
int ret;
|
||||
|
||||
// Load playlists for dir-id
|
||||
memset(&qp, 0, sizeof(struct query_params));
|
||||
qp.type = Q_PL;
|
||||
qp.sort = S_PLAYLIST;
|
||||
qp.idx_type = I_NONE;
|
||||
qp.filter = sqlite3_mprintf("(f.directory_id = %d AND (f.type = %d OR f.type = %d))", directory_id, PL_PLAIN, PL_SMART);
|
||||
ret = db_query_start(&qp);
|
||||
if (ret < 0)
|
||||
{
|
||||
db_query_end(&qp);
|
||||
ret = asprintf(errmsg, "Could not start query");
|
||||
if (ret < 0)
|
||||
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
|
||||
return ACK_ERROR_UNKNOWN;
|
||||
}
|
||||
while (((ret = db_query_fetch_pl(&qp, &dbpli)) == 0) && (dbpli.id))
|
||||
{
|
||||
if (safe_atou32(dbpli.db_timestamp, &time_modified) != 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Error converting time modified to uint32_t: %s\n", dbpli.db_timestamp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listinfo)
|
||||
{
|
||||
mpd_time(modified, sizeof(modified), time_modified);
|
||||
evbuffer_add_printf(evbuf,
|
||||
"playlist: %s\n"
|
||||
"Last-Modified: %s\n",
|
||||
(dbpli.virtual_path + 1),
|
||||
modified);
|
||||
}
|
||||
else
|
||||
{
|
||||
evbuffer_add_printf(evbuf,
|
||||
"playlist: %s\n",
|
||||
(dbpli.virtual_path + 1));
|
||||
}
|
||||
}
|
||||
db_query_end(&qp);
|
||||
sqlite3_free(qp.filter);
|
||||
|
||||
// Load sub directories for dir-id
|
||||
memset(&dir_enum, 0, sizeof(struct directory_enum));
|
||||
dir_enum.parent_id = directory_id;
|
||||
ret = db_directory_enum_start(&dir_enum);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Failed to start directory enum for parent_id %d\n", directory_id);
|
||||
return -1;
|
||||
}
|
||||
while ((ret = db_directory_enum_fetch(&dir_enum, &subdir)) == 0 && subdir.id > 0)
|
||||
{
|
||||
if (listinfo)
|
||||
{
|
||||
evbuffer_add_printf(evbuf,
|
||||
"directory: %s\n"
|
||||
"Last-Modified: %s\n",
|
||||
(subdir.virtual_path + 1),
|
||||
"2015-12-01 00:00");
|
||||
}
|
||||
else
|
||||
{
|
||||
evbuffer_add_printf(evbuf,
|
||||
"directory: %s\n",
|
||||
(subdir.virtual_path + 1));
|
||||
}
|
||||
|
||||
if (listall)
|
||||
{
|
||||
mpd_add_directory(evbuf, subdir.id, listall, listinfo, errmsg);
|
||||
}
|
||||
}
|
||||
db_directory_enum_end(&dir_enum);
|
||||
|
||||
// Load files for dir-id
|
||||
memset(&qp, 0, sizeof(struct query_params));
|
||||
qp.type = Q_ITEMS;
|
||||
qp.sort = S_ARTIST;
|
||||
qp.idx_type = I_NONE;
|
||||
qp.filter = sqlite3_mprintf("(f.directory_id = %d)", directory_id);
|
||||
ret = db_query_start(&qp);
|
||||
if (ret < 0)
|
||||
{
|
||||
db_query_end(&qp);
|
||||
ret = asprintf(errmsg, "Could not start query");
|
||||
if (ret < 0)
|
||||
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
|
||||
return ACK_ERROR_UNKNOWN;
|
||||
}
|
||||
while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
|
||||
{
|
||||
if (listinfo)
|
||||
{
|
||||
ret = mpd_add_db_media_file_info(evbuf, &dbmfi);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Error adding song to the evbuffer, song id: %s\n", dbmfi.id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
evbuffer_add_printf(evbuf,
|
||||
"file: %s\n",
|
||||
(dbmfi.virtual_path + 1));
|
||||
}
|
||||
}
|
||||
db_query_end(&qp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mpd_command_listall(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
{
|
||||
int dir_id;
|
||||
char parent[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
if (argc < 2 || strlen(argv[1]) == 0
|
||||
|| (strncmp(argv[1], "/", 1) == 0 && strlen(argv[1]) == 1))
|
||||
{
|
||||
ret = snprintf(parent, sizeof(parent), "/");
|
||||
}
|
||||
else if (strncmp(argv[1], "/", 1) == 0)
|
||||
{
|
||||
ret = snprintf(parent, sizeof(parent), "%s/", argv[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = snprintf(parent, sizeof(parent), "/%s", argv[1]);
|
||||
}
|
||||
|
||||
if ((ret < 0) || (ret >= sizeof(parent)))
|
||||
{
|
||||
DPRINTF(E_INFO, L_MPD, "Parent path exceeds PATH_MAX\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Load dir-id from db for parent-path
|
||||
dir_id = db_directory_id_byvirtualpath(parent);
|
||||
if (dir_id == 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Directory info not found for virtual-path '%s'\n", parent);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = mpd_add_directory(evbuf, dir_id, 1, 0, errmsg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mpd_command_listallinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
{
|
||||
int dir_id;
|
||||
char parent[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
if (argc < 2 || strlen(argv[1]) == 0
|
||||
|| (strncmp(argv[1], "/", 1) == 0 && strlen(argv[1]) == 1))
|
||||
{
|
||||
ret = snprintf(parent, sizeof(parent), "/");
|
||||
}
|
||||
else if (strncmp(argv[1], "/", 1) == 0)
|
||||
{
|
||||
ret = snprintf(parent, sizeof(parent), "%s/", argv[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = snprintf(parent, sizeof(parent), "/%s", argv[1]);
|
||||
}
|
||||
|
||||
if ((ret < 0) || (ret >= sizeof(parent)))
|
||||
{
|
||||
ret = asprintf(errmsg, "Parent path exceeds PATH_MAX\n");
|
||||
if (ret < 0)
|
||||
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Load dir-id from db for parent-path
|
||||
dir_id = db_directory_id_byvirtualpath(parent);
|
||||
if (dir_id == 0)
|
||||
{
|
||||
ret = asprintf(errmsg, "Directory info not found for virtual-path '%s'\n", parent);
|
||||
if (ret < 0)
|
||||
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = mpd_add_directory(evbuf, dir_id, 1, 1, errmsg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command handler function for 'lsinfo'
|
||||
* Lists the contents of the directory given in argv[1].
|
||||
@ -2692,11 +2900,8 @@ mpd_command_list(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
static int
|
||||
mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
{
|
||||
struct query_params qp;
|
||||
int dir_id;
|
||||
char parent[PATH_MAX];
|
||||
struct filelist_info *fi;
|
||||
struct media_file_info *mfi;
|
||||
char modified[32];
|
||||
int print_playlists;
|
||||
int ret;
|
||||
|
||||
@ -2711,7 +2916,7 @@ mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = snprintf(parent, sizeof(parent), "/%s/", argv[1]);
|
||||
ret = snprintf(parent, sizeof(parent), "/%s", argv[1]);
|
||||
}
|
||||
|
||||
if ((ret < 0) || (ret >= sizeof(parent)))
|
||||
@ -2731,76 +2936,24 @@ mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
print_playlists = 1;
|
||||
}
|
||||
|
||||
fi = (struct filelist_info*)malloc(sizeof(struct filelist_info));
|
||||
if (!fi)
|
||||
|
||||
// Load dir-id from db for parent-path
|
||||
dir_id = db_directory_id_byvirtualpath(parent);
|
||||
if (dir_id == 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Out of memory for fi\n");
|
||||
return ACK_ERROR_UNKNOWN;
|
||||
DPRINTF(E_LOG, L_MPD, "Directory info not found for virtual-path '%s'\n", parent);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&qp, 0, sizeof(struct query_params));
|
||||
ret = mpd_add_directory(evbuf, dir_id, 0, 1, errmsg);
|
||||
|
||||
ret = db_mpd_start_query_filelist(&qp, parent);
|
||||
if (ret < 0)
|
||||
// If the root directory was passed as argument add the stored playlists to the response
|
||||
if (ret == 0 && print_playlists)
|
||||
{
|
||||
ret = asprintf(errmsg, "Could not start query for path '%s'", argv[1]);
|
||||
if (ret < 0)
|
||||
DPRINTF(E_LOG, L_MPD, "Out of memory\n");
|
||||
|
||||
free_fi(fi, 0);
|
||||
return ACK_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
while (((ret = db_mpd_query_fetch_filelist(&qp, fi)) == 0) && (fi->virtual_path))
|
||||
{
|
||||
if (fi->type == F_DIR)
|
||||
{
|
||||
mpd_time(modified, sizeof(modified), fi->time_modified);
|
||||
|
||||
evbuffer_add_printf(evbuf,
|
||||
"directory: %s\n"
|
||||
"Last-Modified: %s\n",
|
||||
(fi->virtual_path + 1),
|
||||
modified);
|
||||
}
|
||||
else if (fi->type == F_PLAYLIST)
|
||||
{
|
||||
mpd_time(modified, sizeof(modified), fi->time_modified);
|
||||
|
||||
evbuffer_add_printf(evbuf,
|
||||
"playlist: %s\n"
|
||||
"Last-Modified: %s\n",
|
||||
(fi->virtual_path + 1),
|
||||
modified);
|
||||
}
|
||||
else if (fi->type == F_FILE)
|
||||
{
|
||||
mfi = db_file_fetch_byvirtualpath(fi->virtual_path);
|
||||
if (mfi)
|
||||
{
|
||||
ret = mpd_add_mediainfo(evbuf, mfi, 0, -1);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not add mediainfo for path '%s'\n", fi->virtual_path);
|
||||
}
|
||||
|
||||
free_mfi(mfi, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db_query_end(&qp);
|
||||
|
||||
if (fi)
|
||||
free_fi(fi, 0);
|
||||
|
||||
if (print_playlists)
|
||||
{
|
||||
// If the root directory was passed as argument add the stored playlists to the response
|
||||
return mpd_command_listplaylists(evbuf, argc, argv, errmsg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -3782,7 +3935,6 @@ static struct command mpd_handlers[] =
|
||||
.mpdcommand = "list",
|
||||
.handler = mpd_command_list
|
||||
},
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "listall",
|
||||
.handler = mpd_command_listall
|
||||
@ -3791,6 +3943,7 @@ static struct command mpd_handlers[] =
|
||||
.mpdcommand = "listallinfo",
|
||||
.handler = mpd_command_listallinfo
|
||||
},
|
||||
/*
|
||||
{
|
||||
.mpdcommand = "listfiles",
|
||||
.handler = mpd_command_listfiles
|
||||
@ -4588,8 +4741,24 @@ int mpd_init(void)
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_port = htons(port);
|
||||
saddr = (struct sockaddr *)&sin6;
|
||||
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase_mpd,
|
||||
mpd_accept_conn_cb,
|
||||
NULL,
|
||||
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
-1,
|
||||
saddr,
|
||||
saddr_length);
|
||||
|
||||
if (!listener)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not bind to port %d, falling back to IPv4\n", port);
|
||||
v6enabled = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!v6enabled)
|
||||
{
|
||||
saddr_length = sizeof(struct sockaddr_in);
|
||||
memset(&sin, 0, saddr_length);
|
||||
@ -4597,23 +4766,24 @@ int mpd_init(void)
|
||||
sin.sin_addr.s_addr = htonl(0);
|
||||
sin.sin_port = htons(port);
|
||||
saddr = (struct sockaddr *)&sin;
|
||||
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase_mpd,
|
||||
mpd_accept_conn_cb,
|
||||
NULL,
|
||||
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
-1,
|
||||
saddr,
|
||||
saddr_length);
|
||||
|
||||
if (!listener)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not create connection listener for mpd clients on port %d\n", port);
|
||||
|
||||
goto connew_fail;
|
||||
}
|
||||
}
|
||||
|
||||
listener = evconnlistener_new_bind(
|
||||
evbase_mpd,
|
||||
mpd_accept_conn_cb,
|
||||
NULL,
|
||||
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||
-1,
|
||||
saddr,
|
||||
saddr_length);
|
||||
|
||||
if (!listener)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not create connection listener for mpd clients on port %d\n", port);
|
||||
|
||||
goto connew_fail;
|
||||
}
|
||||
evconnlistener_set_error_cb(listener, mpd_accept_error_cb);
|
||||
|
||||
http_port = cfg_getint(cfg_getsec(cfg, "mpd"), "http_port");
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/queue.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
@ -573,6 +574,9 @@ spotify_track_save(int plid, sp_track *track, const char *pltitle, int time_adde
|
||||
sp_link *link;
|
||||
char url[1024];
|
||||
int ret;
|
||||
int dir_id;
|
||||
char virtual_path[PATH_MAX];
|
||||
|
||||
|
||||
if (!fptr_sp_track_is_loaded(track))
|
||||
{
|
||||
@ -618,7 +622,36 @@ spotify_track_save(int plid, sp_track *track, const char *pltitle, int time_adde
|
||||
return -1;
|
||||
}
|
||||
|
||||
filescanner_process_media(url, time(NULL), 0, F_SCAN_TYPE_SPOTIFY, &mfi);
|
||||
ret = snprintf(virtual_path, sizeof(virtual_path), "/spotify:/%s", mfi.artist);
|
||||
if ((ret < 0) || (ret >= sizeof(virtual_path)))
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Virtual path exceeds PATH_MAX (/spotify:/%s)\n", mfi.artist);
|
||||
free_mfi(&mfi, 1);
|
||||
return -1;
|
||||
}
|
||||
dir_id = db_directory_addorupdate(virtual_path, 0, DIR_SPOTIFY);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not add or update directory '%s'\n", virtual_path);
|
||||
free_mfi(&mfi, 1);
|
||||
return -1;
|
||||
}
|
||||
ret = snprintf(virtual_path, sizeof(virtual_path), "/spotify:/%s/%s", mfi.artist, mfi.album);
|
||||
if ((ret < 0) || (ret >= sizeof(virtual_path)))
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Virtual path exceeds PATH_MAX (/spotify:/%s/%s)\n", mfi.artist, mfi.album);
|
||||
free_mfi(&mfi, 1);
|
||||
return -1;
|
||||
}
|
||||
dir_id = db_directory_addorupdate(virtual_path, 0, dir_id);
|
||||
if (dir_id <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not add or update directory '%s'\n", virtual_path);
|
||||
free_mfi(&mfi, 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
filescanner_process_media(url, time(NULL), 0, F_SCAN_TYPE_SPOTIFY, &mfi, dir_id);
|
||||
|
||||
free_mfi(&mfi, 1);
|
||||
|
||||
@ -669,7 +702,7 @@ spotify_playlist_save(sp_playlist *pl)
|
||||
int plid;
|
||||
int num_tracks;
|
||||
char virtual_path[PATH_MAX];
|
||||
int time;
|
||||
int created;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@ -759,6 +792,7 @@ spotify_playlist_save(sp_playlist *pl)
|
||||
pli->path = strdup(url);
|
||||
pli->virtual_path = strdup(virtual_path);
|
||||
pli->parent_id = g_base_plid;
|
||||
pli->directory_id = DIR_SPOTIFY;
|
||||
|
||||
ret = db_pl_add(pli, &plid);
|
||||
if ((ret < 0) || (plid < 1))
|
||||
@ -783,9 +817,9 @@ spotify_playlist_save(sp_playlist *pl)
|
||||
continue;
|
||||
}
|
||||
|
||||
time = fptr_sp_playlist_track_create_time(pl, i);
|
||||
created = fptr_sp_playlist_track_create_time(pl, i);
|
||||
|
||||
ret = spotify_track_save(plid, track, name, time);
|
||||
ret = spotify_track_save(plid, track, name, created);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Error saving track %d to playlist '%s' (id %d)\n", i, name, plid);
|
||||
@ -1383,6 +1417,8 @@ logged_in(sp_session *sess, sp_error error)
|
||||
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Login to Spotify succeeded. Reloading playlists.\n");
|
||||
|
||||
db_directory_enable_bypath("/spotify:");
|
||||
|
||||
pl = fptr_sp_session_starred_create(sess);
|
||||
fptr_sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user