Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc-2-2
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-2-2
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