mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 23:55:57 -05:00
1864 lines
59 KiB
C
1864 lines
59 KiB
C
/*
|
|
* $Id$
|
|
* sql-specific db implementation
|
|
*
|
|
* Copyright (C) 2005 Ron Pedde (ron@pedde.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
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#define _XOPEN_SOURCE 500
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "err.h"
|
|
#include "mp3-scanner.h"
|
|
#include "db-generic.h"
|
|
#include "db-sql.h"
|
|
#include "restart.h"
|
|
#include "ssc.h"
|
|
#include "smart-parser.h"
|
|
|
|
#ifdef HAVE_LIBSQLITE
|
|
#include "db-sql-sqlite2.h"
|
|
#endif
|
|
#ifdef HAVE_LIBSQLITE3
|
|
#include "db-sql-sqlite3.h"
|
|
#endif
|
|
|
|
/* Globals */
|
|
static int db_sql_reload=0;
|
|
static int db_sql_in_playlist_scan=0;
|
|
static int db_sql_in_scan=0;
|
|
static int db_sql_need_dispose=0;
|
|
|
|
/* Forwards */
|
|
int db_sql_get_size(DBQUERYINFO *pinfo, char **valarray);
|
|
int db_sql_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len);
|
|
void db_sql_build_mp3file(char **valarray, MP3FILE *pmp3);
|
|
int db_sql_update(char **pe, MP3FILE *pmp3, int *id);
|
|
int db_sql_update_playlists(char **pe);
|
|
int db_sql_parse_smart(char **pe, char **clause, char *phrase);
|
|
|
|
#define STR(a) (a) ? (a) : ""
|
|
#define ISSTR(a) ((a) && strlen((a)))
|
|
#define MAYBEFREE(a) { if((a)) free((a)); };
|
|
#define DMAPLEN(a) (((a) && strlen(a)) ? (8+(int)strlen((a))) : \
|
|
((pinfo->zero_length) ? 8 : 0))
|
|
#define EMIT(a) (pinfo->zero_length ? 1 : ((a) && strlen((a))) ? 1 : 0)
|
|
|
|
/**
|
|
* functions for the specific db backend
|
|
*/
|
|
int (*db_sql_open_fn)(char **pe, char *dsn) = NULL;
|
|
int (*db_sql_close_fn)(void) = NULL;
|
|
int (*db_sql_exec_fn)(char **pe, int loglevel, char *fmt, ...) = NULL;
|
|
char* (*db_sql_vmquery_fn)(char *fmt,va_list ap) = NULL;
|
|
void (*db_sql_vmfree_fn)(char *query) = NULL;
|
|
int (*db_sql_enum_begin_fn)(char **pe, char *fmt, ...) = NULL;
|
|
int (*db_sql_enum_fetch_fn)(char **pe, SQL_ROW *pr) = NULL;
|
|
int (*db_sql_enum_end_fn)(char **pe) = NULL;
|
|
int (*db_sql_enum_restart_fn)(char **pe);
|
|
int (*db_sql_event_fn)(int event_type);
|
|
int (*db_sql_insert_id_fn)(void);
|
|
|
|
#ifdef HAVE_LIBSQLITE
|
|
int db_sql_open_sqlite2(char **pe, char *parameters) {
|
|
/* first, set our external links to sqlite2 */
|
|
db_sql_open_fn = db_sqlite2_open;
|
|
db_sql_close_fn = db_sqlite2_close;
|
|
db_sql_exec_fn = db_sqlite2_exec;
|
|
db_sql_vmquery_fn = db_sqlite2_vmquery;
|
|
db_sql_vmfree_fn = db_sqlite2_vmfree;
|
|
db_sql_enum_begin_fn = db_sqlite2_enum_begin;
|
|
db_sql_enum_fetch_fn = db_sqlite2_enum_fetch;
|
|
db_sql_enum_end_fn = db_sqlite2_enum_end;
|
|
db_sql_enum_restart_fn = db_sqlite2_enum_restart;
|
|
db_sql_event_fn = db_sqlite2_event;
|
|
db_sql_insert_id_fn = db_sqlite2_insert_id;
|
|
|
|
return db_sql_open(pe,parameters);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBSQLITE3
|
|
int db_sql_open_sqlite3(char **pe, char *parameters) {
|
|
/* first, set our external links to sqlite2 */
|
|
db_sql_open_fn = db_sqlite3_open;
|
|
db_sql_close_fn = db_sqlite3_close;
|
|
db_sql_exec_fn = db_sqlite3_exec;
|
|
db_sql_vmquery_fn = db_sqlite3_vmquery;
|
|
db_sql_vmfree_fn = db_sqlite3_vmfree;
|
|
db_sql_enum_begin_fn = db_sqlite3_enum_begin;
|
|
db_sql_enum_fetch_fn = db_sqlite3_enum_fetch;
|
|
db_sql_enum_end_fn = db_sqlite3_enum_end;
|
|
db_sql_enum_restart_fn = db_sqlite3_enum_restart;
|
|
db_sql_event_fn = db_sqlite3_event;
|
|
db_sql_insert_id_fn = db_sqlite3_insert_id;
|
|
|
|
return db_sql_open(pe,parameters);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* escape a sql string, returning it the supplied buffer.
|
|
* note that this uses the sqlite escape format -- use %q for quoted
|
|
* sql.
|
|
*
|
|
* @param buffer buffer to throw the escaped sql into
|
|
* @param size size of buffer (or size required, if DB_E_SIZE)
|
|
* @param fmt printf style format
|
|
* @returns DB_E_SUCCESS with buffer filled, or DB_E_SIZE, with size
|
|
* set to the required size
|
|
*/
|
|
int db_sql_escape(char *buffer, int *size, char *fmt, ...) {
|
|
va_list ap;
|
|
char *escaped;
|
|
|
|
va_start(ap,fmt);
|
|
escaped = db_sql_vmquery_fn(fmt,ap);
|
|
va_end(ap);
|
|
|
|
if(*size < (int)strlen(escaped)) {
|
|
*size = (int)strlen(escaped) + 1;
|
|
db_sql_vmfree_fn(escaped);
|
|
return DB_E_SIZE;
|
|
}
|
|
|
|
strcpy(buffer,escaped);
|
|
*size = (int)strlen(escaped);
|
|
db_sql_vmfree_fn(escaped);
|
|
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
* fetch a single row, using the underlying database enum
|
|
* functions
|
|
*/
|
|
int db_sql_fetch_row(char **pe, SQL_ROW *row, char *fmt, ...) {
|
|
int err;
|
|
char *query;
|
|
va_list ap;
|
|
|
|
|
|
db_sql_need_dispose = 0;
|
|
*row=NULL;
|
|
|
|
va_start(ap,fmt);
|
|
query=db_sql_vmquery_fn(fmt,ap);
|
|
va_end(ap);
|
|
|
|
err=db_sql_enum_begin_fn(pe,"%s",query);
|
|
db_sql_vmfree_fn(query);
|
|
|
|
if(err != DB_E_SUCCESS) {
|
|
DPRINTF(E_SPAM,L_DB,"Error: enum_begin failed: %s\n",(pe) ? *pe : "?");
|
|
return err;
|
|
}
|
|
|
|
err=db_sql_enum_fetch_fn(pe, row);
|
|
if(err != DB_E_SUCCESS) {
|
|
DPRINTF(E_SPAM,L_DB,"Error: enum_fetch failed: %s\n",*pe);
|
|
db_sql_enum_end_fn(NULL);
|
|
return err;
|
|
}
|
|
|
|
if(!(*row)) {
|
|
db_sql_enum_end_fn(NULL);
|
|
db_get_error(pe,DB_E_NOROWS);
|
|
return DB_E_NOROWS;
|
|
}
|
|
|
|
db_sql_need_dispose = 1;
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
int db_sql_fetch_int(char **pe, int *result, char *fmt, ...) {
|
|
int err;
|
|
char *query;
|
|
va_list ap;
|
|
SQL_ROW row;
|
|
|
|
va_start(ap,fmt);
|
|
query=db_sql_vmquery_fn(fmt,ap);
|
|
va_end(ap);
|
|
|
|
err = db_sql_fetch_row(pe, &row, "%s", query);
|
|
db_sql_vmfree_fn(query);
|
|
|
|
if(err != DB_E_SUCCESS) {
|
|
DPRINTF(E_SPAM,L_DB,"fetch_row failed in fetch_int: %s\n",pe ? *pe : NULL);
|
|
return err;
|
|
}
|
|
|
|
*result = atoi(row[0]);
|
|
db_sql_dispose_row();
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
int db_sql_fetch_char(char **pe, char **result, char *fmt, ...) {
|
|
int err;
|
|
char *query;
|
|
va_list ap;
|
|
SQL_ROW row;
|
|
|
|
va_start(ap,fmt);
|
|
query=db_sql_vmquery_fn(fmt,ap);
|
|
va_end(ap);
|
|
|
|
err = db_sql_fetch_row(pe, &row, "%s", query);
|
|
if(err != DB_E_SUCCESS)
|
|
return err;
|
|
|
|
*result = strdup(row[0]);
|
|
db_sql_dispose_row();
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
int db_sql_dispose_row(void) {
|
|
int err = DB_E_SUCCESS;
|
|
|
|
/* don't really need the row */
|
|
if(db_sql_need_dispose) {
|
|
err=db_sql_enum_end_fn(NULL);
|
|
db_sql_need_dispose=0;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* get the sql where clause for a smart playlist spec. This
|
|
* where clause must be freed by the caller
|
|
*
|
|
* @param phrase playlist spec to be converted
|
|
* @returns sql where clause if successful, NULL otherwise
|
|
*/
|
|
int db_sql_parse_smart(char **pe, char **clause, char *phrase) {
|
|
PARSETREE pt;
|
|
|
|
if(strcmp(phrase,"1") == 0) {
|
|
*clause = strdup("1");
|
|
return TRUE;
|
|
}
|
|
|
|
pt=sp_init();
|
|
if(!pt) {
|
|
if(pe) *pe = strdup("Could not initialize parse tree");
|
|
return FALSE;
|
|
}
|
|
|
|
if(!sp_parse(pt,phrase,SP_TYPE_PLAYLIST)) {
|
|
if(pe) *pe = strdup(sp_get_error(pt));
|
|
|
|
DPRINTF(E_LOG,L_DB,"Error parsing playlist: %s\n",sp_get_error(pt));
|
|
|
|
sp_dispose(pt);
|
|
return FALSE;
|
|
} else {
|
|
*clause = sp_sql_clause(pt);
|
|
}
|
|
|
|
sp_dispose(pt);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* open sqlite database
|
|
*
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_open(char **pe, char *parameters) {
|
|
int result;
|
|
|
|
result = db_sql_open_fn(pe,parameters);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* initialize the sqlite database, reloading if requested
|
|
*
|
|
* @param reload whether or not to do a full reload on the db
|
|
* on return, reload is set to 1 if the db MUST be rescanned
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_init(int *reload) {
|
|
int items;
|
|
int rescan = 0;
|
|
int err;
|
|
|
|
err=db_sql_get_count(NULL,&items, countSongs);
|
|
if(err != DB_E_SUCCESS)
|
|
items = 0;
|
|
|
|
/* check if a request has been written into the db (by a db upgrade?) */
|
|
if(db_sql_fetch_int(NULL,&rescan,"select value from config where "
|
|
"term='rescan'") == DB_E_SUCCESS)
|
|
{
|
|
if(rescan)
|
|
*reload=1;
|
|
}
|
|
|
|
|
|
|
|
if(*reload || (!items)) {
|
|
DPRINTF(E_LOG,L_DB,"Full reload...\n");
|
|
db_sql_event_fn(DB_SQL_EVENT_FULLRELOAD);
|
|
db_sql_reload=1;
|
|
*reload=1;
|
|
} else {
|
|
db_sql_event_fn(DB_SQL_EVENT_STARTUP);
|
|
db_sql_reload=0;
|
|
*reload=0;
|
|
}
|
|
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* close the database
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_deinit(void) {
|
|
return db_sql_close_fn();
|
|
}
|
|
|
|
/**
|
|
* start a background scan
|
|
*
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_start_scan(void) {
|
|
DPRINTF(E_DBG,L_DB,"Starting db scan\n");
|
|
db_sql_event_fn(DB_SQL_EVENT_SONGSCANSTART);
|
|
|
|
db_sql_in_scan=1;
|
|
db_sql_in_playlist_scan=0;
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* end song scan -- start playlist scan
|
|
*
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_end_song_scan(void) {
|
|
DPRINTF(E_DBG,L_DB,"Ending song scan\n");
|
|
|
|
db_sql_event_fn(DB_SQL_EVENT_SONGSCANEND);
|
|
db_sql_event_fn(DB_SQL_EVENT_PLSCANSTART);
|
|
|
|
db_sql_in_scan=0;
|
|
db_sql_in_playlist_scan=1;
|
|
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* stop a db scan
|
|
*
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_end_scan(void) {
|
|
db_sql_event_fn(DB_SQL_EVENT_PLSCANEND);
|
|
|
|
db_sql_update_playlists(NULL);
|
|
db_sql_reload=0;
|
|
db_sql_in_playlist_scan=0;
|
|
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* delete a playlist
|
|
*
|
|
* @param playlistid playlist to delete
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_delete_playlist(char **pe, int playlistid) {
|
|
int type;
|
|
int result;
|
|
|
|
result=db_sql_fetch_int(pe,&type,"select type from playlists where id=%d",
|
|
playlistid);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
if(result == DB_E_NOROWS) { /* Override the generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_PLAYLIST);
|
|
return DB_E_INVALID_PLAYLIST;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* got a good playlist, now do what we need to do */
|
|
db_sql_exec_fn(pe,E_FATAL,"delete from playlists where id=%d",playlistid);
|
|
db_sql_exec_fn(pe,E_FATAL,"delete from playlistitems where playlistid=%d",playlistid);
|
|
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* delete a song from a playlist
|
|
*
|
|
* @param playlistid playlist to delete item from
|
|
* @param songid song to delete from playlist
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_delete_playlist_item(char **pe, int playlistid, int songid) {
|
|
int result;
|
|
int playlist_type;
|
|
int count;
|
|
|
|
/* first, check the playlist */
|
|
result=db_sql_fetch_int(pe,&playlist_type,
|
|
"select type from playlists where id=%d",
|
|
playlistid);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
if(result == DB_E_NOROWS) { /* Override generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_PLAYLIST);
|
|
return DB_E_INVALID_PLAYLIST;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if(playlist_type == PL_SMART) { /* can't delete from a smart playlist */
|
|
db_get_error(pe,DB_E_INVALIDTYPE);
|
|
return DB_E_INVALIDTYPE;
|
|
}
|
|
|
|
/* make sure the songid is valid */
|
|
result=db_sql_fetch_int(pe,&count,"select count(*) from playlistitems "
|
|
"where playlistid=%d and songid=%d",
|
|
playlistid,songid);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
if(result == DB_E_NOROWS) { /* Override generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_SONGID);
|
|
return DB_E_INVALID_SONGID;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* looks valid, so lets add the item */
|
|
result=db_sql_exec_fn(pe,E_DBG,"delete from playlistitems where "
|
|
"playlistid=%d and songid=%d",playlistid,songid);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* edit a playlist. The only things worth changing are the name
|
|
* and the "where" clause.
|
|
*
|
|
* @param id id of the playlist to alter
|
|
* @param name new name of the playlist (or NULL)
|
|
* @param where new where clause (or NULL)
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_edit_playlist(char **pe, int id, char *name, char *clause) {
|
|
int result;
|
|
int playlist_type;
|
|
int dup_id=id;
|
|
|
|
if((name == NULL) && (clause == NULL))
|
|
return DB_E_SUCCESS; /* I guess?? */
|
|
|
|
if(id == 1) { /* can't edit the library query */
|
|
db_get_error(pe,DB_E_INVALID_PLAYLIST);
|
|
return DB_E_INVALID_PLAYLIST;
|
|
}
|
|
|
|
/* first, check the playlist */
|
|
result=db_sql_fetch_int(pe,&playlist_type,
|
|
"select type from playlists where id=%d",id);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
if(result == DB_E_NOROWS) { /* Override generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_PLAYLIST);
|
|
return DB_E_INVALID_PLAYLIST;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* TODO: check for duplicate names here */
|
|
if(name) {
|
|
result = db_sql_fetch_int(pe,&dup_id,"select id from playlists "
|
|
"where upper(title)=upper('%q')",name);
|
|
|
|
if((result != DB_E_SUCCESS) && (result != DB_E_NOROWS))
|
|
return result;
|
|
|
|
if(result == DB_E_NOROWS)
|
|
if(pe) free(*pe);
|
|
|
|
if(dup_id != id) {
|
|
db_get_error(pe,DB_E_DUPLICATE_PLAYLIST,name);
|
|
return DB_E_DUPLICATE_PLAYLIST;
|
|
}
|
|
|
|
if((playlist_type == PL_SMART)&&(clause))
|
|
return db_sql_exec_fn(pe,E_LOG,"update playlists set title='%q', "
|
|
"query='%q' where id=%d",name,clause,id);
|
|
|
|
return db_sql_exec_fn(pe,E_LOG,"update playlists set title='%q' "
|
|
"where id=%d",name,id);
|
|
}
|
|
|
|
if((playlist_type == PL_SMART) && (clause))
|
|
return db_sql_exec_fn(pe,E_LOG,"update playlists set query='%q' "
|
|
"where id=%d",clause,id);
|
|
|
|
return DB_E_SUCCESS; /* ?? */
|
|
}
|
|
|
|
/**
|
|
* add a playlist
|
|
*
|
|
* @param name name of the playlist
|
|
* @param type playlist type: 0 - static, 1 - smart, 2 - m3u
|
|
* @param clause: "where" clause for smart playlist
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid) {
|
|
int cnt=0;
|
|
int result=DB_E_SUCCESS;
|
|
char *criteria;
|
|
char *estring;
|
|
|
|
result=db_sql_fetch_int(pe,&cnt,"select count(*) from playlists where "
|
|
"upper(title)=upper('%q')",name);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if(cnt != 0) { /* duplicate */
|
|
db_get_error(pe,DB_E_DUPLICATE_PLAYLIST,name);
|
|
return DB_E_DUPLICATE_PLAYLIST;
|
|
}
|
|
|
|
if((type == PL_SMART) && (!clause)) {
|
|
db_get_error(pe,DB_E_NOCLAUSE);
|
|
return DB_E_NOCLAUSE;
|
|
}
|
|
|
|
/* Let's throw it in */
|
|
switch(type) {
|
|
case PL_STATICWEB: /* static, maintained in web interface */
|
|
case PL_STATICFILE: /* static, from file */
|
|
case PL_STATICXML: /* from iTunes XML file */
|
|
result = db_sql_exec_fn(pe,E_LOG,"insert into playlists "
|
|
"(title,type,items,query,db_timestamp,path,idx) "
|
|
"values ('%q',%d,0,NULL,%d,'%q',%d)",
|
|
name,type,time(NULL),path,index);
|
|
break;
|
|
case PL_SMART: /* smart */
|
|
if(!db_sql_parse_smart(&estring,&criteria,clause)) {
|
|
db_get_error(pe,DB_E_PARSE,estring);
|
|
free(estring);
|
|
return DB_E_PARSE;
|
|
}
|
|
free(criteria);
|
|
|
|
result = db_sql_exec_fn(pe,E_LOG,"insert into playlists "
|
|
"(title,type,items,query,db_timestamp,idx) "
|
|
"values ('%q',%d,%d,'%q',%d,0)",
|
|
name,PL_SMART,cnt,clause,time(NULL));
|
|
break;
|
|
}
|
|
|
|
if(result)
|
|
return result;
|
|
|
|
result = db_sql_fetch_int(pe,playlistid,
|
|
"select id from playlists where title='%q'",
|
|
name);
|
|
|
|
if(((type==PL_STATICFILE)||(type==PL_STATICXML))
|
|
&& (db_sql_in_playlist_scan) && (!db_sql_reload)) {
|
|
db_sql_exec_fn(NULL,E_FATAL,"insert into plupdated values (%d)",*playlistid);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* add a song to a static playlist
|
|
*
|
|
* @param playlistid playlist to add song to
|
|
* @param songid song to add
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_add_playlist_item(char **pe, int playlistid, int songid) {
|
|
int result;
|
|
int playlist_type;
|
|
int count;
|
|
|
|
/* first, check the playlist */
|
|
result=db_sql_fetch_int(pe,&playlist_type,
|
|
"select type from playlists where id=%d",
|
|
playlistid);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
if(result == DB_E_NOROWS) { /* Override generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_PLAYLIST);
|
|
return DB_E_INVALID_PLAYLIST;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if(playlist_type == 1) { /* can't add to smart playlists, or static */
|
|
db_get_error(pe,DB_E_INVALIDTYPE);
|
|
return DB_E_INVALIDTYPE;
|
|
}
|
|
|
|
/* make sure the songid is valid */
|
|
result=db_sql_fetch_int(pe,&count,"select count(*) from songs where "
|
|
"id=%d",songid);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
if(result == DB_E_NOROWS) { /* Override generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_SONGID);
|
|
return DB_E_INVALID_SONGID;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* looks valid, so lets add the item */
|
|
result=db_sql_exec_fn(pe,E_DBG,"insert into playlistitems "
|
|
"(playlistid, songid) values (%d,%d)",
|
|
playlistid,songid);
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* add a database item
|
|
*
|
|
* @param pmp3 mp3 file to add
|
|
*/
|
|
int db_sql_add(char **pe, MP3FILE *pmp3, int *id) {
|
|
int err;
|
|
int count;
|
|
int insertid;
|
|
|
|
DPRINTF(E_SPAM,L_DB,"Entering db_sql_add\n");
|
|
|
|
if(!pmp3->time_added)
|
|
pmp3->time_added = (int)time(NULL);
|
|
|
|
if(!pmp3->time_modified)
|
|
pmp3->time_modified = (int)time(NULL);
|
|
|
|
pmp3->db_timestamp = (int)time(NULL);
|
|
|
|
|
|
/* Always an add if in song scan on full reload */
|
|
if((!db_sql_reload)||(!db_sql_in_scan)) {
|
|
err=db_sql_fetch_int(NULL,&count,"select count(*) from songs where "
|
|
"path='%q'",pmp3->path);
|
|
|
|
if((err == DB_E_SUCCESS) && (count == 1)) { /* we should update */
|
|
return db_sql_update(pe,pmp3,id);
|
|
} else if((err != DB_E_SUCCESS) && (err != DB_E_NOROWS)) {
|
|
DPRINTF(E_LOG,L_DB,"Error: %s\n",pe);
|
|
return err;
|
|
}
|
|
|
|
}
|
|
|
|
pmp3->play_count=0;
|
|
pmp3->time_played=0;
|
|
|
|
err=db_sql_exec_fn(pe,E_DBG,"INSERT INTO songs VALUES "
|
|
"(NULL," // id
|
|
"'%q'," // path
|
|
"'%q'," // fname
|
|
"'%q'," // title
|
|
"'%q'," // artist
|
|
"'%q'," // album
|
|
"'%q'," // genre
|
|
"'%q'," // comment
|
|
"'%q'," // type
|
|
"'%q'," // composer
|
|
"'%q'," // orchestra
|
|
"'%q'," // conductor
|
|
"'%q'," // grouping
|
|
"'%q'," // url
|
|
"%d," // bitrate
|
|
"%d," // samplerate
|
|
"%d," // song_length
|
|
"%d," // file_size
|
|
"%d," // year
|
|
"%d," // track
|
|
"%d," // total_tracks
|
|
"%d," // disc
|
|
"%d," // total_discs
|
|
"%d," // bpm
|
|
"%d," // compilation
|
|
"%d," // rating
|
|
"0," // play_count
|
|
"%d," // data_kind
|
|
"%d," // item_kind
|
|
"'%q'," // description
|
|
"%d," // time_added
|
|
"%d," // time_modified
|
|
"%d," // time_played
|
|
"%d," // db_timestamp
|
|
"%d," // disabled
|
|
"%d," // sample_count
|
|
"0," // force_update
|
|
"'%q'," // codectype
|
|
"%d," // index
|
|
"%d," // has_video
|
|
"%d)", // contentrating
|
|
STR(pmp3->path),
|
|
STR(pmp3->fname),
|
|
STR(pmp3->title),
|
|
STR(pmp3->artist),
|
|
STR(pmp3->album),
|
|
STR(pmp3->genre),
|
|
STR(pmp3->comment),
|
|
STR(pmp3->type),
|
|
STR(pmp3->composer),
|
|
STR(pmp3->orchestra),
|
|
STR(pmp3->conductor),
|
|
STR(pmp3->grouping),
|
|
STR(pmp3->url),
|
|
pmp3->bitrate,
|
|
pmp3->samplerate,
|
|
pmp3->song_length,
|
|
pmp3->file_size,
|
|
pmp3->year,
|
|
pmp3->track,
|
|
pmp3->total_tracks,
|
|
pmp3->disc,
|
|
pmp3->total_discs,
|
|
pmp3->bpm,
|
|
pmp3->compilation,
|
|
pmp3->rating,
|
|
pmp3->data_kind,
|
|
pmp3->item_kind,
|
|
STR(pmp3->description),
|
|
pmp3->time_added,
|
|
pmp3->time_modified,
|
|
pmp3->time_played,
|
|
pmp3->db_timestamp,
|
|
pmp3->disabled,
|
|
pmp3->sample_count,
|
|
STR(pmp3->codectype),
|
|
pmp3->index,
|
|
pmp3->has_video,
|
|
pmp3->contentrating);
|
|
|
|
if(err != DB_E_SUCCESS)
|
|
DPRINTF(E_FATAL,L_DB,"Error inserting file %s in database\n",pmp3->fname);
|
|
|
|
insertid = db_sql_insert_id_fn();
|
|
if((db_sql_in_scan)&&(!db_sql_reload)) {
|
|
db_sql_exec_fn(NULL,E_FATAL,"insert into updated values (%d)",
|
|
insertid);
|
|
}
|
|
|
|
if((!db_sql_in_scan) && (!db_sql_in_playlist_scan))
|
|
db_sql_update_playlists(NULL);
|
|
|
|
if(id)
|
|
*id = insertid;
|
|
|
|
DPRINTF(E_SPAM,L_DB,"Exiting db_sql_add\n");
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* update a database item
|
|
*
|
|
* @param pmp3 mp3 file to update
|
|
*/
|
|
int db_sql_update(char **pe, MP3FILE *pmp3, int *id) {
|
|
int err;
|
|
|
|
if(!pmp3->time_modified)
|
|
pmp3->time_modified = (int)time(NULL);
|
|
|
|
pmp3->db_timestamp = (int)time(NULL);
|
|
|
|
/* FIXME: this should update all fields */
|
|
err=db_sql_exec_fn(pe,E_LOG,"UPDATE songs SET "
|
|
"title='%q'," // title
|
|
"artist='%q'," // artist
|
|
"album='%q'," // album
|
|
"genre='%q'," // genre
|
|
"comment='%q'," // comment
|
|
"type='%q'," // type
|
|
"composer='%q'," // composer
|
|
"orchestra='%q'," // orchestra
|
|
"conductor='%q'," // conductor
|
|
"grouping='%q'," // grouping
|
|
"url='%q'," // url
|
|
"bitrate=%d," // bitrate
|
|
"samplerate=%d," // samplerate
|
|
"song_length=%d," // song_length
|
|
"file_size=%d," // file_size
|
|
"year=%d," // year
|
|
"track=%d," // track
|
|
"total_tracks=%d," // total_tracks
|
|
"disc=%d," // disc
|
|
"total_discs=%d," // total_discs
|
|
"time_modified=%d," // time_modified
|
|
"db_timestamp=%d," // db_timestamp
|
|
"bpm=%d," // bpm
|
|
"disabled=%d," // disabled
|
|
"compilation=%d," // compilation
|
|
"rating=%d," // rating
|
|
"sample_count=%d," // sample_count
|
|
"codectype='%q'" // codec
|
|
" WHERE path='%q'",
|
|
STR(pmp3->title),
|
|
STR(pmp3->artist),
|
|
STR(pmp3->album),
|
|
STR(pmp3->genre),
|
|
STR(pmp3->comment),
|
|
STR(pmp3->type),
|
|
STR(pmp3->composer),
|
|
STR(pmp3->orchestra),
|
|
STR(pmp3->conductor),
|
|
STR(pmp3->grouping),
|
|
STR(pmp3->url),
|
|
pmp3->bitrate,
|
|
pmp3->samplerate,
|
|
pmp3->song_length,
|
|
pmp3->file_size,
|
|
pmp3->year,
|
|
pmp3->track,
|
|
pmp3->total_tracks,
|
|
pmp3->disc,
|
|
pmp3->total_discs,
|
|
pmp3->time_modified,
|
|
pmp3->db_timestamp,
|
|
pmp3->bpm,
|
|
pmp3->disabled,
|
|
pmp3->compilation,
|
|
pmp3->rating,
|
|
pmp3->sample_count,
|
|
STR(pmp3->codectype),
|
|
pmp3->path);
|
|
|
|
|
|
if(err != DB_E_SUCCESS)
|
|
DPRINTF(E_FATAL,L_DB,"Error updating file: %s\n",pmp3->fname);
|
|
|
|
if(id) { /* we need the insert/update id */
|
|
err=db_sql_fetch_int(pe,id,"select id from songs where path='%q'",pmp3->path);
|
|
if(err != DB_E_SUCCESS)
|
|
return err;
|
|
}
|
|
|
|
if((db_sql_in_scan) && (!db_sql_reload)) {
|
|
if(id) {
|
|
db_sql_exec_fn(NULL,E_FATAL,"insert into updated (id) values (%d)",*id);
|
|
} else {
|
|
db_sql_exec_fn(NULL,E_FATAL,"insert into updated (id) "
|
|
"select id from songs where path='%q'",
|
|
pmp3->path);
|
|
}
|
|
}
|
|
|
|
if((!db_sql_in_scan) && (!db_sql_in_playlist_scan))
|
|
db_sql_update_playlists(NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the playlist item counts
|
|
*/
|
|
int db_sql_update_playlists(char **pe) {
|
|
typedef struct tag_plinfo {
|
|
char *plid;
|
|
char *type;
|
|
char *clause;
|
|
} PLINFO;
|
|
|
|
PLINFO *pinfo;
|
|
int playlists;
|
|
int err;
|
|
int index;
|
|
SQL_ROW row;
|
|
char *where_clause;
|
|
|
|
/* FIXME: There is a race here for externally added playlists */
|
|
|
|
err = db_sql_fetch_int(pe,&playlists,"select count(*) from playlists");
|
|
|
|
if(err != DB_E_SUCCESS) {
|
|
return err;
|
|
}
|
|
|
|
pinfo = (PLINFO*)malloc(sizeof(PLINFO) * playlists);
|
|
if(!pinfo) {
|
|
DPRINTF(E_FATAL,L_DB,"Malloc error\n");
|
|
}
|
|
|
|
/* now, let's walk through the table */
|
|
err = db_sql_enum_begin_fn(pe,"select * from playlists");
|
|
if(err != DB_E_SUCCESS)
|
|
return err;
|
|
|
|
/* otherwise, walk the table */
|
|
index=0;
|
|
while((db_sql_enum_fetch_fn(pe, &row) == DB_E_SUCCESS) && (row) &&
|
|
(index < playlists))
|
|
{
|
|
/* process row */
|
|
pinfo[index].plid=strdup(STR(row[0]));
|
|
pinfo[index].type=strdup(STR(row[1]));
|
|
pinfo[index].clause=strdup(STR(row[2]));
|
|
DPRINTF(E_SPAM,L_DB,"Found playlist %s: type %s, clause %s\n",pinfo[index].plid,
|
|
pinfo[index].type,pinfo[index].clause);
|
|
index++;
|
|
}
|
|
db_sql_enum_end_fn(pe);
|
|
if(index != playlists) {
|
|
DPRINTF(E_FATAL,L_DB,"Playlist count mismatch -- transaction problem?\n");
|
|
}
|
|
|
|
/* Now, update the playlists */
|
|
for(index=0;index < playlists; index++) {
|
|
if(atoi(pinfo[index].type) == 1) {
|
|
/* smart */
|
|
if(!db_sql_parse_smart(NULL,&where_clause,pinfo[index].clause)) {
|
|
DPRINTF(E_LOG,L_DB,"Playlist %d bad syntax",pinfo[index].plid);
|
|
where_clause = strdup("0");
|
|
}
|
|
db_sql_exec_fn(NULL,E_FATAL,"update playlists set items=("
|
|
"select count(*) from songs where %s) "
|
|
"where id=%s",where_clause,pinfo[index].plid);
|
|
free(where_clause);
|
|
} else {
|
|
db_sql_exec_fn(NULL,E_FATAL,"update playlists set items=("
|
|
"select count(*) from playlistitems where "
|
|
"playlistid=%s) where id=%s",
|
|
pinfo[index].plid, pinfo[index].plid);
|
|
}
|
|
|
|
if(pinfo[index].plid) free(pinfo[index].plid);
|
|
if(pinfo[index].type) free(pinfo[index].type);
|
|
if(pinfo[index].clause) free(pinfo[index].clause);
|
|
}
|
|
|
|
free(pinfo);
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
* start enum based on the DBQUERYINFO struct passed
|
|
*
|
|
* @param pinfo DBQUERYINFO struct detailing what to enum
|
|
*/
|
|
int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo) {
|
|
char scratch[4096];
|
|
char query[4096];
|
|
char query_select[255];
|
|
char query_count[255];
|
|
char query_rest[4096];
|
|
char *where_clause;
|
|
char *filter;
|
|
|
|
int is_smart;
|
|
int have_clause=0;
|
|
int err;
|
|
int browse=0;
|
|
int results=0;
|
|
SQL_ROW temprow;
|
|
|
|
query[0] = '\0';
|
|
query_select[0] = '\0';
|
|
query_count[0] = '\0';
|
|
query_rest[0] = '\0';
|
|
|
|
|
|
/* get the where qualifier to filter based on playlist, if there */
|
|
if((pinfo->playlist_id) && (pinfo->playlist_id != 1)) {
|
|
err = db_sql_enum_begin_fn(pe, "select type,query from playlists "
|
|
"where id=%d",pinfo->playlist_id);
|
|
|
|
if(err != DB_E_SUCCESS)
|
|
return err;
|
|
|
|
err = db_sql_enum_fetch_fn(pe,&temprow);
|
|
|
|
if(err != DB_E_SUCCESS) {
|
|
db_sql_enum_end_fn(NULL);
|
|
return err;
|
|
}
|
|
|
|
if(!temprow) { /* bad playlist */
|
|
db_get_error(pe,DB_E_INVALID_PLAYLIST);
|
|
db_sql_enum_end_fn(NULL);
|
|
return DB_E_INVALID_PLAYLIST;
|
|
}
|
|
|
|
is_smart=(atoi(temprow[0]) == 1);
|
|
if(is_smart) {
|
|
if(!db_sql_parse_smart(NULL,&where_clause,temprow[1]))
|
|
where_clause = strdup("0");
|
|
|
|
if(!where_clause) {
|
|
db_sql_enum_end_fn(NULL);
|
|
db_get_error(pe,DB_E_PARSE);
|
|
return DB_E_PARSE;
|
|
}
|
|
|
|
snprintf(query_rest,sizeof(query_rest)," where (%s)",where_clause);
|
|
free(where_clause);
|
|
} else {
|
|
|
|
/* We need to fix playlist queries to stop
|
|
* pulling the whole song db... the performance
|
|
* of these playlist queries sucks.
|
|
*/
|
|
|
|
sprintf(query_rest," where (songs.id in (select songid from "
|
|
"playlistitems where playlistid=%d))",
|
|
pinfo->playlist_id);
|
|
/*
|
|
sprintf(query_playlist,"(songs.id=playlistitems.songid and "
|
|
"playlistitems.playlistid=%d) order by "
|
|
"playlistitems.id",pinfo->playlist_id);
|
|
*/
|
|
}
|
|
have_clause=1;
|
|
db_sql_enum_end_fn(NULL);
|
|
}
|
|
|
|
switch(pinfo->query_type) {
|
|
case queryTypeItems:
|
|
strcpy(query_select,"select * from songs ");
|
|
strcpy(query_count,"select count (*) from songs ");
|
|
break;
|
|
|
|
case queryTypePlaylists:
|
|
strcpy(query_select,"select * from playlists ");
|
|
strcpy(query_count,"select count (*) from playlists ");
|
|
// sprintf(query_rest,"where (%s)",query_playlist);
|
|
break;
|
|
|
|
case queryTypePlaylistItems: /* Figure out if it's smart or dull */
|
|
sprintf(query_select,"select * from songs ");
|
|
sprintf(query_count,"select count(id) from songs ");
|
|
break;
|
|
|
|
/* Note that sqlite doesn't support COUNT(DISTINCT x) */
|
|
case queryTypeBrowseAlbums:
|
|
strcpy(query_select,"select distinct album from songs ");
|
|
strcpy(query_count,"select count(album) from (select "
|
|
"distinct album from songs ");
|
|
browse=1;
|
|
break;
|
|
|
|
case queryTypeBrowseArtists:
|
|
strcpy(query_select,"select distinct artist from songs ");
|
|
strcpy(query_count,"select count(artist) from (select "
|
|
"distinct artist from songs ");
|
|
browse=1;
|
|
break;
|
|
|
|
case queryTypeBrowseGenres:
|
|
strcpy(query_select,"select distinct genre from songs ");
|
|
strcpy(query_count,"select count(genre) from (select "
|
|
"distinct genre from songs ");
|
|
browse=1;
|
|
break;
|
|
|
|
case queryTypeBrowseComposers:
|
|
strcpy(query_select,"select distinct composer from songs ");
|
|
strcpy(query_count,"select count(composer) from (select "
|
|
"distinct composer from songs ");
|
|
browse=1;
|
|
break;
|
|
default:
|
|
DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Apply the query/filter */
|
|
if(pinfo->pt) {
|
|
DPRINTF(E_DBG,L_DB,"Got query/filter\n");
|
|
filter = sp_sql_clause(pinfo->pt);
|
|
if(filter) {
|
|
if(have_clause) {
|
|
strcat(query_rest," and ");
|
|
} else {
|
|
strcpy(query_rest," where ");
|
|
}
|
|
strcat(query_rest,"(");
|
|
strcat(query_rest,filter);
|
|
strcat(query_rest,")");
|
|
free(filter);
|
|
} else {
|
|
DPRINTF(E_LOG,L_DB,"Error getting sql for parse tree\n");
|
|
}
|
|
} else {
|
|
DPRINTF(E_DBG,L_DB,"No query/filter\n");
|
|
}
|
|
|
|
if((pinfo->index_type != indexTypeNone) || (pinfo->want_count)) {
|
|
/* the only time returned count is not specifiedtotalcount
|
|
* is if we have an index. */
|
|
strcpy(scratch,query_count);
|
|
strcat(scratch,query_rest);
|
|
if(browse)
|
|
strcat(scratch,")");
|
|
|
|
err = db_sql_fetch_int(pe,&results,"%s",scratch);
|
|
if(err != DB_E_SUCCESS)
|
|
return err;
|
|
DPRINTF(E_DBG,L_DB,"Number of results: %d\n",results);
|
|
pinfo->specifiedtotalcount = results;
|
|
}
|
|
|
|
strcpy(query,query_select);
|
|
strcat(query,query_rest);
|
|
|
|
/* FIXME: sqlite specific */
|
|
/* Apply any index */
|
|
switch(pinfo->index_type) {
|
|
case indexTypeFirst:
|
|
sprintf(scratch," limit %d",pinfo->index_low);
|
|
break;
|
|
case indexTypeLast:
|
|
if(pinfo->index_low >= results) {
|
|
sprintf(scratch," limit %d",pinfo->index_low); /* unnecessary */
|
|
} else {
|
|
sprintf(scratch," limit %d offset %d",pinfo->index_low, results-pinfo->index_low);
|
|
}
|
|
break;
|
|
case indexTypeSub:
|
|
sprintf(scratch," limit %d offset %d",
|
|
pinfo->index_high - pinfo->index_low + 1,
|
|
pinfo->index_low);
|
|
break;
|
|
case indexTypeNone:
|
|
break;
|
|
default:
|
|
DPRINTF(E_LOG,L_DB,"Bad indexType: %d\n",(int)pinfo->index_type);
|
|
scratch[0]='\0';
|
|
break;
|
|
}
|
|
|
|
if(pinfo->index_type != indexTypeNone)
|
|
strcat(query,scratch);
|
|
|
|
/* start fetching... */
|
|
err=db_sql_enum_begin_fn(pe,"%s",query);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* find the size of the response by walking through the query and
|
|
* sizing it
|
|
*
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size) {
|
|
int err;
|
|
int record_size;
|
|
SQL_ROW row;
|
|
|
|
DPRINTF(E_DBG,L_DB,"Enumerating size\n");
|
|
|
|
*count=0;
|
|
*total_size = 0;
|
|
|
|
while(((err=db_sql_enum_fetch_fn(pe,&row)) == DB_E_SUCCESS) && (row)) {
|
|
if((record_size = db_sql_get_size(pinfo,row))) {
|
|
*total_size += record_size;
|
|
*count = *count + 1;
|
|
}
|
|
}
|
|
|
|
if(err != DB_E_SUCCESS) {
|
|
db_sql_enum_end_fn(NULL);
|
|
return err;
|
|
}
|
|
|
|
err=db_sql_enum_restart_fn(pe);
|
|
|
|
DPRINTF(E_DBG,L_DB,"Got size: %d\n",*total_size);
|
|
return err;
|
|
}
|
|
|
|
|
|
/**
|
|
* fetch the next row in raw row format
|
|
*/
|
|
int db_sql_enum_fetch_row(char **pe, PACKED_MP3FILE *row, DBQUERYINFO *pinfo) {
|
|
int err;
|
|
|
|
err=db_sql_enum_fetch_fn(pe, (char***)row);
|
|
if(err != DB_E_SUCCESS) {
|
|
db_sql_enum_end_fn(NULL);
|
|
return err;
|
|
}
|
|
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* fetch the next record from the enum
|
|
*/
|
|
int db_sql_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap) {
|
|
int err;
|
|
int result_size=0;
|
|
unsigned char *presult;
|
|
SQL_ROW row;
|
|
|
|
err=db_sql_enum_fetch_fn(pe, &row);
|
|
if(err != DB_E_SUCCESS) {
|
|
db_sql_enum_end_fn(NULL);
|
|
return err;
|
|
}
|
|
|
|
if(row) {
|
|
result_size = db_sql_get_size(pinfo,row);
|
|
if(result_size) {
|
|
presult = (unsigned char*)malloc(result_size);
|
|
if(!presult) {
|
|
DPRINTF(E_FATAL,L_DB,"Malloc error\n");
|
|
}
|
|
|
|
db_sql_build_dmap(pinfo,row,presult,result_size);
|
|
*pdmap=presult;
|
|
*size = result_size;
|
|
}
|
|
} else {
|
|
*size = 0;
|
|
}
|
|
|
|
return DB_E_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* start the enum again
|
|
*/
|
|
int db_sql_enum_reset(char **pe, DBQUERYINFO *pinfo) {
|
|
return db_sql_enum_restart_fn(pe);
|
|
}
|
|
|
|
|
|
/**
|
|
* stop the enum
|
|
*/
|
|
int db_sql_enum_end(char **pe) {
|
|
return db_sql_enum_end_fn(pe);
|
|
}
|
|
|
|
/**
|
|
* get the size of the generated dmap, given a specific meta
|
|
*/
|
|
int db_sql_get_size(DBQUERYINFO *pinfo, SQL_ROW valarray) {
|
|
int size;
|
|
int transcode;
|
|
|
|
switch(pinfo->query_type) {
|
|
case queryTypeBrowseArtists: /* simple 'mlit' entry */
|
|
case queryTypeBrowseAlbums:
|
|
case queryTypeBrowseGenres:
|
|
case queryTypeBrowseComposers:
|
|
return valarray[0] ? (8 + (int) strlen(valarray[0])) : 0;
|
|
case queryTypePlaylists:
|
|
size = 8; /* mlit */
|
|
size += 12; /* mimc - you get it whether you want it or not */
|
|
if(db_wantsmeta(pinfo->meta, metaItemId))
|
|
size += 12; /* miid */
|
|
if(db_wantsmeta(pinfo->meta, metaItunesSmartPlaylist)) {
|
|
if(valarray[plType] && (atoi(valarray[plType])==1))
|
|
size += 9; /* aeSP */
|
|
}
|
|
if(db_wantsmeta(pinfo->meta, metaItemName))
|
|
size += (8 + (int) strlen(valarray[plTitle])); /* minm */
|
|
if(valarray[plType] && (atoi(valarray[plType])==1) &&
|
|
db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
|
|
size += (8 + (int) strlen(valarray[plQuery])); /* MSPS */
|
|
if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
|
|
size += 9; /* MPTY */
|
|
return size;
|
|
break;
|
|
case queryTypeItems:
|
|
case queryTypePlaylistItems: /* essentially the same query */
|
|
/* see if this is going to be transcoded */
|
|
transcode = server_side_convert(valarray[37]);
|
|
|
|
/* Items that get changed by transcode:
|
|
*
|
|
* type: item 8: changes to 'wav'
|
|
* description: item 29: changes to 'wav audio file'
|
|
* bitrate: item 15: guestimated, based on item 15, samplerate
|
|
*
|
|
* probably file size should change as well, but currently doesn't
|
|
*/
|
|
|
|
size = 8; /* mlit */
|
|
if(db_wantsmeta(pinfo->meta, metaItemKind))
|
|
/* mikd */
|
|
size += 9;
|
|
if(db_wantsmeta(pinfo->meta, metaSongDataKind))
|
|
/* asdk */
|
|
size += 9;
|
|
if(db_wantsmeta(pinfo->meta, metaSongDataURL))
|
|
/* asul */
|
|
size += DMAPLEN(valarray[13]);
|
|
if(db_wantsmeta(pinfo->meta, metaSongAlbum))
|
|
/* asal */
|
|
size += DMAPLEN(valarray[5]);
|
|
if(db_wantsmeta(pinfo->meta, metaSongArtist))
|
|
/* asar */
|
|
size += DMAPLEN(valarray[4]);
|
|
if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
|
|
/* asbt */
|
|
size += 10;
|
|
if(db_wantsmeta(pinfo->meta, metaSongBitRate)) {
|
|
/* asbr */
|
|
if(transcode) {
|
|
if(valarray[15] && atoi(valarray[15]))
|
|
size += 10;
|
|
} else {
|
|
if(valarray[14] && atoi(valarray[14]))
|
|
size += 10;
|
|
}
|
|
}
|
|
if(db_wantsmeta(pinfo->meta, metaSongComment))
|
|
/* ascm */
|
|
size += DMAPLEN(valarray[7]);
|
|
if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
|
|
/* asco */
|
|
size += 9;
|
|
if(db_wantsmeta(pinfo->meta, metaSongComposer))
|
|
/* ascp */
|
|
size += DMAPLEN(valarray[9]);
|
|
if(db_wantsmeta(pinfo->meta, metaSongGrouping))
|
|
/* agrp */
|
|
size += DMAPLEN(valarray[12]);
|
|
if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
|
|
/* asda */
|
|
size += 12;
|
|
if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
|
|
/* asdm */
|
|
size += 12;
|
|
if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
|
|
/* asdc */
|
|
size += 10;
|
|
if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
|
|
/* asdn */
|
|
size += 10;
|
|
if(db_wantsmeta(pinfo->meta, metaSongGenre))
|
|
/* asgn */
|
|
size += DMAPLEN(valarray[6]);
|
|
if(db_wantsmeta(pinfo->meta,metaItemId))
|
|
/* miid */
|
|
size += 12;
|
|
if(db_wantsmeta(pinfo->meta,metaSongFormat)) {
|
|
/* asfm */
|
|
if(transcode) {
|
|
size += 11; /* 'wav' */
|
|
} else {
|
|
size += DMAPLEN(valarray[8]);
|
|
}
|
|
}
|
|
if(db_wantsmeta(pinfo->meta,metaSongDescription)) {
|
|
/* asdt */
|
|
if(transcode) {
|
|
size += 22; /* 'wav audio file' */
|
|
} else {
|
|
size += DMAPLEN(valarray[29]);
|
|
}
|
|
}
|
|
if(db_wantsmeta(pinfo->meta,metaItemName))
|
|
/* minm */
|
|
size += DMAPLEN(valarray[3]);
|
|
if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
|
|
/* asdb */
|
|
size += 9;
|
|
if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
|
|
/* assr */
|
|
size += 12;
|
|
if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
|
|
/* assz */
|
|
size += 12;
|
|
|
|
/* In the old daap code, we always returned 0 for asst and assp
|
|
* (song start time, song stop time). I don't know if this
|
|
* is required, so I'm going to disabled it
|
|
*/
|
|
|
|
if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
|
|
/* astm */
|
|
size += 12;
|
|
if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
|
|
/* astc */
|
|
size += 10;
|
|
if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
|
|
/* astn */
|
|
size += 10;
|
|
if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
|
|
/* asur */
|
|
size += 9;
|
|
if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
|
|
/* asyr */
|
|
size += 10;
|
|
if(db_wantsmeta(pinfo->meta, metaContainerItemId))
|
|
/* mcti */
|
|
size += 12;
|
|
if((valarray[37]) && (strlen(valarray[37]) == 4) &&
|
|
db_wantsmeta(pinfo->meta,metaSongCodecType))
|
|
/* ascd */
|
|
size += 12;
|
|
|
|
if(db_wantsmeta(pinfo->meta,metaSongContentRating))
|
|
/* ascr */
|
|
size += 9;
|
|
if(db_wantsmeta(pinfo->meta,metaItunesHasVideo))
|
|
/* aeHV */
|
|
size += 9;
|
|
|
|
return size;
|
|
break;
|
|
|
|
default:
|
|
DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int db_sql_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len) {
|
|
unsigned char *current = presult;
|
|
int transcode;
|
|
int samplerate=0;
|
|
|
|
switch(pinfo->query_type) {
|
|
case queryTypeBrowseArtists: /* simple 'mlit' entry */
|
|
case queryTypeBrowseAlbums:
|
|
case queryTypeBrowseGenres:
|
|
case queryTypeBrowseComposers:
|
|
return db_dmap_add_string(current,"mlit",valarray[0]);
|
|
case queryTypePlaylists:
|
|
/* do I want to include the mlit? */
|
|
current += db_dmap_add_container(current,"mlit",len - 8);
|
|
if(db_wantsmeta(pinfo->meta,metaItemId))
|
|
current += db_dmap_add_int(current,"miid",atoi(valarray[plID]));
|
|
current += db_dmap_add_int(current,"mimc",atoi(valarray[plItems]));
|
|
if(db_wantsmeta(pinfo->meta,metaItunesSmartPlaylist)) {
|
|
if(valarray[plType] && (atoi(valarray[plType]) == 1))
|
|
current += db_dmap_add_char(current,"aeSP",1);
|
|
}
|
|
if(db_wantsmeta(pinfo->meta,metaItemName))
|
|
current += db_dmap_add_string(current,"minm",valarray[plTitle]);
|
|
if((valarray[plType]) && (atoi(valarray[plType])==1) &&
|
|
db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
|
|
current += db_dmap_add_string(current,"MSPS",valarray[plQuery]);
|
|
if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
|
|
current += db_dmap_add_char(current,"MPTY",atoi(valarray[plType]));
|
|
break;
|
|
case queryTypeItems:
|
|
case queryTypePlaylistItems: /* essentially the same query */
|
|
/* see if this is going to be transcoded */
|
|
transcode = server_side_convert(valarray[37]);
|
|
|
|
/* Items that get changed by transcode:
|
|
*
|
|
* type: item 8: changes to 'wav'
|
|
* description: item 29: changes to 'wav audio file'
|
|
* bitrate: item 15: guestimated, but doesn't change file size
|
|
*
|
|
* probably file size should change as well, but currently doesn't
|
|
*/
|
|
|
|
current += db_dmap_add_container(current,"mlit",len-8);
|
|
if(db_wantsmeta(pinfo->meta, metaItemKind))
|
|
current += db_dmap_add_char(current,"mikd",(char)atoi(valarray[28]));
|
|
if(db_wantsmeta(pinfo->meta, metaSongDataKind))
|
|
current += db_dmap_add_char(current,"asdk",(char)atoi(valarray[27]));
|
|
if(EMIT(valarray[13]) && db_wantsmeta(pinfo->meta, metaSongDataURL))
|
|
current += db_dmap_add_string(current,"asul",valarray[13]);
|
|
if(EMIT(valarray[5]) && db_wantsmeta(pinfo->meta, metaSongAlbum))
|
|
current += db_dmap_add_string(current,"asal",valarray[5]);
|
|
if(EMIT(valarray[4]) && db_wantsmeta(pinfo->meta, metaSongArtist))
|
|
current += db_dmap_add_string(current,"asar",valarray[4]);
|
|
if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
|
|
current += db_dmap_add_short(current,"asbt",(short)atoi(valarray[23]));
|
|
if(valarray[14] && atoi(valarray[14]) && db_wantsmeta(pinfo->meta, metaSongBitRate)) {
|
|
if(transcode) {
|
|
if(valarray[15]) samplerate=atoi(valarray[15]);
|
|
if(samplerate) {
|
|
current += db_dmap_add_short(current,"asbr",
|
|
(short)(samplerate * 4 * 8)/1000);
|
|
}
|
|
} else {
|
|
current += db_dmap_add_short(current,"asbr",(short)atoi(valarray[14]));
|
|
}
|
|
}
|
|
if(EMIT(valarray[7]) && db_wantsmeta(pinfo->meta, metaSongComment))
|
|
current += db_dmap_add_string(current,"ascm",valarray[7]);
|
|
if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
|
|
current += db_dmap_add_char(current,"asco",(char)atoi(valarray[24]));
|
|
if(EMIT(valarray[9]) && db_wantsmeta(pinfo->meta, metaSongComposer))
|
|
current += db_dmap_add_string(current,"ascp",valarray[9]);
|
|
if(EMIT(valarray[12]) && db_wantsmeta(pinfo->meta, metaSongGrouping))
|
|
current += db_dmap_add_string(current,"agrp",valarray[12]);
|
|
if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
|
|
current += db_dmap_add_int(current,"asda",(int)atoi(valarray[30]));
|
|
if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
|
|
current += db_dmap_add_int(current,"asdm",(int)atoi(valarray[31]));
|
|
if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
|
|
current += db_dmap_add_short(current,"asdc",(short)atoi(valarray[22]));
|
|
if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
|
|
current += db_dmap_add_short(current,"asdn",(short)atoi(valarray[21]));
|
|
if(EMIT(valarray[6]) && db_wantsmeta(pinfo->meta, metaSongGenre))
|
|
current += db_dmap_add_string(current,"asgn",valarray[6]);
|
|
if(db_wantsmeta(pinfo->meta,metaItemId))
|
|
current += db_dmap_add_int(current,"miid",(int)atoi(valarray[0]));
|
|
if(EMIT(valarray[8]) && db_wantsmeta(pinfo->meta,metaSongFormat)) {
|
|
if(transcode) {
|
|
current += db_dmap_add_string(current,"asfm","wav");
|
|
} else {
|
|
current += db_dmap_add_string(current,"asfm",valarray[8]);
|
|
}
|
|
}
|
|
if(EMIT(valarray[29]) && db_wantsmeta(pinfo->meta,metaSongDescription)) {
|
|
if(transcode) {
|
|
current += db_dmap_add_string(current,"asdt","wav audio file");
|
|
} else {
|
|
current += db_dmap_add_string(current,"asdt",valarray[29]);
|
|
}
|
|
}
|
|
if(EMIT(valarray[3]) && db_wantsmeta(pinfo->meta,metaItemName))
|
|
current += db_dmap_add_string(current,"minm",valarray[3]);
|
|
if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
|
|
current += db_dmap_add_char(current,"asdb",(char)atoi(valarray[34]));
|
|
if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
|
|
current += db_dmap_add_int(current,"assr",atoi(valarray[15]));
|
|
if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
|
|
current += db_dmap_add_int(current,"assz",atoi(valarray[17]));
|
|
if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
|
|
current += db_dmap_add_int(current,"astm",atoi(valarray[16]));
|
|
if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
|
|
current += db_dmap_add_short(current,"astc",(short)atoi(valarray[20]));
|
|
if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
|
|
current += db_dmap_add_short(current,"astn",(short)atoi(valarray[19]));
|
|
if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
|
|
current += db_dmap_add_char(current,"asur",(char)atoi(valarray[25]));
|
|
if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
|
|
current += db_dmap_add_short(current,"asyr",(short)atoi(valarray[18]));
|
|
if((valarray[37]) && (strlen(valarray[37]) == 4) &&
|
|
db_wantsmeta(pinfo->meta,metaSongCodecType))
|
|
current += db_dmap_add_literal(current,"ascd",valarray[37],4);
|
|
if(db_wantsmeta(pinfo->meta, metaContainerItemId))
|
|
current += db_dmap_add_int(current,"mcti",atoi(valarray[0]));
|
|
if(db_wantsmeta(pinfo->meta, metaItunesHasVideo))
|
|
current += db_dmap_add_char(current,"aeHV",atoi(valarray[39]));
|
|
if(db_wantsmeta(pinfo->meta, metaSongContentRating))
|
|
current += db_dmap_add_char(current,"ascr",atoi(valarray[40]));
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int db_sql_atoi(const char *what) {
|
|
return what ? atoi(what) : 0;
|
|
}
|
|
char *db_sql_strdup(const char *what) {
|
|
return what ? (strlen(what) ? strdup(what) : NULL) : NULL;
|
|
}
|
|
|
|
void db_sql_build_m3ufile(SQL_ROW valarray, M3UFILE *pm3u) {
|
|
memset(pm3u,0x00,sizeof(M3UFILE));
|
|
|
|
pm3u->id=db_sql_atoi(valarray[0]);
|
|
pm3u->title=db_sql_strdup(valarray[1]);
|
|
pm3u->type=db_sql_atoi(valarray[2]);
|
|
pm3u->items=db_sql_atoi(valarray[3]);
|
|
pm3u->query=db_sql_strdup(valarray[4]);
|
|
pm3u->db_timestamp=db_sql_atoi(valarray[5]);
|
|
pm3u->path=db_sql_strdup(valarray[6]);
|
|
pm3u->index=db_sql_atoi(valarray[7]);
|
|
return;
|
|
}
|
|
|
|
void db_sql_build_mp3file(SQL_ROW valarray, MP3FILE *pmp3) {
|
|
memset(pmp3,0x00,sizeof(MP3FILE));
|
|
pmp3->id=db_sql_atoi(valarray[0]);
|
|
pmp3->path=db_sql_strdup(valarray[1]);
|
|
pmp3->fname=db_sql_strdup(valarray[2]);
|
|
pmp3->title=db_sql_strdup(valarray[3]);
|
|
pmp3->artist=db_sql_strdup(valarray[4]);
|
|
pmp3->album=db_sql_strdup(valarray[5]);
|
|
pmp3->genre=db_sql_strdup(valarray[6]);
|
|
pmp3->comment=db_sql_strdup(valarray[7]);
|
|
pmp3->type=db_sql_strdup(valarray[8]);
|
|
pmp3->composer=db_sql_strdup(valarray[9]);
|
|
pmp3->orchestra=db_sql_strdup(valarray[10]);
|
|
pmp3->conductor=db_sql_strdup(valarray[11]);
|
|
pmp3->grouping=db_sql_strdup(valarray[12]);
|
|
pmp3->url=db_sql_strdup(valarray[13]);
|
|
pmp3->bitrate=db_sql_atoi(valarray[14]);
|
|
pmp3->samplerate=db_sql_atoi(valarray[15]);
|
|
pmp3->song_length=db_sql_atoi(valarray[16]);
|
|
pmp3->file_size=db_sql_atoi(valarray[17]);
|
|
pmp3->year=db_sql_atoi(valarray[18]);
|
|
pmp3->track=db_sql_atoi(valarray[19]);
|
|
pmp3->total_tracks=db_sql_atoi(valarray[20]);
|
|
pmp3->disc=db_sql_atoi(valarray[21]);
|
|
pmp3->total_discs=db_sql_atoi(valarray[22]);
|
|
pmp3->bpm=db_sql_atoi(valarray[23]);
|
|
pmp3->compilation=db_sql_atoi(valarray[24]);
|
|
pmp3->rating=db_sql_atoi(valarray[25]);
|
|
pmp3->play_count=db_sql_atoi(valarray[26]);
|
|
pmp3->data_kind=db_sql_atoi(valarray[27]);
|
|
pmp3->item_kind=db_sql_atoi(valarray[28]);
|
|
pmp3->description=db_sql_strdup(valarray[29]);
|
|
pmp3->time_added=db_sql_atoi(valarray[30]);
|
|
pmp3->time_modified=db_sql_atoi(valarray[31]);
|
|
pmp3->time_played=db_sql_atoi(valarray[32]);
|
|
pmp3->db_timestamp=db_sql_atoi(valarray[33]);
|
|
pmp3->disabled=db_sql_atoi(valarray[34]);
|
|
pmp3->sample_count=db_sql_atoi(valarray[35]);
|
|
pmp3->force_update=db_sql_atoi(valarray[36]);
|
|
pmp3->codectype=db_sql_strdup(valarray[37]);
|
|
pmp3->index=db_sql_atoi(valarray[38]);
|
|
pmp3->has_video=db_sql_atoi(valarray[39]);
|
|
pmp3->contentrating=db_sql_atoi(valarray[40]);
|
|
}
|
|
|
|
/**
|
|
* fetch a playlist by path and index
|
|
*
|
|
* @param path path to fetch
|
|
*/
|
|
M3UFILE *db_sql_fetch_playlist(char **pe, char *path, int index) {
|
|
int result;
|
|
M3UFILE *pm3u=NULL;
|
|
SQL_ROW row;
|
|
|
|
|
|
result = db_sql_fetch_row(pe, &row, "select * from playlists where "
|
|
"path='%q' and idx=%d",path,index);
|
|
|
|
if(result != DB_E_SUCCESS) {
|
|
if(result == DB_E_NOROWS) {
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_PLAYLIST);
|
|
return NULL;
|
|
}
|
|
|
|
return NULL; /* sql error or something */
|
|
}
|
|
|
|
pm3u=(M3UFILE*)malloc(sizeof(M3UFILE));
|
|
if(!pm3u)
|
|
DPRINTF(E_FATAL,L_MISC,"malloc error: db_sql_fetch_playlist\n");
|
|
|
|
db_sql_build_m3ufile(row,pm3u);
|
|
db_sql_dispose_row();
|
|
|
|
if((db_sql_in_playlist_scan) && (!db_sql_reload)) {
|
|
db_sql_exec_fn(NULL,E_FATAL,"insert into plupdated values (%d)",
|
|
pm3u->id);
|
|
}
|
|
|
|
return pm3u;
|
|
}
|
|
|
|
|
|
/* FIXME: bad error handling -- not like the rest */
|
|
|
|
/**
|
|
* fetch a MP3FILE for a specific id
|
|
*
|
|
* @param id id to fetch
|
|
*/
|
|
MP3FILE *db_sql_fetch_item(char **pe, int id) {
|
|
SQL_ROW row;
|
|
MP3FILE *pmp3=NULL;
|
|
int err;
|
|
|
|
err=db_sql_fetch_row(pe,&row,"select * from songs where id=%d",id);
|
|
if(err != DB_E_SUCCESS) {
|
|
if(err == DB_E_NOROWS) { /* Override generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_SONGID);
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
|
|
if(!pmp3)
|
|
DPRINTF(E_FATAL,L_MISC,"Malloc error in db_sql_fetch_item\n");
|
|
|
|
db_sql_build_mp3file(row,pmp3);
|
|
|
|
db_sql_dispose_row();
|
|
|
|
if ((db_sql_in_scan) && (!db_sql_reload)) {
|
|
db_sql_exec_fn(pe,E_FATAL,"INSERT INTO updated VALUES (%d)",id);
|
|
}
|
|
|
|
return pmp3;
|
|
}
|
|
|
|
/**
|
|
* retrieve a MP3FILE struct for the song with a given path
|
|
*
|
|
* @param path path of the file to retreive
|
|
*/
|
|
MP3FILE *db_sql_fetch_path(char **pe, char *path, int index) {
|
|
SQL_ROW row;
|
|
MP3FILE *pmp3=NULL;
|
|
int err;
|
|
|
|
err=db_sql_fetch_row(pe,&row,"select * from songs where path='%q'",path);
|
|
if(err != DB_E_SUCCESS) {
|
|
if(err == DB_E_NOROWS) { /* Override generic error */
|
|
if(pe) { free(*pe); };
|
|
db_get_error(pe,DB_E_INVALID_SONGID);
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
|
|
if(!pmp3)
|
|
DPRINTF(E_FATAL,L_MISC,"Malloc error in db_sql_fetch_path\n");
|
|
|
|
db_sql_build_mp3file(row,pmp3);
|
|
|
|
db_sql_dispose_row();
|
|
|
|
if ((db_sql_in_scan) && (!db_sql_reload)) {
|
|
db_sql_exec_fn(pe,E_FATAL,"INSERT INTO updated VALUES (%d)",pmp3->id);
|
|
}
|
|
|
|
return pmp3;
|
|
}
|
|
|
|
/**
|
|
* dispose of a MP3FILE struct that was obtained
|
|
* from db_sql_fetch_item
|
|
*
|
|
* @param pmp3 item obtained from db_sql_fetch_item
|
|
*/
|
|
void db_sql_dispose_item(MP3FILE *pmp3) {
|
|
if(!pmp3)
|
|
return;
|
|
|
|
MAYBEFREE(pmp3->path);
|
|
MAYBEFREE(pmp3->fname);
|
|
MAYBEFREE(pmp3->title);
|
|
MAYBEFREE(pmp3->artist);
|
|
MAYBEFREE(pmp3->album);
|
|
MAYBEFREE(pmp3->genre);
|
|
MAYBEFREE(pmp3->comment);
|
|
MAYBEFREE(pmp3->type);
|
|
MAYBEFREE(pmp3->composer);
|
|
MAYBEFREE(pmp3->orchestra);
|
|
MAYBEFREE(pmp3->conductor);
|
|
MAYBEFREE(pmp3->grouping);
|
|
MAYBEFREE(pmp3->description);
|
|
MAYBEFREE(pmp3->url);
|
|
MAYBEFREE(pmp3->codectype);
|
|
free(pmp3);
|
|
}
|
|
|
|
void db_sql_dispose_playlist(M3UFILE *pm3u) {
|
|
if(!pm3u)
|
|
return;
|
|
|
|
MAYBEFREE(pm3u->title);
|
|
MAYBEFREE(pm3u->query);
|
|
MAYBEFREE(pm3u->path);
|
|
free(pm3u);
|
|
}
|
|
|
|
/**
|
|
* count either the number of playlists, or the number of
|
|
* songs
|
|
*
|
|
* @param type either countPlaylists or countSongs (type to count)
|
|
*/
|
|
int db_sql_get_count(char **pe, int *count, CountType_t type) {
|
|
char *table;
|
|
int err;
|
|
|
|
switch(type) {
|
|
case countPlaylists:
|
|
table="playlists";
|
|
break;
|
|
|
|
case countSongs:
|
|
default:
|
|
table="songs";
|
|
break;
|
|
}
|
|
|
|
err=db_sql_fetch_int(pe,count,"select count(*) FROM %q", table);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* increment the play_count and time_played
|
|
*
|
|
* @param pe error string
|
|
* @param id id of the song to increment
|
|
* @returns DB_E_SUCCESS on success, error code otherwise
|
|
*/
|
|
int db_sql_playcount_increment(char **pe, int id) {
|
|
time_t now = time(NULL);
|
|
|
|
return db_sql_exec_fn(pe,E_INF,"update songs set play_count=play_count + 1"
|
|
", time_played=%d where id=%d",now,id);
|
|
}
|
|
|
|
|