Commit 98d2036a authored by Srikanth Raju's avatar Srikanth Raju

Media library CRUD operations

parent db662f04
/*****************************************************************************
* sql_media_library.c: SQL-based media library
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN Team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
/*****************************************************************************
* ADD FUNCTIONS
*****************************************************************************/
/**
* @brief Add element to ML based on a ml_media_t (media ID ignored)
* @param p_ml This media_library_t object
* @param p_media media item to add in the DB. The media_id is ignored
* @return VLC_SUCCESS or VLC_EGENERIC
* @note This function is threadsafe
*/
int AddMedia( media_library_t *p_ml, ml_media_t *p_media )
{
int i_ret = VLC_SUCCESS;
int i_album_artist = 0;
Begin( p_ml );
ml_LockMedia( p_media );
assert( p_media->i_id == 0 );
/* Add any people */
ml_person_t* person = p_media->p_people;
while( person )
{
if( person->i_id <= 0 )
{
if( person->psz_name )
{
person->i_id = ml_GetInt( p_ml, ML_PEOPLE_ID, person->psz_role,
ML_PEOPLE, person->psz_role,
person->psz_name );
if( person->i_id <= 0 )
{
/* Create person */
AddPeople( p_ml, person->psz_name, person->psz_role );
person->i_id = ml_GetInt( p_ml, ML_PEOPLE_ID, person->psz_role,
ML_PEOPLE, person->psz_role,
person->psz_name );
}
}
}
if( strcmp( person->psz_role, ML_PERSON_ALBUM_ARTIST ) == 0 )
i_album_artist = person->i_id;
person = person->p_next;
}
/* Album id */
if( p_media->i_album_id <= 0 )
{
if( p_media->psz_album )
{
/* TODO:Solidly incorporate Album artist */
int i_album_id = ml_GetAlbumId( p_ml, p_media->psz_album );
if( i_album_id <= 0 )
{
/* Create album */
i_ret = AddAlbum( p_ml, p_media->psz_album, p_media->psz_cover,
i_album_artist );
if( i_ret != VLC_SUCCESS )
return i_ret;
i_album_id = ml_GetAlbumId( p_ml, p_media->psz_album );
if( i_album_id <= 0 )
return i_ret;
}
p_media->i_album_id = i_album_id;
}
}
if( !p_media->psz_uri || !*p_media->psz_uri )
{
msg_Dbg( p_ml, "cannot add a media without uri (%s)", __func__ );
return VLC_EGENERIC;
}
i_ret = QuerySimple( p_ml,
"INSERT INTO media ( uri, title, original_title, genre, type, "
"comment, cover, preview, year, track, disc, album_id, vote, score, "
"duration, first_played, played_count, last_played, "
"skipped_count, last_skipped, import_time, filesize ) "
"VALUES ( %Q, %Q, %Q, %Q, '%d',%Q, %Q, %Q, '%d', '%d', '%d', '%d',"
"'%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d' )",
p_media->psz_uri,
p_media->psz_title,
p_media->psz_orig_title,
p_media->psz_genre,
(int)p_media->i_type,
p_media->psz_comment,
p_media->psz_cover,
p_media->psz_preview,
(int)p_media->i_year,
(int)p_media->i_track_number,
(int)p_media->i_disc_number,
(int)p_media->i_album_id,
(int)p_media->i_vote,
(int)p_media->i_score,
(int)p_media->i_duration,
(int)p_media->i_first_played,
(int)p_media->i_played_count,
(int)p_media->i_last_played,
(int)p_media->i_skipped_count,
(int)p_media->i_last_skipped,
(int)p_media->i_import_time,
(int)p_media->i_filesize );
if( i_ret != VLC_SUCCESS )
goto quit_addmedia;
int id = GetMediaIdOfURI( p_ml, p_media->psz_uri );
if( id <= 0 )
{
i_ret = VLC_EGENERIC;
goto quit_addmedia;
}
p_media->i_id = id;
person = p_media->p_people;
if( !person )
{
/* If there is no person, set it to "Unknown", ie. people_id=0 */
i_ret = QuerySimple( p_ml, "INSERT into media_to_people ( media_id, "
"people_id ) VALUES ( %d, %d )",
id, 0 );
if( i_ret != VLC_SUCCESS )
goto quit_addmedia;
} else {
while( person )
{
i_ret = QuerySimple( p_ml, "INSERT into media_to_people ( media_id, "
"people_id ) VALUES ( %d, %d )",
id, person->i_id );
if( i_ret != VLC_SUCCESS )
goto quit_addmedia;
person = person->p_next;
}
}
i_ret = QuerySimple( p_ml, "INSERT into extra ( id, extra, language, bitrate, "
"samplerate, bpm ) VALUES ( '%d', %Q, %Q, '%d', '%d', '%d' )",
id, p_media->psz_extra, p_media->psz_language,
p_media->i_bitrate, p_media->i_samplerate, p_media->i_bpm );
if( i_ret != VLC_SUCCESS )
goto quit_addmedia;
i_ret = pool_InsertMedia( p_ml, p_media, true );
quit_addmedia:
if( i_ret == VLC_SUCCESS )
{
Commit( p_ml );
}
else
Rollback( p_ml );
ml_UnlockMedia( p_media );
if( i_ret == VLC_SUCCESS )
var_SetInteger( p_ml, "media-added", id );
return i_ret;
}
/**
* @brief Add generic album to ML
*
* @param p_ml this Media Library
* @param psz_title album title, cannot be null
* @param psz_cover album cover, can be null
* @return VLC_SUCCESS or a VLC error code
*
* This will add a new in the album table, without checking if album is
* already present (or another album with same title)
*/
int AddAlbum( media_library_t *p_ml, const char *psz_title,
const char *psz_cover, const int i_album_artist )
{
assert( p_ml );
if( !psz_title || !*psz_title )
{
msg_Warn( p_ml, "tried to add an album without title" );
return VLC_EGENERIC;
}
msg_Dbg( p_ml, "New album: '%s'", psz_title );
int i_ret = QuerySimple( p_ml,
"INSERT INTO album ( title, cover, album_artist_id ) "
"VALUES ( %Q, %Q, '%d' )",
psz_title , psz_cover, i_album_artist );
return i_ret;
}
/**
* @brief Add generic people to ML
*
* @param p_ml this Media Library
* @param psz_title name
* @param i_role role: 1 for artist, 2 for publisher
* @return VLC_SUCCESS or a VLC error code
*
* This will add a new in the album table, without checking if album is
* already present (or another album with same title)
*/
int AddPeople( media_library_t *p_ml, const char *psz_name,
const char* psz_role )
{
assert( p_ml );
assert( psz_role && *psz_role );
if( !psz_name || !*psz_name )
{
msg_Warn( p_ml, "tried to add an artist without name" );
return VLC_EGENERIC;
}
msg_Dbg( p_ml, "New people: (%s) '%s'", psz_role, psz_name );
int i_ret = QuerySimple( p_ml,
"INSERT INTO people ( name, role ) "
"VALUES ( %Q, %Q )",
psz_name, psz_role );
return i_ret;
}
/**
* @brief Add element to ML based on an Input Item
* @param p_ml This media_library_t object
* @param p_input input item to add
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int AddInputItem( media_library_t *p_ml, input_item_t *p_input )
{
assert( p_ml );
if( !p_input )
return VLC_EGENERIC;
int i_ret = VLC_SUCCESS;
vlc_gc_incref( p_input );
/* Check input item is not already in the ML */
i_ret = GetMediaIdOfInputItem( p_ml, p_input );
if( i_ret > 0 )
{
msg_Dbg( p_ml, "Item already in Media Library (id: %d)", i_ret );
vlc_gc_decref( p_input );
return VLC_SUCCESS;
}
ml_media_t* p_media = media_New( p_ml, 0, ML_MEDIA, false );
/* Add media to the database */
CopyInputItemToMedia( p_media, p_input );
i_ret = AddMedia( p_ml, p_media );
if( i_ret == VLC_SUCCESS )
watch_add_Item( p_ml, p_input, p_media );
ml_gc_decref( p_media );
vlc_gc_decref( p_input );
return i_ret;
}
/**
* @brief Add element to ML based on a Playlist Item
*
* @param p_ml the media library object
* @param p_playlist_item playlist_item to add
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int AddPlaylistItem( media_library_t *p_ml, playlist_item_t *p_playlist_item )
{
if( !p_playlist_item )
return VLC_EGENERIC;
return AddInputItem( p_ml, p_playlist_item->p_input );
}
/*****************************************************************************
* sql_delete.c: SQL-based media library: all database delete functions
*****************************************************************************
* Copyright (C) 2008-2010 The VideoLAN Team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
/**
* @brief Generic DELETE function for many medias
* Delete a media and all its referencies which don't point
* an anything else.
*
* @param p_ml This media_library_t object
* @param p_array list of ids to delete
* @return VLC_SUCCESS or VLC_EGENERIC
* TODO: Expand to delete media/artist/album given any params
*/
int Delete( media_library_t *p_ml, vlc_array_t *p_array )
{
char *psz_idlist = NULL, *psz_tmp = NULL;
int i_return = VLC_ENOMEM;
int i_rows = 0, i_cols = 0;
char **pp_results = NULL;
if( vlc_array_count( p_array ) <= 0 )
{
i_return = VLC_SUCCESS;
goto quit_delete_final;
}
for( int i = 0; i < vlc_array_count( p_array ); i++ )
{
ml_element_t* find = ( ml_element_t * )
vlc_array_item_at_index( p_array, i );
assert( find->criteria == ML_ID );
if( !psz_idlist )
{
if( asprintf( &psz_tmp, "( %d", find->value.i ) == -1)
{
goto quit_delete_final;
}
}
else
{
if( asprintf( &psz_tmp, "%s, %d", psz_idlist,
find->value.i ) == -1)
{
goto quit_delete_final;
}
}
free( psz_idlist );
psz_idlist = psz_tmp;
psz_tmp = NULL;
}
free( psz_tmp );
if( asprintf( &psz_tmp, "%s )", psz_idlist ? psz_idlist : "(" ) == -1 )
{
goto quit_delete_final;
}
psz_idlist = psz_tmp;
psz_tmp = NULL;
msg_Dbg( p_ml, "Multi Delete id list: %s", psz_idlist );
/**
* Below ensures you are emitting media-deleted only
* for existant media
*/
Begin( p_ml );
i_return = Query( p_ml, &pp_results, &i_rows, &i_cols,
"SELECT id FROM media WHERE id IN %s", psz_idlist );
if( i_return != VLC_SUCCESS )
goto quit;
i_return = QuerySimple( p_ml,
"DELETE FROM media WHERE media.id IN %s", psz_idlist );
if( i_return != VLC_SUCCESS )
goto quit;
i_return = QuerySimple( p_ml,
"DELETE FROM extra WHERE extra.id IN %s", psz_idlist );
if( i_return != VLC_SUCCESS )
goto quit;
quit:
if( i_return == VLC_SUCCESS )
{
Commit( p_ml );
/* Emit delete on var media-deleted */
for( int i = 1; i <= i_rows; i++ )
{
var_SetInteger( p_ml, "media-deleted", atoi( pp_results[i*i_cols] ) );
}
}
else
Rollback( p_ml );
quit_delete_final:
FreeSQLResult( p_ml, pp_results );
free( psz_tmp );
free( psz_idlist );
return i_return;
}
/*****************************************************************************
* sql_search.c: SQL-based media library: all find/get functions
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
int Find( media_library_t *p_ml, vlc_array_t *p_result_array, ... )
{
va_list args;
int returned;
va_start( args, p_result_array );
returned = FindVa( p_ml, p_result_array, args );
va_end( args );
return returned;
}
/**
* @brief Generic find in Media Library, returns arrays of psz or int
*
* @param p_ml the media library object
* @param result A pointer to a result array
* @param criterias list of criterias used in SELECT
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int FindVa( media_library_t *p_ml,
vlc_array_t *p_result_array, va_list criterias )
{
int i_ret = VLC_SUCCESS;
char *psz_query;
ml_result_type_e result_type;
char **pp_results = NULL;
int i_cols, i_rows;
if( !p_result_array )
return VLC_EGENERIC;
i_ret = BuildSelectVa( p_ml, &psz_query, &result_type, criterias );
if( i_ret != VLC_SUCCESS )
return i_ret;
if( Query( p_ml, &pp_results, &i_rows, &i_cols, "%s", psz_query )
!= VLC_SUCCESS )
{
msg_Err( p_ml, "Error occured while making the query to the database" );
return VLC_EGENERIC;
}
i_ret = SQLToResultArray( p_ml, p_result_array, pp_results, i_rows, i_cols,
result_type );
free( psz_query);
FreeSQLResult( p_ml, pp_results );
return i_ret;
}
/**
* @brief Generic find in Media Library, returns arrays of psz or int
*
* @param p_ml the media library object
* @param result a pointer to a result array
* @param selected_type the type of the element we're selecting
* @param criterias list of criterias used in SELECT
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int FindAdv( media_library_t *p_ml, vlc_array_t *p_result_array,
ml_select_e selected_type, const char* psz_lvalue, ml_ftree_t *tree )
{
int i_ret = VLC_SUCCESS;
char *psz_query;
ml_result_type_e result_type;
char **pp_results = NULL;
int i_cols, i_rows;
if( !p_result_array )
return VLC_EGENERIC;
i_ret = BuildSelect( p_ml, &psz_query, &result_type, psz_lvalue,
selected_type, tree );
if( i_ret != VLC_SUCCESS )
return i_ret;
if( Query( p_ml, &pp_results, &i_rows, &i_cols, "%s", psz_query )
!= VLC_SUCCESS )
{
msg_Err( p_ml, "Error occured while making the query to the database" );
return VLC_EGENERIC;
}
i_ret = SQLToResultArray( p_ml, p_result_array, pp_results, i_rows, i_cols,
result_type );
free( psz_query);
FreeSQLResult( p_ml, pp_results );
return i_ret;
}
/**
* @brief Generic SELECT query builder with va_list parameter
*
* @param p_ml This media_library_t object
* @param ppsz_query *ppsz_query will contain query
* @param p_result_type see enum ml_result_type_e
* @param criterias list of criterias used in SELECT
* @return VLC_SUCCESS or a VLC error code
* NOTE va_list criterias must end with ML_END or this will fail (segfault)
*
* This function handles results of only one column (or two if ID is included),
* of 'normal' types: int and strings
*/
int BuildSelectVa( media_library_t *p_ml, char **ppsz_query,
ml_result_type_e *p_result_type, va_list criterias )
{
int i_continue = 1;
ml_ftree_t* p_ftree = NULL;
char* psz_lvalue = NULL;
/* Get the name of the data we want */
ml_select_e selected_type = va_arg( criterias, int );
if( selected_type == ML_PEOPLE || selected_type == ML_PEOPLE_ID ||
selected_type == ML_PEOPLE_ROLE )
psz_lvalue = va_arg( criterias, char * );
/* Loop on every arguments */
while( i_continue )
{
ml_ftree_t *p_find = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
if( !p_find )
return VLC_ENOMEM;
p_find->criteria = va_arg( criterias, int );
p_find->comp = ML_COMP_EQUAL;
switch( p_find->criteria )
{
case ML_SORT_ASC:
p_ftree = ml_FtreeSpecAsc( p_ftree, va_arg( criterias, char* ) ); break;
case ML_SORT_DESC:
p_ftree = ml_FtreeSpecDesc( p_ftree, va_arg( criterias, char* ) ); break;
case ML_DISTINCT:
p_ftree = ml_FtreeSpecDistinct( p_ftree ); break;
case ML_LIMIT:
p_ftree = ml_FtreeSpecLimit( p_ftree, va_arg( criterias, int ) );
break;
case ML_ARTIST:
/* This is OK because of a shallow free find */
p_find->lvalue.str = ML_PERSON_ARTIST;
p_find->value.str = va_arg( criterias, char* );
p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
break;
case ML_PEOPLE:
p_find->lvalue.str = va_arg( criterias, char* );
p_find->value.str = va_arg( criterias, char* );
p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
break;
case ML_PEOPLE_ID:
p_find->lvalue.str = va_arg( criterias, char* );
p_find->value.i = va_arg( criterias, int );
p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
break;
case ML_END:
i_continue = 0;
break;
default:
switch( ml_AttributeIsString( p_find->criteria ) )
{
case 0:
p_find->value.i = va_arg( criterias, int );
break;
case 1:
p_find->value.str = va_arg( criterias, char* );
break;
}
p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
break;
}
}
int i_ret = BuildSelect( p_ml, ppsz_query, p_result_type, psz_lvalue,
selected_type, p_ftree );
ml_ShallowFreeFindTree( p_ftree );
return i_ret;
}
/**
* @brief Append a string and format it using SQL vmprintf
**/
static int AppendStringFmtVa( media_library_t *p_ml,
char **ppsz_dst, const char *psz_fmt,
va_list args )
{
char *psz_tmp = NULL, *psz_fullexp = NULL;
assert( ppsz_dst != NULL );
if( !( *ppsz_dst ) )
{
/* New expression */
*ppsz_dst = sql_VPrintf( p_ml->p_sys->p_sql, psz_fmt, args );
if( !( *ppsz_dst ) )
return VLC_ENOMEM;
}
else
{
/* Create new expression B */
psz_tmp = sql_VPrintf( p_ml->p_sys->p_sql, psz_fmt, args );
if( !psz_tmp )
return VLC_ENOMEM;
if( asprintf( &psz_fullexp, "%s%s", *ppsz_dst, psz_tmp ) == -1 )
{
free( psz_tmp );
return VLC_ENOMEM;
}
free( *ppsz_dst );
*ppsz_dst = psz_fullexp;
}
return VLC_SUCCESS;
}
static int AppendStringFmt( media_library_t *p_ml,
char **ppsz_dst, const char *psz_fmt, ... )
{
va_list args;
va_start( args, psz_fmt );
int i_ret = AppendStringFmtVa( p_ml, ppsz_dst, psz_fmt, args );
va_end( args );
return i_ret;
}
/* Early Declaration of Where String Generator */
static int BuildWhere( media_library_t* p_ml, char **ppsz_where, ml_ftree_t* tree,
char** sort, int* limit, const char** distinct, char*** pppsz_frompersons,
int* i_frompersons, int* join );
# define table_media (1 << 0)
# define table_album (1 << 1)
# define table_people (1 << 2)
# define table_extra (1 << 3)
static void PackFromPersons( char*** pppsz_frompersons, int i_num_frompersons )
{
for( int i = 0; i < i_num_frompersons; i++ )
{
if( *pppsz_frompersons[i] == NULL )
continue;
for( int j = i+1; j < i_num_frompersons; j++ )
{
if( strcmp( *pppsz_frompersons[i], *pppsz_frompersons[j] ) == 0 )
{
*pppsz_frompersons[j] = NULL;
}
}
}
}
/**
* @brief Generic SELECT query builder
*
* @param p_ml This media_library_t object
* @param ppsz_query *ppsz_query will contain query
* @param p_result_type see enum ml_result_type_e
* @param selected_type the type of the element we're selecting
* @param tree the find tree
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int BuildSelect( media_library_t *p_ml,
char **ppsz_query, ml_result_type_e *p_result_type,
const char *psz_selected_type_lvalue, ml_select_e selected_type,
ml_ftree_t *tree )
{
/* Basic verification */
if( !ppsz_query )
return VLC_EGENERIC;
int i_ret = VLC_SUCCESS;
char *psz_query = NULL;
/* Building psz_query :
psz_query = "SELECT psz_distinct psz_select
FROM psz_from [JOIN psz_join ON psz_on]
[JOIN psz_join2 ON psz_on2]
[WHERE psz_where[i] [AND psz_where[j] ...]]
[LIMIT psz_limit] [ORDER BY psz_select psz_sort] ;"
*/
char *psz_select = NULL;
const char *psz_distinct = ""; /* "DISTINCT" or "" */
/* FROM */
char *psz_from = NULL;
int i_from = 0; /* Main select table */
char **ppsz_frompersons = NULL;
int i_num_frompersons = 0;
char *psz_peoplerole = NULL; /* Person to get selected */
/* JOIN ... ON ... */
char *psz_join = NULL;
char *psz_join2 = NULL;
char *psz_on = NULL;
char *psz_on2 = NULL;
int i_join = 0; /* Tables that need to be joined */
/* String buffers */
char *psz_where = NULL;
char *psz_sort = NULL; /* ASC or DESC or NULL */
char *psz_tmp = NULL;
int i_limit = 0;
/* Build the WHERE condition */
BuildWhere( p_ml, &psz_where, tree, &psz_sort, &i_limit,
&psz_distinct, &ppsz_frompersons, &i_num_frompersons, &i_join );
PackFromPersons( &ppsz_frompersons, i_num_frompersons );
/* What is the result type? */
ml_result_type_e res_type = ML_TYPE_PSZ;
/* SELECT, FROM */
/* Note that a DISTINCT select makes id of result non sense */
switch( selected_type )
{
case ML_ALBUM:
psz_select = ( !*psz_distinct ) ?
strdup( "album.id, album.title AS album_title" )
: strdup( "album.title AS album_title" );
i_from = table_album;
break;
case ML_ALBUM_COVER:
psz_select = ( !*psz_distinct ) ?
strdup( "album.id, album.cover" ) : strdup( "album.cover" );
i_from = table_album;
break;
case ML_ALBUM_ID:
psz_select = strdup( "album.id" );
psz_distinct = "DISTINCT";
i_from = table_album;
res_type = ML_TYPE_INT;
break;
case ML_ARTIST:
psz_select = ( !*psz_distinct ) ?
strdup( "people_Artist.id, people_Artist.name" )
: strdup( "people_Artist.name" );
i_from = table_people;
psz_peoplerole = strdup( ML_PERSON_ARTIST );
break;
case ML_ARTIST_ID:
psz_select = strdup( "people_Artist.id" );
psz_distinct = "DISTINCT";
i_from = table_people;
res_type = ML_TYPE_INT;
psz_peoplerole = strdup( ML_PERSON_ARTIST );
break;
case ML_COVER:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.cover" ) : strdup( "media.cover" );
i_from = table_media;
break;
case ML_COMMENT:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, extra.comment" ) : strdup( "extra.comment" );
i_from = table_extra;
break;
case ML_GENRE:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.genre" ) : strdup( "media.genre" );
i_from = table_media;
break;
case ML_COUNT_MEDIA:
psz_select = ( !*psz_distinct ) ?
strdup( "COUNT()" ) : strdup( "COUNT( DISTINCT media.id )" );
i_from = table_media;
res_type = ML_TYPE_INT;
break;
case ML_COUNT_ALBUM:
psz_select = ( !*psz_distinct ) ?
strdup( "COUNT()" ) : strdup( "COUNT( DISTINCT album.id )" );
i_from = table_album;
res_type = ML_TYPE_INT;
break;
case ML_COUNT_PEOPLE:
psz_select = ( !*psz_distinct ) ?
strdup( "COUNT()" ) : strdup( "COUNT( DISTINCT people.id )" );
i_from = table_people;
res_type = ML_TYPE_INT;
break;
case ML_FILESIZE:
psz_select = strdup( "media.filesize" );
i_from = table_media;
res_type = ML_TYPE_INT;
break;
case ML_ID:
psz_select = strdup( "media.id" ); /* ID: must be distinct */
psz_distinct = "DISTINCT";
i_from = table_media;
res_type = ML_TYPE_INT;
break;
case ML_LANGUAGE:
psz_select = strdup( "extra.language" );
psz_distinct = "DISTINCT";
i_from = table_extra;
break;
case ML_MEDIA_SPARSE:
i_ret = AppendStringFmt( p_ml, &psz_select, "media.id AS id,"
"media.uri AS uri,"
"media.type AS type,"
"media.title AS title,"
"media.duration AS duration,"
"media.original_title AS original_title,"
"media.album_id AS album_id,"
"media.cover AS cover,"
"media.preview AS preview,"
"media.disc AS disc,"
"media.track AS track,"
"media.year AS year,"
"media.genre AS genre,"
"media.played_count AS played_count,"
"media.last_played AS last_played,"
"media.first_played AS first_played,"
"media.import_time AS import_time,"
"media.skipped_count AS skipped_count,"
"media.last_skipped AS last_skipped,"
"media.vote AS vote,"
"media.score AS score,"
"media.comment AS comment,"
"media.filesize AS filesize,"
"album.title AS album_title,"
"album.cover AS album_cover,"
"(SELECT name FROM media_to_people JOIN people "
"ON (people_id = id) WHERE media_id = media.id AND role = %Q LIMIT 1) AS people_%s",
ML_PERSON_ARTIST, ML_PERSON_ARTIST );
if( i_ret != VLC_SUCCESS )
goto exit;
i_from = table_media;
i_join |= ( table_album | table_people );
psz_distinct = "DISTINCT";
res_type = ML_TYPE_MEDIA;
break;
case ML_MEDIA:
/* Who said this was over-complicated ?? */
/* Yea right. */
psz_select = strdup( "media.id AS id,"
"media.uri AS uri,"
"media.type AS type,"
"media.title AS title,"
"media.duration AS duration,"
"media.original_title AS original_title,"
"media.album_id AS album_id,"
"media.cover AS cover,"
"media.preview AS preview,"
"media.disc as disc,"
"media.track AS track,"
"media.year AS year,"
"media.genre AS genre,"
"media.played_count AS played_count,"
"media.last_played AS last_played,"
"media.first_played AS first_played,"
"media.import_time AS import_time,"
"media.last_skipped AS last_skipped,"
"media.skipped_count AS skipped_count,"
"media.vote AS vote,"
"media.score AS score,"
"media.comment AS comment,"
"media.filesize AS filesize,"
"album.title AS album_title,"
"album.cover AS album_cover,"
"people.id AS people_id,"
"people.name AS people_name,"
"people.role AS people_role,"
"extra.language AS language,"
"extra.extra AS extra" );
i_from = table_media;
i_join |= ( table_album | table_people | table_extra );
psz_distinct = "DISTINCT";
res_type = ML_TYPE_MEDIA;
break;
case ML_MEDIA_EXTRA:
psz_select = strdup( "media.id AS id,"
"people.id AS people_id,"
"people.name AS people_name,"
"people.role AS people_role,"
"extra.extra AS extra,"
"extra.language AS language" );
i_from = table_media;
i_join |= ( table_album | table_people | table_extra );
psz_distinct = "DISTINCT";
res_type = ML_TYPE_MEDIA;
break;
case ML_ORIGINAL_TITLE:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.original_title" ) : strdup( "media.original_title" );
i_from = table_media;
break;
/* For people, if lvalue = "", then we want ANY people. */
case ML_PEOPLE:
assert( psz_selected_type_lvalue );
i_ret = AppendStringFmt( p_ml, &psz_select, "people%s%s.name",
*psz_selected_type_lvalue ? "_" : "",
*psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
if( i_ret != VLC_SUCCESS )
goto exit;
if( *psz_distinct )
{
i_ret = AppendStringFmt( p_ml, &psz_select, ", people%s%s.name",
*psz_selected_type_lvalue ? "_" : "",
*psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
if( i_ret != VLC_SUCCESS )
goto exit;
}
i_from = table_people;
psz_peoplerole = strdup( psz_selected_type_lvalue );
break;
case ML_PEOPLE_ID:
assert( psz_selected_type_lvalue );
i_ret = AppendStringFmt( p_ml, &psz_select, "people%s%s.id",
*psz_selected_type_lvalue ? "_" : "",
*psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
if( i_ret != VLC_SUCCESS )
goto exit;
if( *psz_distinct )
{
i_ret = AppendStringFmt( p_ml, &psz_select, ", people%s%s.id",
*psz_selected_type_lvalue ? "_" : "",
*psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
if( i_ret != VLC_SUCCESS )
goto exit;
}
psz_distinct = "DISTINCT";
i_from = table_people;
psz_peoplerole = strdup( psz_selected_type_lvalue );
res_type = ML_TYPE_INT;
break;
case ML_PEOPLE_ROLE:
psz_select = strdup( "people.role" );
psz_distinct = "DISTINCT";
i_from = table_people;
break;
case ML_TITLE:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.title" ) : strdup( "media.title" );
i_from = table_media;
break;
case ML_TYPE:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.type" ): strdup( "media.type" );
i_from = table_media;
res_type = ML_TYPE_INT;
break;
case ML_URI:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.uri" ) : strdup( "media.uri" );
i_from = table_media;
break;
case ML_VOTE:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.vote" ) : strdup( "media.vote" );
i_from = table_media;
res_type = ML_TYPE_INT;
break;
case ML_YEAR:
psz_select = ( !*psz_distinct ) ?
strdup( "media.id, media.year" ) : strdup( "media.year" );
i_from = table_media;
res_type = ML_TYPE_INT;
break;
case ML_LIMIT:
case ML_SORT_DESC:
case ML_SORT_ASC:
case ML_END:
default:
msg_Dbg( p_ml, "unknown select (%d) in BuildSelect", selected_type );
return VLC_EGENERIC;
}
/* Let's build full psz_query ! */
i_ret = VLC_SUCCESS;
/* Figure out select and join tables */
switch( i_from )
{
case table_media:
break;
case table_album:
switch( i_join )
{
case 0: break;
case 2: i_join = 0; break;
case 1:
case 3: i_from = table_media; i_join = table_album; break;
case 4:
case 5:
case 6:
case 7: i_from = table_media; i_join = table_album | table_people; break;
case 8:
case 9:
case 10:
case 11: i_from = table_media; i_join = table_extra | table_album; break;
case 12:
case 13:
case 14:
case 15: i_from = table_media; i_join = table_extra | table_album | table_people; break;
default: break;
}
break;
case table_people:
switch( i_join )
{
case 0: break;
case 1: i_from = table_media; i_join = table_people; break;
case 2:
case 3: i_from = table_media; i_join = table_album | table_people; break;
case 4:
/* Determine if a join from media is required */
if( i_num_frompersons > 1 )
i_from = table_media;
else
i_join = 0;
break;
case 5: i_from = table_media; i_join = table_people; break;
case 6:
case 7: i_from = table_media; i_join = table_album | table_people; break;
case 8:
case 9: i_from = table_media; i_join = table_people | table_extra; break;
case 10:
case 11: i_from = table_media; i_join = table_people | table_album | table_extra; break;
case 12:
case 13: i_from = table_media; i_join = table_people | table_extra; break;
case 14:
case 15: i_from = table_media; i_join = table_people | table_album | table_extra; break;
default: break;
}
break;
case table_extra:
switch( i_join )
{
case 0: break;
case 1: i_from = table_media; i_join = table_extra; break;
case 2:
case 3: i_from = table_media; i_join = table_extra | table_album; break;
case 4:
case 5: i_from = table_media; i_join = table_extra | table_people; break;
case 6:
case 7: i_from = table_media; i_join = table_extra | table_people | table_album; break;
case 8: i_from = table_extra; i_join = 0; break;
case 9: i_from = table_media; i_join = table_extra; break;
case 10:
case 11: i_from = table_media; i_join = table_extra | table_album; break;
case 12:
case 13: i_from = table_media; i_join = table_extra | table_people; break;
case 14:
case 15: i_from = table_media; i_join = table_extra | table_people | table_album; break;
default: break;
}
break;
default: msg_Warn( p_ml, "You can't be selecting from this table!!" );
i_ret = VLC_EGENERIC;
goto exit;
}
assert( !( i_from & table_album && i_join & table_album ) );
assert( !( i_from & table_people && i_join & table_people ) );
assert( !( i_from & table_extra && i_join & table_extra ) );
/* Generate FROM - psz_from */
if( i_from == table_media )
i_ret = AppendStringFmt( p_ml, &psz_from, "media" );
else if( i_from == table_album )
i_ret = AppendStringFmt( p_ml, &psz_from, "album" );
else if( i_from == table_extra )
i_ret = AppendStringFmt( p_ml, &psz_from, "extra" );
else if( i_from == table_people )
{
i_ret = AppendStringFmt( p_ml, &psz_from, "people AS people%s%s",
psz_peoplerole ? "_" : "", psz_peoplerole );
if( i_ret < 0 ) goto exit;
/* The ugly next statement is only required if persons are being
* selected. Otherwise the joins will handle this */
if( psz_peoplerole && *psz_peoplerole )
{
i_ret = AppendStringFmt( p_ml, &psz_where, "%s people_%s.role = %Q ",
( psz_where && *psz_where ) ? " AND" : "",
psz_peoplerole, psz_peoplerole );
if( i_ret < 0 ) goto exit;
}
}
if( i_ret < 0 ) goto exit;
i_ret = AppendStringFmt( p_ml, &psz_query,
"SELECT %s %s ", psz_distinct, psz_select );
if( i_ret < 0 ) goto exit;
i_ret = AppendStringFmt( p_ml, &psz_query, "FROM %s ", psz_from );
if( i_ret < 0 ) goto exit;
/* Create join conditions */
if( i_join & table_people )
{
/* we can join psz_peoplerole safely because
* if i_join = people, then i_from != people */
bool join = true;
for( int i = 0; i < i_num_frompersons ; i++ )
{
/* We assume ppsz_frompersons has unique entries and
* if ppsz_frompersons[i] is empty(but not NULL), then it
* means we accept any role */
if( ppsz_frompersons[i] && *ppsz_frompersons[i] )
{
if( strcmp( psz_peoplerole, ppsz_frompersons[i] ) == 0 )
join = false;
AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS people_%sx ",
psz_join == NULL ? "" : ",", ppsz_frompersons[i] );
/* This is possible because from is usually the media table */
AppendStringFmt( p_ml, &psz_on, "%speople_%sx.media_id = media.id ",
psz_on == NULL ? "" : " AND ", ppsz_frompersons[i] );
AppendStringFmt( p_ml, &psz_join2, "%speople AS people_%s ",
psz_join2 == NULL ? "" : ",", ppsz_frompersons[i] );
AppendStringFmt( p_ml, &psz_on2, "%s ( people_%sx.people_id = people_%s.id AND "
"people_%s.role = %Q )", psz_on2 == NULL ? "" : " AND ",
ppsz_frompersons[i], ppsz_frompersons[i],
ppsz_frompersons[i], ppsz_frompersons[i] );
}
else if( ppsz_frompersons[i] )
{
if( strcmp( psz_peoplerole, ppsz_frompersons[i] ) == 0 )
join = false;
AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS peoplex ",
psz_join == NULL ? "" : "," );
/* This is possible because from is usually the media table */
AppendStringFmt( p_ml, &psz_on, "%speoplex.media_id = media.id ",
psz_on == NULL ? "" : " AND " );
AppendStringFmt( p_ml, &psz_join2, "%speople AS people ",
psz_join2 == NULL ? "" : "," );
AppendStringFmt( p_ml, &psz_on2, "%s peoplex.people_id = people.id",
psz_on2 == NULL ? "" : " AND " );
}
}
if( join == true )
{
if( psz_peoplerole && *psz_peoplerole )
{
AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS people_%sx ",
psz_join == NULL ? "" : ",", psz_peoplerole );
/* This is possible because from is always the media table */
AppendStringFmt( p_ml, &psz_on, "%speople_%sx.media_id = media.id ",
psz_on == NULL ? "" : " AND ", psz_peoplerole );
AppendStringFmt( p_ml, &psz_join2, "%speople AS people_%s ",
psz_join2 == NULL ? "" : ",", psz_peoplerole );
AppendStringFmt( p_ml, &psz_on2, "%s ( people_%sx.people_id = people_%s.id AND "
"people_%s.role = %Q )", psz_on2 == NULL ? "" : " AND ",
psz_peoplerole, psz_peoplerole,
psz_peoplerole, psz_peoplerole );
}
else
{
AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS peoplex ",
psz_join == NULL ? "" : "," );
/* This is possible because from is usually the media table */
AppendStringFmt( p_ml, &psz_on, "%speoplex.media_id = media.id ",
psz_on == NULL ? "" : " AND " );
AppendStringFmt( p_ml, &psz_join2, "%speople ",
psz_join2 == NULL ? "" : "," );
AppendStringFmt( p_ml, &psz_on2, "%s peoplex.people_id = people.id",
psz_on2 == NULL ? "" : " AND " );
}
}
}
if( i_join & table_album )
{
AppendStringFmt( p_ml, &psz_join, "%salbum", psz_join == NULL ? "" : "," );
AppendStringFmt( p_ml, &psz_on, "%s album.id = media.album_id ",
psz_on == NULL ? "" : " AND " );
}
if( i_join & table_extra )
{
AppendStringFmt( p_ml, &psz_join, "%sextra", psz_join == NULL ? "" : "," );
AppendStringFmt( p_ml, &psz_on, "%s extra.id = media.id ",
psz_on == NULL ? "" : " AND " );
}
/* Complete the join clauses */
if( psz_join )
{
AppendStringFmt( p_ml, &psz_query,
"JOIN %s ON %s ", psz_join, psz_on );
}
if( psz_join2 )
{
AppendStringFmt( p_ml, &psz_query,
"JOIN %s ON %s ", psz_join2, psz_on2 );
}
if( psz_where && *psz_where )
{
AppendStringFmt( p_ml, &psz_query,
"WHERE %s ", psz_where );
}
/* TODO: FIXME: Limit on media objects doesn't work! */
if( i_limit )
{
AppendStringFmt( p_ml, &psz_query,
"LIMIT %d ", i_limit );
}
if( psz_sort )
{
AppendStringFmt( p_ml, &psz_query,
"ORDER BY %s %s", psz_select, psz_sort );
}
if( i_ret > 0 ) i_ret = VLC_SUCCESS;
if( p_result_type ) *p_result_type = res_type;
if( !psz_query ) i_ret = VLC_EGENERIC;
else *ppsz_query = strdup( psz_query );
exit:
free( psz_query );
free( psz_where );
free( psz_tmp );
free( psz_from );
free( psz_join );
free( psz_select );
free( psz_join2 );
free( psz_on );
free( psz_on2 );
free( psz_peoplerole );
free( ppsz_frompersons );
if( i_ret != VLC_SUCCESS )
msg_Warn( p_ml, "an unknown error occured (%d)", i_ret );
return i_ret;
}
#undef CASE_INT
#define CASE_INT( casestr, fmt, table ) \
case casestr: \
assert( tree->comp != ML_COMP_HAS && tree->comp != ML_COMP_STARTS_WITH \
&& tree->comp != ML_COMP_ENDS_WITH ); \
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "%s %s %d", fmt, \
tree->comp == ML_COMP_LESSER ? "<" : \
tree->comp == ML_COMP_LESSER_OR_EQUAL ? "<=" : \
tree->comp == ML_COMP_GREATER ? ">" : \
tree->comp == ML_COMP_GREATER_OR_EQUAL ? ">=" : "=", tree->value.i ); \
if( *ppsz_where == NULL ) \
goto parsefail; \
*join |= table; \
break
#undef CASE_PSZ
#define CASE_PSZ( casestr, fmt, table ) \
case casestr: \
assert( tree->comp == ML_COMP_HAS || tree->comp == ML_COMP_EQUAL \
|| tree->comp == ML_COMP_STARTS_WITH \
|| tree->comp == ML_COMP_ENDS_WITH ); \
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "%s LIKE '%s%q%s'", fmt, \
tree->comp == ML_COMP_HAS \
|| tree->comp == ML_COMP_STARTS_WITH? "%%" : "", \
tree->value.str, \
tree->comp == ML_COMP_HAS \
|| tree->comp == ML_COMP_ENDS_WITH? "%%" : "" ); \
if( *ppsz_where == NULL ) \
goto parsefail; \
*join |= table; \
break
#define SLDPJ sort, limit, distinct, pppsz_frompersons, i_frompersons, join
static int BuildWhere( media_library_t* p_ml, char **ppsz_where, ml_ftree_t* tree,
char** sort, int* limit, const char** distinct,
char*** pppsz_frompersons, int* i_frompersons, int* join )
{
assert( ppsz_where && sort && distinct );
if( !tree ) /* Base case */
{
return VLC_SUCCESS;
}
int i_ret = VLC_EGENERIC;
char* psz_left = NULL;
char* psz_right = NULL;
switch( tree->op )
{
case ML_OP_AND:
case ML_OP_OR:
i_ret = BuildWhere( p_ml, &psz_left, tree->left, SLDPJ );
if( i_ret != VLC_SUCCESS )
goto parsefail;
i_ret = BuildWhere( p_ml, &psz_right, tree->right, SLDPJ );
if( i_ret != VLC_SUCCESS )
goto parsefail;
if( psz_left == NULL || psz_right == NULL )
{
msg_Err( p_ml, "Parsing failed for AND/OR" );
i_ret = VLC_EGENERIC;
goto parsefail;
}
if( asprintf( ppsz_where, "( %s %s %s )", psz_left,
( tree->op == ML_OP_AND ? "AND" : "OR" ), psz_right ) == -1 )
{
i_ret = VLC_ENOMEM;
goto parsefail;
}
break;
case ML_OP_NOT:
i_ret = BuildWhere( p_ml, &psz_left, tree->left, SLDPJ );
if( i_ret != VLC_SUCCESS )
goto parsefail;
if( psz_left == NULL )
{
msg_Err( p_ml, "Parsing failed at NOT" );
i_ret = VLC_EGENERIC;
goto parsefail;
}
if( asprintf( ppsz_where, "( NOT %s )", psz_left ) == -1 )
{
i_ret = VLC_ENOMEM;
goto parsefail;
}
break;
case ML_OP_SPECIAL:
i_ret = BuildWhere( p_ml, &psz_right, tree->right, SLDPJ );
if( i_ret != VLC_SUCCESS )
goto parsefail;
i_ret = BuildWhere( p_ml, &psz_left, tree->left, SLDPJ );
if( i_ret != VLC_SUCCESS )
goto parsefail;
/* Ignore right parse tree as this is a special node */
if( asprintf( ppsz_where, "%s", psz_left ? psz_left : "" ) == -1 )
{
i_ret = VLC_ENOMEM;
goto parsefail;
}
break;
case ML_OP_NONE:
switch( tree->criteria )
{
case ML_PEOPLE:
assert( tree->comp == ML_COMP_HAS
|| tree->comp == ML_COMP_EQUAL
|| tree->comp == ML_COMP_STARTS_WITH
|| tree->comp == ML_COMP_ENDS_WITH );
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
"people%s%s.name LIKE '%s%q%s'",
tree->lvalue.str ? "_" : "",
tree->lvalue.str ? tree->lvalue.str : "",
tree->comp == ML_COMP_HAS
|| tree->comp == ML_COMP_STARTS_WITH ? "%%" : "",
tree->value.str,
tree->comp == ML_COMP_HAS
|| tree->comp == ML_COMP_ENDS_WITH ? "%%" : "" );
if( *ppsz_where == NULL )
goto parsefail;
*pppsz_frompersons = realloc( *pppsz_frompersons,
++*i_frompersons * sizeof( char* ) );
*pppsz_frompersons[ *i_frompersons - 1 ] = tree->lvalue.str;
*join |= table_people;
break;
case ML_PEOPLE_ID:
assert( tree->comp == ML_COMP_EQUAL );
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
"( people%s%s.id = %d )", tree->lvalue.str ? "_":"",
tree->lvalue.str ? tree->lvalue.str:"",
tree->value.i );
if( *ppsz_where == NULL )
goto parsefail;
*pppsz_frompersons = realloc( *pppsz_frompersons,
++*i_frompersons * sizeof( char* ) );
*pppsz_frompersons[ *i_frompersons - 1 ] = tree->lvalue.str;
*join |= table_people;
break;
case ML_PEOPLE_ROLE:
assert( tree->comp == ML_COMP_HAS
|| tree->comp == ML_COMP_EQUAL
|| tree->comp == ML_COMP_STARTS_WITH
|| tree->comp == ML_COMP_ENDS_WITH );
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
"people%s%s.role LIKE '%s%q%s'",
tree->lvalue.str ? "_" : "",
tree->lvalue.str ? tree->lvalue.str : "",
tree->comp == ML_COMP_HAS
|| tree->comp == ML_COMP_STARTS_WITH ? "%%" : "",
tree->value.str,
tree->comp == ML_COMP_HAS
|| tree->comp == ML_COMP_ENDS_WITH ? "%%" : "" );
if( *ppsz_where == NULL )
goto parsefail;
*pppsz_frompersons = realloc( *pppsz_frompersons,
++*i_frompersons * sizeof( char* ) );
*pppsz_frompersons[ *i_frompersons - 1 ] = tree->lvalue.str;
*join |= table_people;
break;
CASE_PSZ( ML_ALBUM, "album.title", table_album );
CASE_PSZ( ML_ALBUM_COVER, "album.cover", table_album );
case ML_ALBUM_ID:
assert( tree->comp == ML_COMP_EQUAL );
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
"album.id = %d", tree->value.i );
if( *ppsz_where == NULL )
goto parsefail;
*join |= table_album;
break;
CASE_PSZ( ML_COMMENT, "media.comment", table_media );
CASE_PSZ( ML_COVER, "media.cover", table_media );
CASE_INT( ML_DURATION, "media.duration", table_media );
CASE_PSZ( ML_EXTRA, "extra.extra", table_extra );
CASE_INT( ML_FILESIZE, "media.filesize", table_media );
CASE_PSZ( ML_GENRE, "media.genre", table_media );
case ML_ID:
assert( tree->comp == ML_COMP_EQUAL );
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
"media.id = %d", tree->value.i );
if( *ppsz_where == NULL )
goto parsefail;
*join |= table_media;
break;
CASE_PSZ( ML_LANGUAGE, "extra.language", table_extra );
CASE_INT( ML_LAST_PLAYED, "media.last_played", table_media );
CASE_PSZ( ML_ORIGINAL_TITLE, "media.original_title", table_media );
msg_Warn( p_ml, "Deprecated Played Count tags" );
CASE_INT( ML_PLAYED_COUNT, "media.played_count", table_media );
CASE_INT( ML_SCORE, "media.score", table_media );
CASE_PSZ( ML_TITLE, "media.title", table_media );
CASE_INT( ML_TRACK_NUMBER, "media.track", table_media);
CASE_INT( ML_TYPE, "media.type", table_media );
CASE_PSZ( ML_URI, "media.uri", table_media );
CASE_INT( ML_VOTE, "media.vote", table_media );
CASE_INT( ML_YEAR, "media.year", table_media );
case ML_LIMIT:
if( !*limit )
*limit = tree->value.i;
else
msg_Warn( p_ml, "Double LIMIT found" );
break;
case ML_SORT_DESC:
*sort = sql_Printf( p_ml->p_sys->p_sql, "%s%s%s DESC ",
sort ? *sort : "", sort ? ", " : "",
tree->value.str );
if( *sort == NULL )
goto parsefail;
break;
case ML_SORT_ASC:
*sort = sql_Printf( p_ml->p_sys->p_sql, "%s%s%s ASC ",
sort ? *sort : "", sort ? ", " : "",
tree->value.str );
if( *sort == NULL )
goto parsefail;
break;
case ML_DISTINCT:
if( !**distinct )
*distinct = "DISTINCT";
else
msg_Warn( p_ml, "Double DISTINCT found!" );
break;
default:
msg_Err( p_ml, "Invalid select type or unsupported: %d", tree->criteria );
}
break;
default:
msg_Err( p_ml, "Broken find tree!" );
i_ret = VLC_EGENERIC;
goto parsefail;
}
i_ret = VLC_SUCCESS;
parsefail:
free( psz_left );
free( psz_right );
return i_ret;
}
# undef CASE_INT
# undef CASE_PSZ
# undef table_media
# undef table_album
# undef table_people
# undef table_extra
/*****************************************************************************
* sql_update.c: SQL-based media library: all database update functions
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: Antoine Lejeune <phytos@videolan.org>
* Jean-Philippe André <jpeg@videolan.org>
* Rémi Duraffort <ivoire@videolan.org>
* Adrien Maglo <magsoft@videolan.org>
* Srikanth Raju <srikiraju at gmail dot 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sql_media_library.h"
/**
* @brief Generic update in Media Library database
*
* @param p_ml the media library object
* @param selected_type the type of the element we're selecting
* @param where the list of ids or uri to change
* @param changes list of changes to make in the entries
* @return VLC_SUCCESS or VLC_EGENERIC
* @note This function is transactional
*/
int Update( media_library_t *p_ml, ml_select_e selected_type,
const char* psz_lvalue, ml_ftree_t *where, vlc_array_t *changes )
{
int i_ret = VLC_EGENERIC;
char *psz_query = NULL;
char *psz_id_query = NULL;
char **pp_results = NULL;
int i_rows = 0, i_cols = 0;
i_ret = BuildUpdate( p_ml, &psz_query, &psz_id_query,
psz_lvalue, selected_type, where, changes );
if( i_ret != VLC_SUCCESS )
{
msg_Err(p_ml,"Failed to generate update query" );
return i_ret;
}
i_ret = VLC_EGENERIC;
Begin( p_ml );
if( QuerySimple( p_ml, "%s", psz_query ) != VLC_SUCCESS )
{
msg_Err( p_ml, "Couldn't run the generated update query successfully" );
goto quitdelete;
}
/* Get the updated IDs to send events! */
if( Query( p_ml, &pp_results, &i_rows, &i_cols, psz_id_query )
!= VLC_SUCCESS )
goto quitdelete;
i_ret = VLC_SUCCESS;
quitdelete:
if( i_ret != VLC_SUCCESS )
Rollback( p_ml );
else
{
Commit( p_ml );
if( i_rows > 0 )
{
for( int i = 0; i < i_rows; i++ )
{
var_SetInteger( p_ml, "media-meta-change",
atoi( pp_results[i*i_cols] ) );
}
}
}
FreeSQLResult( p_ml, pp_results );
free( psz_id_query );
free( psz_query );
return i_ret;
}
#define SET_STR( a ) \
if( !psz_set[i_type] ) \
{ \
psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql, a, find->value.str ); \
if( !psz_set[i_type] ) \
goto quit_buildupdate; \
} \
break;
#define SET_INT( a ) \
if( !psz_set[i_type] ) \
{ \
psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql, a, find->value.i ); \
if( !psz_set[i_type] ) \
goto quit_buildupdate; \
} \
break;
/* TODO: Build smarter updates by using IN () */
static int BuildWhere( media_library_t* p_ml, char **ppsz_where, ml_ftree_t *tree )
{
assert( ppsz_where );
char* psz_left = NULL;
char* psz_right = NULL;
int i_ret = VLC_SUCCESS;
switch( tree->op )
{
case ML_OP_AND:
case ML_OP_OR:
i_ret = BuildWhere( p_ml, &psz_left, tree->left );
if( i_ret != VLC_SUCCESS )
goto quit_buildwhere;
i_ret = BuildWhere( p_ml, &psz_right, tree->right );
if( i_ret != VLC_SUCCESS )
goto quit_buildwhere;
if( psz_left == NULL || psz_right == NULL )
{
msg_Err( p_ml, "Couldn't build AND/OR for Update statement" );
i_ret = VLC_EGENERIC;
goto quit_buildwhere;
}
if( asprintf( ppsz_where, "( %s %s %s )", psz_left,
( tree->op == ML_OP_AND ? "AND" : "OR" ), psz_right ) == -1 )
{
i_ret = VLC_ENOMEM;
goto quit_buildwhere;
}
break;
case ML_OP_NOT:
i_ret = BuildWhere( p_ml, &psz_left, tree->left );
if( i_ret != VLC_SUCCESS )
goto quit_buildwhere;
if( psz_left == NULL )
{
msg_Err( p_ml, "Couldn't build NOT for Update statement" );
i_ret = VLC_EGENERIC;
goto quit_buildwhere;
}
if( asprintf( ppsz_where, "( NOT %s )", psz_left ) == -1 )
{
i_ret = VLC_ENOMEM;
goto quit_buildwhere;
}
break;
case ML_OP_SPECIAL:
msg_Err( p_ml, "Couldn't build special for Update statement" );
break;
case ML_OP_NONE:
switch( tree->criteria )
{
case ML_ID:
assert( tree->comp == ML_COMP_EQUAL );
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "media.id = %d",
tree->value.i );
if( *ppsz_where == NULL )
goto quit_buildwhere;
break;
case ML_URI:
assert( tree->comp == ML_COMP_EQUAL );
*ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "media.uri = %q",
tree->value.str );
if( *ppsz_where == NULL )
goto quit_buildwhere;
break;
default:
msg_Err( p_ml, "Trying to update with unsupported condition" );
break;
}
}
quit_buildwhere:
return i_ret;
}
/**
* @brief Generic UPDATE query builder
*
* @param p_ml This media_library_t object
* @param ppsz_query *ppsz_query will contain query for update
* @param ppsz_id_query will contain query to get the ids of updated files
* @param selected_type the type of the element we're selecting
* @param where parse tree of where condition
* @param changes list of changes to make in the entries
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int BuildUpdate( media_library_t *p_ml,
char **ppsz_query, char **ppsz_id_query,
const char *psz_lvalue,
ml_select_e selected_type,
ml_ftree_t *where, vlc_array_t *changes )
{
assert( ppsz_query );
assert( ppsz_id_query );
*ppsz_query = NULL;
int i_type;
int i_index;
int i_ret = VLC_ENOMEM;
char *psz_table = NULL;
/* TODO: Hack? */
char *psz_set[ ML_DIRECTORY + 1 ] = { NULL };
char *psz_fullset = NULL;
char *psz_extra = NULL; /*<< For an update to extra table */
char *psz_where = NULL;
char *psz_tmp = NULL;
int *pi_padd_ids = NULL;
int i_people_add = 0;
int i_album_id = 0;
char *psz_album = NULL;
char *psz_cover = NULL;
if( !where )
{
msg_Warn( p_ml, "You're trying to update no rows."
"Trying to guess update based on uri" );
}
/* Create the id/uri lists for WHERE part of the query */
i_ret = BuildWhere( p_ml, &psz_where, where );
if( i_ret != VLC_SUCCESS )
goto quit_buildupdate;
/** Firstly, choose the right table */
switch( selected_type )
{
case ML_ALBUM:
psz_table = strdup( "album" );
break;
case ML_PEOPLE:
psz_table = strdup( "people" );
break;
case ML_MEDIA:
psz_table = strdup( "media" );
break;
default:
msg_Err( p_ml, "Not a valid element to Update!" );
i_ret = VLC_EGENERIC;
goto quit_buildupdate;
break;
}
if( !psz_table )
return VLC_ENOMEM;
/** Secondly, build the SET part of the query */
for( i_index = 0; i_index < vlc_array_count( changes ); i_index++ )
{
ml_element_t *find = ( ml_element_t * )
vlc_array_item_at_index( changes, i_index );
i_type = find->criteria;
switch( i_type )
{
case ML_ALBUM:
if( selected_type == ML_ALBUM )
{
if( !psz_set[i_type] )
{
psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
"title = %Q",
find->value.str );
if( !psz_set[i_type] )
goto quit_buildupdate;
}
}
else if( selected_type == ML_MEDIA )
{
if( !psz_album )
psz_album = find->value.str;
}
else
assert( 0 );
break;
case ML_ALBUM_ID:
assert( selected_type != ML_ALBUM );
if( selected_type == ML_MEDIA )
{
if( i_album_id <= 0 )
{
i_album_id = find->value.i;
if( !psz_set[i_type] )
{
psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
"album_id = '%d'",
find->value.i );
if( !psz_set[i_type] )
goto quit_buildupdate;
}
}
}
break;
case ML_PEOPLE:
if( selected_type == ML_MEDIA )
{
pi_padd_ids = (int*) realloc( pi_padd_ids , ( ++i_people_add * sizeof(int) ) );
pi_padd_ids[ i_people_add - 1 ] = ml_GetInt( p_ml, ML_PEOPLE_ID,
find->lvalue.str, ML_PEOPLE, find->lvalue.str,
find->value.str );
if( pi_padd_ids[ i_people_add - 1 ] <= 0 )
{
AddPeople( p_ml, find->value.str, find->lvalue.str );
pi_padd_ids[ i_people_add - 1 ] = ml_GetInt( p_ml, ML_PEOPLE_ID,
find->lvalue.str, ML_PEOPLE, find->lvalue.str,
find->value.str );
}
}
else if( strcmp( psz_lvalue, find->lvalue.str ) )
{
msg_Err( p_ml, "Trying to update a different person type" );
return VLC_EGENERIC;
}
else
{
if( !psz_set[i_type] ) psz_set[i_type] =
sql_Printf( p_ml->p_sys->p_sql, "name = %Q", find->value.str );
}
break;
case ML_PEOPLE_ID:
/* TODO: Implement smarter updates for this case? */
assert( selected_type == ML_MEDIA );
if( selected_type == ML_MEDIA )
{
bool b_update = true;
for( int i = 0; i < i_people_add; i++ )
{
if( pi_padd_ids[ i ] == find->value.i )
{
b_update = false;
break;
}
}
if( b_update )
{
pi_padd_ids = (int *)realloc( pi_padd_ids, ( ++i_people_add * sizeof(int) ) );
pi_padd_ids[ i_people_add - 1 ] = find->value.i;
}
}
break;
case ML_PEOPLE_ROLE:
msg_Dbg( p_ml, "Can't update role" );
break;
case ML_COMMENT:
assert( selected_type == ML_MEDIA );
SET_STR( "comment = %Q" );
case ML_COVER:
assert( selected_type == ML_ALBUM || selected_type == ML_MEDIA );
psz_cover = find->value.str;
SET_STR( "cover = %Q" );
case ML_DISC_NUMBER:
assert( selected_type == ML_MEDIA );
SET_INT( "disc = '%d'" );
case ML_DURATION:
assert( selected_type == ML_MEDIA );
SET_INT( "duration = '%d'" );
case ML_EXTRA:
assert( selected_type == ML_MEDIA );
SET_STR( "extra = %Q" );
case ML_FIRST_PLAYED:
assert( selected_type == ML_MEDIA );
SET_INT( "first_played =='%d'" );
case ML_GENRE:
assert( selected_type == ML_MEDIA );
SET_STR( "genre = %Q" );
/* ID cannot be updated */
/* Import time can't be updated */
case ML_LAST_PLAYED:
assert( selected_type == ML_MEDIA );
SET_INT( "last_played = '%d'" );
case ML_ORIGINAL_TITLE:
assert( selected_type == ML_MEDIA );
SET_STR( "original_title = %Q" );
case ML_PLAYED_COUNT:
assert( selected_type == ML_MEDIA );
SET_INT( "played_count = '%d'" );
case ML_PREVIEW:
assert( selected_type == ML_MEDIA );
SET_STR( "preview = %Q" );
case ML_SKIPPED_COUNT:
assert( selected_type == ML_MEDIA );
SET_INT( "skipped_count = '%d'" );
case ML_SCORE:
assert( selected_type == ML_MEDIA );
SET_INT( "score = '%d'" );
case ML_TITLE:
assert( selected_type == ML_MEDIA );
SET_STR( "title = %Q" );
case ML_TRACK_NUMBER:
assert( selected_type == ML_MEDIA );
SET_INT( "track = '%d'" );
case ML_TYPE:
assert( selected_type == ML_MEDIA );
if( !psz_set[i_type] ) psz_set[i_type] =
sql_Printf( p_ml->p_sys->p_sql, "type = '%d'", find->value.i );
break;
case ML_URI:
assert( selected_type == ML_MEDIA );
if( !psz_set[i_type] )
{
psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
"uri = %Q",
find->value.str );
}
break;
case ML_VOTE:
assert( selected_type == ML_MEDIA );
SET_INT( "vote = '%d'" );
case ML_YEAR:
assert( selected_type == ML_MEDIA );
SET_INT( "year = '%d'" );
case ML_END:
goto exitfor;
default:
msg_Warn( p_ml, "Invalid type for update : %d", i_type );
}
}
exitfor:
/* TODO: Album artist. Verify albumart */
if( i_album_id <= 0 && psz_album && *psz_album )
{
i_ret = AddAlbum( p_ml, psz_album, psz_cover, 0 );
if( i_ret != VLC_SUCCESS )
goto quit_buildupdate;
i_album_id = ml_GetAlbumId( p_ml, psz_album );
if( i_album_id <= 0 )
goto quit_buildupdate;
psz_set[ML_ALBUM_ID] = sql_Printf( p_ml->p_sys->p_sql,
"album_id = '%d'", i_album_id );
if( !psz_set[i_type] )
goto quit_buildupdate;
}
for( unsigned i = 0; i <= ML_DIRECTORY; i++ )
{
if( psz_set[i] )
{
if( i == ML_EXTRA || i == ML_LANGUAGE )
{
free( psz_tmp );
if( asprintf( &psz_tmp, "%s%s%s", psz_extra ? psz_extra : "",
psz_extra ? ", ": "", psz_set[i] ) == -1 )
goto quit_buildupdate;
free( psz_extra );
psz_extra = strdup( psz_tmp );
}
else
{
free( psz_tmp );
if( asprintf( &psz_tmp, "%s%s%s", psz_fullset ? psz_fullset : "",
psz_fullset ? ", ": "", psz_set[i] ) == -1 )
goto quit_buildupdate;
free( psz_fullset );
psz_fullset = strdup( psz_tmp );
}
}
}
i_ret = VLC_SUCCESS;
/** Now build the right WHERE condition */
assert( psz_where && *psz_where );
/** Finally build the full query */
/** Pass if we have some people to add - Indirect update*/
if( !psz_fullset && i_people_add == 0 )
{
i_ret = VLC_EGENERIC;
goto quit_buildupdate;
}
if( psz_fullset ){
if( asprintf( ppsz_query, "UPDATE %s SET %s WHERE %s", psz_table,
psz_fullset, psz_where ) == -1 )
{
goto quit_buildupdate;
}
}
if( selected_type == ML_MEDIA )
{
if( psz_extra )
{
if( asprintf( &psz_tmp, "%s; UPDATE extra SET %s WHERE %s",
*ppsz_query, psz_extra, psz_where ) == -1 )
goto quit_buildupdate;
free( *ppsz_query );
*ppsz_query = psz_tmp;
psz_tmp = NULL;
}
char* psz_idstring = NULL;
if( i_people_add > 0 )
{
for( int i = 0; i < i_people_add; i++ )
{
if( asprintf( &psz_tmp, "%s%s%d", psz_idstring == NULL? "" : psz_idstring,
psz_idstring == NULL ? "" : ",", pi_padd_ids[i] ) == -1 )
{
free( psz_tmp );
free( psz_idstring );
goto quit_buildupdate;
}
free( psz_idstring );
psz_idstring = psz_tmp;
psz_tmp = NULL;
}
/* Delete all connections with people whom we will update now! */
if( asprintf( &psz_tmp, "%s;DELETE FROM media_to_people WHERE EXISTS "
"(SELECT media.id, people.id FROM media JOIN media_to_people "
"AS temp ON media.id = temp.media_id "
"JOIN people ON temp.people_id = people.id "
"WHERE %s AND people.role IN "
"(SELECT people.role FROM people WHERE people.id IN (%s)) "
"AND people.id NOT IN (%s) "
"AND temp.media_id = media_to_people.media_id AND "
"temp.people_id = media_to_people.people_id )",
*ppsz_query == NULL ? "": *ppsz_query, psz_where,
psz_idstring, psz_idstring ) == -1 )
{
free( psz_idstring );
goto quit_buildupdate;
}
free( *ppsz_query );
*ppsz_query = psz_tmp;
psz_tmp = NULL;
free( psz_idstring );
}
for( int i = 0; i < i_people_add ; i++ )
{
if( pi_padd_ids[i] > 0 )
{
/* OR IGNORE will avoid errors from collisions from old media
* Perhaps this hack can be fixed...FIXME */
if( asprintf( &psz_tmp, "%s;INSERT OR IGNORE into media_to_people "
"(media_id,people_id) SELECT media.id, %d FROM media WHERE %s",
*ppsz_query == NULL ? "" : *ppsz_query, pi_padd_ids[i],
psz_where ) == -1 )
goto quit_buildupdate;
FREENULL( *ppsz_query );
*ppsz_query = psz_tmp;
psz_tmp = NULL;
}
}
}
if( asprintf( ppsz_id_query, "SELECT id AS %s_id FROM %s WHERE %s",
psz_table, psz_table, psz_where ) == -1 )
{
goto quit_buildupdate;
}
#ifndef NDEBUG
msg_Dbg( p_ml, "updated media where %s", psz_where );
#endif
quit_buildupdate:
free( psz_tmp );
free( psz_table );
free( psz_fullset );
free( psz_extra );
free( pi_padd_ids );
for( int i = 0; i <= ML_DIRECTORY; i++ )
free( psz_set[ i ] );
return i_ret;
}
#undef SET_STR
#undef SET_INT
/**
* @brief Update a ml_media_t
*
* @param p_ml the media library object
* @param p_media media to synchronise in the database
* @return VLC_SUCCESS or VLC_EGENERIC
* @note: the media id may be 0, in this case, the update is based
* on the url (less powerful). This function is threadsafe
*
* This synchronises all non NULL and non zero fields of p_media
* Synchronization of album and people is TODO
*/
int UpdateMedia( media_library_t *p_ml, ml_media_t *p_media )
{
assert( p_media->i_id || ( p_media->psz_uri && *p_media->psz_uri ) );
vlc_array_t *changes = vlc_array_new();
ml_element_t *find = NULL;
int i_ret = VLC_EGENERIC;
ml_LockMedia( p_media );
#define APPEND_ICHANGES( cond, crit ) \
if( cond ) { \
find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) ); \
find->criteria = crit; \
find->value.i = cond; \
vlc_array_append( changes, find ); \
}
#define APPEND_SCHANGES( cond, crit ) \
if( cond ) { \
find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) ); \
find->criteria = crit; \
find->value.str = cond; \
vlc_array_append( changes, find ); \
}
APPEND_SCHANGES( p_media->psz_title, ML_TITLE );
APPEND_ICHANGES( p_media->i_type, ML_TYPE );
APPEND_ICHANGES( p_media->i_duration, ML_DURATION );
APPEND_SCHANGES( p_media->psz_preview, ML_PREVIEW );
APPEND_SCHANGES( p_media->psz_cover, ML_COVER );
APPEND_ICHANGES( p_media->i_disc_number, ML_DISC_NUMBER );
APPEND_ICHANGES( p_media->i_track_number, ML_TRACK_NUMBER );
APPEND_ICHANGES( p_media->i_year, ML_YEAR);
APPEND_SCHANGES( p_media->psz_genre, ML_GENRE );
APPEND_ICHANGES( p_media->i_album_id, ML_ALBUM_ID );
APPEND_SCHANGES( p_media->psz_album, ML_ALBUM );
APPEND_ICHANGES( p_media->i_skipped_count, ML_SKIPPED_COUNT );
APPEND_ICHANGES( p_media->i_last_skipped, ML_LAST_SKIPPED );
APPEND_ICHANGES( p_media->i_played_count, ML_PLAYED_COUNT );
APPEND_ICHANGES( p_media->i_last_played, ML_LAST_PLAYED );
APPEND_ICHANGES( p_media->i_first_played, ML_FIRST_PLAYED );
APPEND_ICHANGES( p_media->i_vote, ML_VOTE );
APPEND_ICHANGES( p_media->i_score, ML_SCORE );
APPEND_SCHANGES( p_media->psz_comment, ML_COMMENT );
APPEND_SCHANGES( p_media->psz_extra, ML_EXTRA );
APPEND_SCHANGES( p_media->psz_language, ML_LANGUAGE );
if( p_media->psz_uri && p_media->i_id )
{
find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
find->criteria = ML_URI;
find->value.str = p_media->psz_uri;
vlc_array_append( changes, find );
}
/*TODO: implement extended meta */
/* We're not taking import time! Good */
#undef APPEND_ICHANGES
#undef APPEND_SCHANGES
ml_person_t* person = p_media->p_people;
while( person )
{
if( person->i_id > 0 )
{
find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
find->criteria = ML_PEOPLE_ID;
find->lvalue.str = person->psz_role;
find->value.i = person->i_id;
vlc_array_append( changes, find );
}
else if( person->psz_name && *person->psz_name )
{
find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
find->criteria = ML_PEOPLE;
find->lvalue.str = person->psz_role;
find->value.str = person->psz_name;
vlc_array_append( changes, find );
}
person = person->p_next;
}
ml_ftree_t* p_where = NULL;
ml_ftree_t* p_where_elt = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
if( p_media->i_id )
{
p_where_elt->criteria = ML_ID;
p_where_elt->value.i = p_media->i_id ;
p_where_elt->comp = ML_COMP_EQUAL;
p_where = ml_FtreeFastAnd( p_where, p_where_elt );
}
else if( p_media->psz_uri )
{
p_where_elt->criteria = ML_URI;
p_where_elt->value.str = p_media->psz_uri;
p_where_elt->comp = ML_COMP_EQUAL;
p_where = ml_FtreeFastAnd( p_where, p_where_elt );
}
else
{
goto quit1;
}
i_ret = Update( p_ml, ML_MEDIA, NULL, p_where, changes );
quit1:
ml_FreeFindTree( p_where );
for( int i = 0; i < vlc_array_count( changes ); i++ )
/* Note: DO NOT free the strings because
* they belong to the ml_media_t object */
free( vlc_array_item_at_index( changes, i ) );
vlc_array_destroy( changes );
ml_UnlockMedia( p_media );
return i_ret;
}
/**
* @brief Update an album's cover art
* @param p_ml The Media Library
* @param i_album_id Album's ID
* @param psz_cover New cover art
* @return VLC success/error code
**/
int SetArtCover( media_library_t *p_ml,
int i_album_id, const char *psz_cover )
{
assert( i_album_id != 0 );
assert( psz_cover != NULL );
char *psz_query = sql_Printf( p_ml->p_sys->p_sql,
"UPDATE album SET cover = %Q WHERE id = '%d'",
psz_cover, i_album_id );
if( !psz_query )
return VLC_ENOMEM;
if( QuerySimple( p_ml, "%s", psz_query ) != VLC_SUCCESS )
{
msg_Warn( p_ml, "Could not update the album's cover art "
"(Database error)" );
free( psz_query );
return VLC_EGENERIC;
}
free( psz_query );
return VLC_SUCCESS;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment