Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc
Commits
98d2036a
Commit
98d2036a
authored
Oct 18, 2010
by
Srikanth Raju
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Media library CRUD operations
parent
db662f04
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
2276 additions
and
0 deletions
+2276
-0
modules/media_library/sql_add.c
modules/media_library/sql_add.c
+303
-0
modules/media_library/sql_delete.c
modules/media_library/sql_delete.c
+125
-0
modules/media_library/sql_search.c
modules/media_library/sql_search.c
+1109
-0
modules/media_library/sql_update.c
modules/media_library/sql_update.c
+739
-0
No files found.
modules/media_library/sql_add.c
0 → 100644
View file @
98d2036a
/*****************************************************************************
* 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
);
}
modules/media_library/sql_delete.c
0 → 100644
View file @
98d2036a
/*****************************************************************************
* 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
;
}
modules/media_library/sql_search.c
0 → 100644
View file @
98d2036a
/*****************************************************************************
* 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
modules/media_library/sql_update.c
0 → 100644
View file @
98d2036a
/*****************************************************************************
* 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
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment