Commit 98d2036a authored by jetru's avatar jetru
Browse files

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: