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
79990b94
Commit
79990b94
authored
Mar 24, 2013
by
Rémi Denis-Courmont
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
media library: good riddance
Big pile of non-sensical and unmaintained code.
parent
8e3288f9
Changes
16
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
0 additions
and
5778 deletions
+0
-5778
configure.ac
configure.ac
+0
-2
modules/LIST
modules/LIST
+0
-1
modules/Makefile.am
modules/Makefile.am
+0
-1
modules/media_library/Modules.am
modules/media_library/Modules.am
+0
-11
modules/media_library/item_list.c
modules/media_library/item_list.c
+0
-291
modules/media_library/item_list.h
modules/media_library/item_list.h
+0
-84
modules/media_library/media_pool.c
modules/media_library/media_pool.c
+0
-144
modules/media_library/ml_watch.c
modules/media_library/ml_watch.c
+0
-579
modules/media_library/sql_add.c
modules/media_library/sql_add.c
+0
-303
modules/media_library/sql_delete.c
modules/media_library/sql_delete.c
+0
-129
modules/media_library/sql_media_library.c
modules/media_library/sql_media_library.c
+0
-1428
modules/media_library/sql_media_library.h
modules/media_library/sql_media_library.h
+0
-330
modules/media_library/sql_monitor.c
modules/media_library/sql_monitor.c
+0
-565
modules/media_library/sql_search.c
modules/media_library/sql_search.c
+0
-1115
modules/media_library/sql_update.c
modules/media_library/sql_update.c
+0
-794
po/POTFILES.in
po/POTFILES.in
+0
-1
No files found.
configure.ac
View file @
79990b94
...
...
@@ -3967,7 +3967,6 @@ AC_ARG_ENABLE(media-library,
AS_IF([test "${enable_media_library}" = "yes"], [
AC_DEFINE([MEDIA_LIBRARY], 1, [Define if you want to use the VLC media library])
VLC_ADD_CPPFLAGS([qt4],"-DMEDIA_LIBRARY")
VLC_ADD_PLUGIN([media_library])
dnl
dnl SQLite
...
...
@@ -4135,7 +4134,6 @@ AC_CONFIG_FILES([
modules/lua/Makefile
modules/meta_engine/Makefile
modules/misc/Makefile
modules/media_library/Makefile
modules/mux/Makefile
modules/notify/Makefile
modules/packetizer/Makefile
...
...
modules/LIST
View file @
79990b94
...
...
@@ -192,7 +192,6 @@ $Id$
* magnify: zoom video filter
* marq: Overlays a marquee on the video
* mash: OpenMash based decoder
* media_library: a sql based media library
* mediacodec: Android Jelly Bean MediaCodec decoder module
* mediadirs: Picture/Music/Video user directories as service discoveries
* minimal_macosx: a minimal Mac OS X GUI, using the FrameWork
...
...
modules/Makefile.am
View file @
79990b94
...
...
@@ -10,7 +10,6 @@ BASE_SUBDIRS = \
gui
\
meta_engine
\
misc
\
media_library
\
notify
\
packetizer
\
services_discovery
\
...
...
modules/media_library/Modules.am
deleted
100644 → 0
View file @
8e3288f9
SOURCES_media_library = sql_media_library.c \
sql_media_library.h \
sql_monitor.c \
sql_search.c \
sql_add.c \
sql_update.c \
sql_delete.c \
item_list.c \
item_list.h \
ml_watch.c \
media_pool.c
modules/media_library/item_list.c
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* item_list.c: An input_item_t+media_id couple list for the 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"
#include "item_list.h"
/**
* @short item hash list for the media library monitoring system
*/
/**
* @brief Add an item to the head of the list
* @param p_ml
* @param p_media media object. ID must be non zero and valid
* @param p_item input item to add, MUST NOT be NULL
* @param locked flag set if the list is locked. do not use
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int
__item_list_add
(
watch_thread_t
*
p_wt
,
ml_media_t
*
p_media
,
input_item_t
*
p_item
,
bool
locked
)
{
if
(
!
locked
)
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
ml_LockMedia
(
p_media
);
assert
(
p_media
->
i_id
);
/* Ensure duplication does not occur */
il_foreachlist
(
p_wt
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
->
i_id
==
p_item
->
i_id
)
{
ml_UnlockMedia
(
p_media
);
if
(
!
locked
)
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
VLC_EGENERIC
;
}
}
item_list_t
*
p_new
=
(
item_list_t
*
)
calloc
(
1
,
sizeof
(
item_list_t
)
);
if
(
!
p_new
)
{
ml_UnlockMedia
(
p_media
);
if
(
!
locked
)
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
VLC_ENOMEM
;
}
p_new
->
p_next
=
p_wt
->
p_hlist
[
item_hash
(
p_item
)
];
p_new
->
i_refs
=
1
;
p_new
->
i_media_id
=
p_media
->
i_id
;
p_new
->
p_media
=
p_media
;
p_new
->
p_item
=
p_item
;
p_wt
->
p_hlist
[
item_hash
(
p_item
)
]
=
p_new
;
ml_UnlockMedia
(
p_media
);
if
(
!
locked
)
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
VLC_SUCCESS
;
}
/**
* @brief Delete an item from the list
* @param p_ml this media library
* @param i_media_id media library's media ID
*/
item_list_t
*
item_list_delMedia
(
watch_thread_t
*
p_wt
,
int
i_media_id
)
{
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
item_list_t
*
p_prev
=
NULL
;
il_foreachhashlist
(
p_wt
->
p_hlist
,
p_elt
,
ixx
)
{
if
(
p_elt
->
i_media_id
==
i_media_id
)
{
if
(
p_prev
)
p_prev
->
p_next
=
p_elt
->
p_next
;
else
p_wt
->
p_hlist
[
ixx
]
=
p_elt
->
p_next
;
p_elt
->
p_next
=
NULL
;
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
p_elt
;
}
else
{
p_prev
=
p_elt
;
}
}
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
NULL
;
}
/**
* @brief Delete an item from the list and return the single element
* @param p_ml this media library
* @param p_item item to delete
* @return The element from the list containing p_item
*/
item_list_t
*
item_list_delItem
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
,
bool
locked
)
{
if
(
!
locked
)
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
item_list_t
*
p_prev
=
NULL
;
il_foreachlist
(
p_wt
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
==
p_item
)
{
if
(
p_prev
)
p_prev
->
p_next
=
p_elt
->
p_next
;
else
p_wt
->
p_hlist
[
item_hash
(
p_item
)
]
=
p_elt
->
p_next
;
p_elt
->
p_next
=
NULL
;
if
(
!
locked
)
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
p_elt
;
}
else
{
p_prev
=
p_elt
;
}
}
if
(
!
locked
)
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
NULL
;
}
/**
* @brief Find an input item
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return input item if found, NULL if not found
*/
input_item_t
*
item_list_itemOfMediaId
(
watch_thread_t
*
p_wt
,
int
i_media_id
)
{
item_list_t
*
p_tmp
=
item_list_listitemOfMediaId
(
p_wt
,
i_media_id
);
if
(
p_tmp
)
return
p_tmp
->
p_item
;
else
return
NULL
;
}
/**
* @brief Find an item list item
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return input item if found, NULL if not found
*/
item_list_t
*
item_list_listitemOfMediaId
(
watch_thread_t
*
p_wt
,
int
i_media_id
)
{
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
il_foreachhashlist
(
p_wt
->
p_hlist
,
p_elt
,
ixx
)
{
if
(
p_elt
->
i_media_id
==
i_media_id
)
{
p_elt
->
i_age
=
0
;
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
p_elt
;
}
}
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
NULL
;
}
/**
* @brief Find a media
* @param p_ml this media library
* @param i_media_id item to find and gc_incref
* @return media if found. NULL otherwise
*/
ml_media_t
*
item_list_mediaOfMediaId
(
watch_thread_t
*
p_wt
,
int
i_media_id
)
{
item_list_t
*
p_tmp
=
item_list_listitemOfMediaId
(
p_wt
,
i_media_id
);
if
(
p_tmp
)
return
p_tmp
->
p_media
;
else
return
NULL
;
}
/**
* @brief Find a media ID by its input_item
* @param p_ml this media library
* @param p_item item to find
* @return media_id found, or VLC_EGENERIC
*/
int
item_list_mediaIdOfItem
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
)
{
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
il_foreachlist
(
p_wt
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
==
p_item
)
{
if
(
p_elt
->
i_media_id
<=
0
)
/* TODO! */
p_elt
->
i_age
=
0
;
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
p_elt
->
i_media_id
;
}
}
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
VLC_EGENERIC
;
}
/**
* @brief Find a media by its input_item
* @param p_ml this media library
* @param p_item item to find
* @return media found, or VLC_EGENERIC
*/
ml_media_t
*
item_list_mediaOfItem
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
,
bool
locked
)
{
if
(
!
locked
)
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
il_foreachlist
(
p_wt
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
==
p_item
)
{
p_wt
->
p_hlist
[
item_hash
(
p_item
)
]
=
p_elt
->
p_next
;
p_elt
->
p_next
=
NULL
;
if
(
!
locked
)
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
p_elt
->
p_media
;
}
}
if
(
!
locked
)
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
NULL
;
}
/**
* @brief Flag an item as updated
* @param p_ml this media library
* @param p_item item to find and flag
* @param b_played raise play count or not, update last play
* @return media_id found, or VLC_EGENERIC
*/
int
item_list_updateInput
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
,
bool
b_played
)
{
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
il_foreachlist
(
p_wt
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
==
p_item
)
{
/* Item found, flag and return */
p_elt
->
i_age
=
0
;
p_elt
->
i_update
|=
b_played
?
3
:
1
;
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
p_elt
->
i_media_id
;
}
}
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
return
VLC_EGENERIC
;
}
/**
* @brief Free every item in the item list.
* @param p_wt Watch thread
* @note All decref of objects must be handled by watch system
*/
void
item_list_destroy
(
watch_thread_t
*
p_wt
)
{
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
for
(
int
i
=
0
;
i
<
ML_ITEMLIST_HASH_LENGTH
;
i
++
)
{
for
(
item_list_t
*
p_elt
=
p_wt
->
p_hlist
[
i
]
;
p_elt
;
p_elt
=
p_wt
->
p_hlist
[
i
]
)
{
p_wt
->
p_hlist
[
i
]
=
p_elt
->
p_next
;
free
(
p_elt
);
}
}
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
}
modules/media_library/item_list.h
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* item_list.h : Item list data structure for Watching system
*****************************************************************************
* Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: 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.
*****************************************************************************/
#ifndef ML_ITEM_LIST_H
#define ML_ITEM_LIST_H
#include <vlc_input.h>
#include <vlc_arrays.h>
struct
watch_thread_t
;
typedef
struct
watch_thread_t
watch_thread_t
;
typedef
struct
item_list_t
item_list_t
;
/**
* Definition of item_list_t
*/
struct
item_list_t
{
input_item_t
*
p_item
;
/**< Input item */
ml_media_t
*
p_media
;
/**< Media item */
item_list_t
*
p_next
;
/**< Next element in the list */
int
i_media_id
;
/**< Media id */
int
i_age
;
/**< Time spent in this list without activity */
int
i_refs
;
/**< Number of important refs */
int
i_update
;
/**< Flag set when the input item is updated:
0: no update,
1: meta update,
2: increment play count,
3: both */
};
#define ML_ITEMLIST_HASH_LENGTH 40
#define il_foreachhashlist( a, b, c ) \
for( int c = 0 ; c < ML_ITEMLIST_HASH_LENGTH ; c++ ) \
for( item_list_t* b = a[c]; b; b = b->p_next )
#define il_foreachlist( a, b ) for( item_list_t* b = a ; b; b = b->p_next )
#define item_list_add( a, b, c ) __item_list_add( a, b, c, false )
int
__item_list_add
(
watch_thread_t
*
p_wt
,
ml_media_t
*
p_media
,
input_item_t
*
p_item
,
bool
);
item_list_t
*
item_list_delMedia
(
watch_thread_t
*
p_wt
,
int
i_media_id
);
item_list_t
*
item_list_delItem
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
,
bool
);
item_list_t
*
item_list_listitemOfMediaId
(
watch_thread_t
*
p_wt
,
int
i_media_id
);
input_item_t
*
item_list_itemOfMediaId
(
watch_thread_t
*
p_wt
,
int
i_media_id
);
ml_media_t
*
item_list_mediaOfMediaId
(
watch_thread_t
*
p_wt
,
int
i_media_id
);
ml_media_t
*
item_list_mediaOfItem
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
,
bool
);
int
item_list_mediaIdOfItem
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
);
int
item_list_updateInput
(
watch_thread_t
*
p_wt
,
input_item_t
*
p_item
,
bool
b_play_count
);
void
item_list_destroy
(
watch_thread_t
*
p_wt
);
/**
* @brief Simple hash function
* @param item_id Hash Key
* @return Hash index
*/
static
inline
int
item_hash
(
input_item_t
*
p_item
)
{
return
DictHash
(
p_item
->
psz_uri
,
ML_ITEMLIST_HASH_LENGTH
);
}
#endif
/* ML_ITEM_LIST_H */
modules/media_library/media_pool.c
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* media_pool.c : Media pool for watching system
*****************************************************************************
* Copyright (C) 2009-2010 the VideoLAN team and AUTHORS
* $Id$
*
* Authors: 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"
#define mp_foreachlist( a, b ) for( ml_poolobject_t* b = a; b; b = b->p_next )
static
inline
int
mediapool_hash
(
int
media_id
)
{
return
media_id
%
ML_MEDIAPOOL_HASH_LENGTH
;
}
/**
* @brief Get a media from the pool
* @param p_ml ML object
* @param media_id The media id of the object to get
* @return the found media or NULL if not found
*/
ml_media_t
*
pool_GetMedia
(
media_library_t
*
p_ml
,
int
media_id
)
{
vlc_mutex_lock
(
&
p_ml
->
p_sys
->
pool_mutex
);
ml_media_t
*
p_media
=
NULL
;
mp_foreachlist
(
p_ml
->
p_sys
->
p_mediapool
[
mediapool_hash
(
media_id
)
],
p_item
)
{
if
(
p_item
->
p_media
->
i_id
==
media_id
)
{
p_media
=
p_item
->
p_media
;
break
;
}
}
if
(
p_media
)
ml_gc_incref
(
p_media
);
vlc_mutex_unlock
(
&
p_ml
->
p_sys
->
pool_mutex
);
return
p_media
;
}
/**
* @brief Insert a media into the media pool
* @param p_ml ML object
* @param p_media Media object to insert
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int
pool_InsertMedia
(
media_library_t
*
p_ml
,
ml_media_t
*
p_media
,
bool
locked
)
{
if
(
!
locked
)
ml_LockMedia
(
p_media
);
assert
(
p_media
);
assert
(
p_media
->
i_id
>
0
);
if
(
p_media
->
ml_gc_data
.
pool
)
{
msg_Dbg
(
p_ml
,
"Already in pool! %s %d"
,
p_media
->
psz_uri
,
p_media
->
i_id
);
ml_UnlockMedia
(
p_media
);
return
VLC_EGENERIC
;
}
p_media
->
ml_gc_data
.
pool
=
true
;
int
i_ret
=
VLC_SUCCESS
;
vlc_mutex_lock
(
&
p_ml
->
p_sys
->
pool_mutex
);
mp_foreachlist
(
p_ml
->
p_sys
->
p_mediapool
[
(
mediapool_hash
(
p_media
->
i_id
))
],
p_item
)
{
if
(
p_media
==
p_item
->
p_media
)
{
i_ret
=
VLC_EGENERIC
;
break
;
}
else
if
(
p_media
->
i_id
==
p_item
->
p_media
->
i_id
)
{
i_ret
=
VLC_EGENERIC
;
msg_Warn
(
p_ml
,
"A media of the same id was found, but in different objects!"
);
break
;
}
}
if
(
i_ret
==
VLC_SUCCESS
)
{
ml_poolobject_t
*
p_new
=
(
ml_poolobject_t
*
)
calloc
(
1
,
sizeof
(
ml_poolobject_t
*
)
);
if
(
!
p_new
)
i_ret
=
VLC_EGENERIC
;
else
{
ml_gc_incref
(
p_media
);
p_new
->
p_media
=
p_media
;
p_new
->
p_next
=
p_ml
->
p_sys
->
p_mediapool
[
(
mediapool_hash
(
p_media
->
i_id
)
)
];
p_ml
->
p_sys
->
p_mediapool
[
(
mediapool_hash
(
p_media
->
i_id
)
)
]
=
p_new
;
}
}
vlc_mutex_unlock
(
&
p_ml
->
p_sys
->
pool_mutex
);
if
(
!
locked
)
ml_UnlockMedia
(
p_media
);
return
i_ret
;
}
/**
* @brief Perform a single garbage collection scan on the media pool
* @param p_ml The ML object
* @note Scans all media and removes any medias not held by any other objects.
*/
void
pool_GC
(
media_library_t
*
p_ml
)
{
vlc_mutex_lock
(
&
p_ml
->
p_sys
->
pool_mutex
);
ml_poolobject_t
*
p_prev
=
NULL
;
ml_media_t
*
p_media
=
NULL
;
for
(
int
i_idx
=
0
;
i_idx
<
ML_MEDIAPOOL_HASH_LENGTH
;
i_idx
++
)
{
p_prev
=
NULL
;
for
(
ml_poolobject_t
*
p_item
=
p_ml
->
p_sys
->
p_mediapool
[
i_idx
];
p_item
!=
NULL
;
p_item
=
p_item
->
p_next
)
{
p_media
=
p_item
->
p_media
;
int
refs
;
refs
=
p_media
->
ml_gc_data
.
refs
;
if
(
refs
==
1
)
{
if
(
p_prev
==
NULL
)
p_ml
->
p_sys
->
p_mediapool
[
i_idx
]
=
p_item
->
p_next
;
else
p_prev
->
p_next
=
p_item
->
p_next
;
p_media
->
ml_gc_data
.
pool
=
false
;
ml_gc_decref
(
p_item
->
p_media
);
//This should destroy the object
free
(
p_item
);
}
p_prev
=
p_item
;
}
}
vlc_mutex_unlock
(
&
p_ml
->
p_sys
->
pool_mutex
);
}
modules/media_library/ml_watch.c
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* ml_watch.c: SQL-based media library: Medias watching system
*****************************************************************************
* 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"
#include "item_list.h"
#include <vlc_events.h>
static
void
watch_ItemChange
(
const
vlc_event_t
*
,
void
*
);
static
int
watch_PlaylistItemCurrent
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
data
);
static
int
watch_PlaylistItemAppend
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
data
);
static
int
watch_PlaylistItemDeleted
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
data
);
static
void
watch_loop
(
media_library_t
*
p_ml
,
bool
b_force
);
static
void
watch_Thread_Cleanup
(
void
*
p_object
);
static
int
watch_update_Item
(
media_library_t
*
p_ml
,
int
i_media_id
,
input_item_t
*
p_item
,
bool
b_raise_count
,
bool
locked
);
static
void
watch_ProcessAppendQueue
(
media_library_t
*
p_ml
);
/**
* @brief Watching thread
*/
static
void
*
watch_Thread
(
void
*
obj
)
{
watch_thread_t
*
p_watch
=
(
watch_thread_t
*
)
obj
;
media_library_t
*
p_ml
=
p_watch
->
p_ml
;
int
i_ret
=
0
;
vlc_mutex_lock
(
&
p_watch
->
lock
);
vlc_cleanup_push
(
watch_Thread_Cleanup
,
p_ml
);
for
(
;;
)
{
watch_loop
(
p_ml
,
!
i_ret
);
i_ret
=
vlc_cond_timedwait
(
&
p_watch
->
cond
,
&
p_watch
->
lock
,
mdate
()
+
1000000
*
THREAD_SLEEP_DELAY
);
}
vlc_cleanup_run
();
return
NULL
;
}
/**
* @brief Callback for thread exit
*/
static
void
watch_Thread_Cleanup
(
void
*
p_object
)
{
media_library_t
*
p_ml
=
(
media_library_t
*
)
p_object
;
watch_loop
(
p_ml
,
true
);
vlc_mutex_unlock
(
&
p_ml
->
p_sys
->
p_watch
->
lock
);
}
/**
* @brief Init watching system
* @return Error if the object or the thread could not be created
*/
int
watch_Init
(
media_library_t
*
p_ml
)
{
/* init and launch watching thread */
p_ml
->
p_sys
->
p_watch
=
calloc
(
1
,
sizeof
(
*
p_ml
->
p_sys
->
p_watch
)
);
if
(
!
p_ml
->
p_sys
->
p_watch
)
return
VLC_ENOMEM
;
watch_thread_t
*
p_wt
=
p_ml
->
p_sys
->
p_watch
;
vlc_mutex_init
(
&
p_wt
->
list_mutex
);
p_wt
->
p_ml
=
p_ml
;
vlc_cond_init
(
&
p_wt
->
cond
);
vlc_mutex_init
(
&
p_wt
->
lock
);
/* Initialise item append queue */
vlc_mutex_init
(
&
p_wt
->
item_append_queue_lock
);
p_wt
->
item_append_queue
=
NULL
;
p_wt
->
item_append_queue_count
=
0
;
if
(
vlc_clone
(
&
p_wt
->
thread
,
watch_Thread
,
p_wt
,
VLC_THREAD_PRIORITY_LOW
)
)
{
msg_Dbg
(
p_ml
,
"unable to launch the auto-updating thread"
);
free
(
p_wt
);
return
VLC_EGENERIC
;
}
/* Wait on playlist events
* playlist-item-append -> entry to playlist
* activity -> to ensure that we catch played item only!
* playlist-item-deleted -> exit from playlist
* item-change -> Currently not required, as we monitor input_item events
*/
playlist_t
*
p_pl
=
pl_Get
(
p_ml
);
var_AddCallback
(
p_pl
,
"activity"
,
watch_PlaylistItemCurrent
,
p_ml
);
var_AddCallback
(
p_pl
,
"playlist-item-append"
,
watch_PlaylistItemAppend
,
p_ml
);
var_AddCallback
(
p_pl
,
"playlist-item-deleted"
,
watch_PlaylistItemDeleted
,
p_ml
);
return
VLC_SUCCESS
;
}
/**
* @brief Add the input to the watch system
* @param p_ml The Media Library Object
* @param p_item Item to be watched
* @param p_media Corresponding media item to sync with
* @param locked Status of item list lock
* @return VLC_SUCCESS or error code
*/
int
__watch_add_Item
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
,
ml_media_t
*
p_media
,
bool
locked
)
{
vlc_gc_incref
(
p_item
);
ml_gc_incref
(
p_media
);
int
i_ret
=
__item_list_add
(
p_ml
->
p_sys
->
p_watch
,
p_media
,
p_item
,
locked
);
if
(
i_ret
!=
VLC_SUCCESS
)
return
i_ret
;
vlc_event_manager_t
*
p_em
=
&
p_item
->
event_manager
;
vlc_event_attach
(
p_em
,
vlc_InputItemMetaChanged
,
watch_ItemChange
,
p_ml
);
vlc_event_attach
(
p_em
,
vlc_InputItemNameChanged
,
watch_ItemChange
,
p_ml
);
vlc_event_attach
(
p_em
,
vlc_InputItemInfoChanged
,
watch_ItemChange
,
p_ml
);
/*
Note: vlc_InputItemDurationChanged is disabled because
it is triggered too often, even without consequent changes
*/
return
VLC_SUCCESS
;
}
/**
* @brief Detach event manager
* @param p_ml The Media Library Object
*/
static
void
detachItemEvents
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
)
{
vlc_event_manager_t
*
p_em
=
&
p_item
->
event_manager
;
vlc_event_detach
(
p_em
,
vlc_InputItemMetaChanged
,
watch_ItemChange
,
p_ml
);
vlc_event_detach
(
p_em
,
vlc_InputItemNameChanged
,
watch_ItemChange
,
p_ml
);
vlc_event_detach
(
p_em
,
vlc_InputItemInfoChanged
,
watch_ItemChange
,
p_ml
);
}
/**
* @brief Close the watching system
* @param p_ml The Media Library Object
*/
void
watch_Close
(
media_library_t
*
p_ml
)
{
playlist_t
*
p_pl
=
pl_Get
(
p_ml
);
var_DelCallback
(
p_pl
,
"playlist-item-deleted"
,
watch_PlaylistItemDeleted
,
p_ml
);
var_DelCallback
(
p_pl
,
"playlist-item-append"
,
watch_PlaylistItemAppend
,
p_ml
);
var_DelCallback
(
p_pl
,
"activity"
,
watch_PlaylistItemCurrent
,
p_ml
);
/* Flush item list */
il_foreachhashlist
(
p_ml
->
p_sys
->
p_watch
->
p_hlist
,
p_elt
,
ixx
)
{
detachItemEvents
(
p_ml
,
p_elt
->
p_item
);
ml_gc_decref
(
p_elt
->
p_media
);
vlc_gc_decref
(
p_elt
->
p_item
);
}
item_list_destroy
(
p_ml
->
p_sys
->
p_watch
);
/* Stop the watch thread and join in */
vlc_cancel
(
p_ml
->
p_sys
->
p_watch
->
thread
);
vlc_join
(
p_ml
->
p_sys
->
p_watch
->
thread
,
NULL
);
/* Clear up other stuff */
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
p_watch
->
lock
);
vlc_cond_destroy
(
&
p_ml
->
p_sys
->
p_watch
->
cond
);
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
p_watch
->
list_mutex
);
free
(
p_ml
->
p_sys
->
p_watch
);
free
(
p_ml
->
p_sys
->
p_watch
->
item_append_queue
);
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
p_watch
->
item_append_queue_lock
);
p_ml
->
p_sys
->
p_watch
=
NULL
;
}
/**
* @brief Del item that is currently being watched
* @param p_ml The Media Library Object
* @param p_item Item to stop watching
* @param locked Lock state of item list
*/
int
__watch_del_Item
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
,
bool
locked
)
{
assert
(
p_item
);
item_list_t
*
p_tmp
=
item_list_delItem
(
p_ml
->
p_sys
->
p_watch
,
p_item
,
locked
);
if
(
p_tmp
==
NULL
)
return
VLC_EGENERIC
;
detachItemEvents
(
p_ml
,
p_tmp
->
p_item
);
vlc_gc_decref
(
p_tmp
->
p_item
);
ml_gc_decref
(
p_tmp
->
p_media
);
free
(
p_tmp
);
return
VLC_SUCCESS
;
}
/**
* @brief Del media from watching by ID
* @param p_ml The Media Library Object
* @param i_media_id Media ID
*/
int
watch_del_MediaById
(
media_library_t
*
p_ml
,
int
i_media_id
)
{
assert
(
i_media_id
>
0
);
item_list_t
*
p_elt
=
item_list_delMedia
(
p_ml
->
p_sys
->
p_watch
,
i_media_id
);
if
(
p_elt
==
NULL
)
return
VLC_EGENERIC
;
detachItemEvents
(
p_ml
,
p_elt
->
p_item
);
vlc_gc_decref
(
p_elt
->
p_item
);
ml_gc_decref
(
p_elt
->
p_media
);
free
(
p_elt
);
return
VLC_SUCCESS
;
}
/**
* @brief Get item using media id, if exists in item list
* @param p_ml The Media Library Object
* @param i_media_id Media ID
*/
input_item_t
*
watch_get_itemOfMediaId
(
media_library_t
*
p_ml
,
int
i_media_id
)
{
input_item_t
*
p_tmp
=
item_list_itemOfMediaId
(
p_ml
->
p_sys
->
p_watch
,
i_media_id
);
if
(
p_tmp
==
NULL
)
return
NULL
;
vlc_gc_incref
(
p_tmp
);
return
p_tmp
;
}
/**
* @brief Get media using media id, if exists in item list
* @param p_ml The Media Library Object
* @param i_media_id Media ID
*/
ml_media_t
*
watch_get_mediaOfMediaId
(
media_library_t
*
p_ml
,
int
i_media_id
)
{
ml_media_t
*
p_tmp
=
item_list_mediaOfMediaId
(
p_ml
->
p_sys
->
p_watch
,
i_media_id
);
if
(
p_tmp
==
NULL
)
return
NULL
;
ml_gc_incref
(
p_tmp
);
return
p_tmp
;
}
/**
* @brief Get mediaid of existing item
* @param p_ml The Media Library Object
* @param p_item Pointer to input item
*/
int
watch_get_mediaIdOfItem
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
)
{
return
item_list_mediaIdOfItem
(
p_ml
->
p_sys
->
p_watch
,
p_item
);
}
/**
* @brief Updates a media each time it is changed (name, info or meta)
*/
static
void
watch_ItemChange
(
const
vlc_event_t
*
event
,
void
*
data
)
{
input_item_t
*
p_item
=
(
input_item_t
*
)
event
->
p_obj
;
media_library_t
*
p_ml
=
(
media_library_t
*
)
data
;
/* Note: we don't add items to the item_list, but normally there should
not be any item at this point that is not in the list. */
if
(
item_list_updateInput
(
p_ml
->
p_sys
->
p_watch
,
p_item
,
false
)
<=
0
)
{
#ifndef NDEBUG
msg_Dbg
(
p_ml
,
"Couldn't update in watch_ItemChange(): (%s:%d)"
,
__FILE__
,
__LINE__
);
#endif
}
/*
if( event->type == vlc_InputItemMetaChanged )
{
int id = item_list_mediaIdOfItem( p_ml->p_sys->p_watch, p_item );
if( !id ) return;
* Tell the world what happened *
var_SetInteger( p_ml, "media-meta-change", id );
}
*/
}
/**
* @brief Callback when item is added to playlist
*/
static
int
watch_PlaylistItemAppend
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
data
)
{
VLC_UNUSED
(
oldval
);
VLC_UNUSED
(
p_this
);
VLC_UNUSED
(
psz_var
);
media_library_t
*
p_ml
=
(
media_library_t
*
)
data
;
playlist_t
*
p_playlist
=
pl_Get
(
p_ml
);
playlist_add_t
*
p_add
;
p_add
=
(
playlist_add_t
*
)
newval
.
p_address
;
playlist_item_t
*
p_pitem
=
playlist_ItemGetById
(
p_playlist
,
p_add
->
i_item
);
input_item_t
*
p_item
=
p_pitem
->
p_input
;
watch_thread_t
*
p_wt
=
p_ml
->
p_sys
->
p_watch
;
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
/* Check if we are already watching this item */
il_foreachlist
(
p_wt
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
->
i_id
==
p_item
->
i_id
)
{
p_elt
->
i_refs
++
;
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
goto
quit_playlistitemappend
;
}
}
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
/* Add the the append queue */
vlc_mutex_lock
(
&
p_wt
->
item_append_queue_lock
);
p_wt
->
item_append_queue_count
++
;
p_wt
->
item_append_queue
=
realloc
(
p_wt
->
item_append_queue
,
sizeof
(
input_item_t
*
)
*
p_wt
->
item_append_queue_count
);
vlc_gc_incref
(
p_item
);
p_wt
->
item_append_queue
[
p_wt
->
item_append_queue_count
-
1
]
=
p_item
;
vlc_mutex_unlock
(
&
p_wt
->
item_append_queue_lock
);
quit_playlistitemappend:
return
VLC_SUCCESS
;
}
/**
* @brief Callback when item is deleted from playlist
*/
static
int
watch_PlaylistItemDeleted
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
data
)
{
VLC_UNUSED
(
oldval
);
VLC_UNUSED
(
p_this
);
VLC_UNUSED
(
psz_var
);
media_library_t
*
p_ml
=
(
media_library_t
*
)
data
;
playlist_t
*
p_playlist
=
pl_Get
(
p_ml
);
/* Luckily this works, because the item isn't deleted from PL, yet */
playlist_item_t
*
p_pitem
=
playlist_ItemGetById
(
p_playlist
,
newval
.
i_int
);
input_item_t
*
p_item
=
p_pitem
->
p_input
;
/* Find the new item and decrement its ref */
il_foreachlist
(
p_ml
->
p_sys
->
p_watch
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
->
i_id
==
p_item
->
i_id
)
{
p_elt
->
i_refs
--
;
break
;
}
}
return
VLC_SUCCESS
;
}
/**
* @brief Callback when watched input item starts playing
* @note This will update playcount mainly
* TODO: Increment playcount on playing 50%(configurable)
*/
static
int
watch_PlaylistItemCurrent
(
vlc_object_t
*
p_this
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
data
)
{
(
void
)
p_this
;
(
void
)
oldval
;
(
void
)
newval
;
media_library_t
*
p_ml
=
(
media_library_t
*
)
data
;
input_item_t
*
p_item
=
NULL
;
/* Get current input */
input_thread_t
*
p_input
=
pl_CurrentInput
(
p_ml
);
p_item
=
p_input
?
input_GetItem
(
p_input
)
:
NULL
;
if
(
p_input
)
vlc_object_release
(
p_input
);
if
(
!
p_item
)
return
VLC_EGENERIC
;
if
(
item_list_updateInput
(
p_ml
->
p_sys
->
p_watch
,
p_item
,
true
)
==
0
)
{
#ifndef NDEBUG
msg_Dbg
(
p_ml
,
"couldn't in watch_PlaylistItemCurrent(): (%s:%d)"
,
__FILE__
,
__LINE__
);
#endif
}
return
VLC_SUCCESS
;
}
/**
* @brief Update information in the DB for an input item
*
* @param p_ml this media library instance
* @param i_media_id may be 0 (but not recommended)
* @param p_item input item that was updated
* @param b_raise_count increment the played count
* @return result of UpdateMedia()
*/
static
int
watch_update_Item
(
media_library_t
*
p_ml
,
int
i_media_id
,
input_item_t
*
p_item
,
bool
b_raise_count
,
bool
locked
)
{
#ifndef NDEBUG
msg_Dbg
(
p_ml
,
"automatically updating media %d"
,
i_media_id
);
#endif
ml_media_t
*
p_media
=
item_list_mediaOfItem
(
p_ml
->
p_sys
->
p_watch
,
p_item
,
locked
);
CopyInputItemToMedia
(
p_media
,
p_item
);
ml_LockMedia
(
p_media
);
p_media
->
i_played_count
+=
b_raise_count
?
1
:
0
;
ml_UnlockMedia
(
p_media
);
int
i_ret
=
UpdateMedia
(
p_ml
,
p_media
);
/* Add the poster to the album */
ml_LockMedia
(
p_media
);
if
(
p_media
->
i_album_id
&&
p_media
->
psz_cover
)
{
SetArtCover
(
p_ml
,
p_media
->
i_album_id
,
p_media
->
psz_cover
);
}
ml_UnlockMedia
(
p_media
);
return
i_ret
;
}
/**
* @brief Signals the watch system to update all medias
*/
void
watch_Force_Update
(
media_library_t
*
p_ml
)
{
vlc_mutex_lock
(
&
p_ml
->
p_sys
->
p_watch
->
lock
);
vlc_cond_signal
(
&
p_ml
->
p_sys
->
p_watch
->
cond
);
vlc_mutex_unlock
(
&
p_ml
->
p_sys
->
p_watch
->
lock
);
}
/**
* @brief Loop on the item_list: old objects collector and automatic updater
*
* This function is *not* a garbage collector. It actually decrefs items
* when they are too old. ITEM_GC_MAX_AGE is the maximum 'time' an item
* can stay in the list. After that, it is gc_decref'ed but not removed
* from this list. If you try to get it after that, either the input item
* is still alive, then you get it, or you'll have
*
* The update of an item is done when its age is >= ITEM_LOOP_UPDATE
* (0 could lead to a too early update)
*
* A thread should call this function every N seconds
*
* @param p_ml the media library instance
*/
static
void
watch_loop
(
media_library_t
*
p_ml
,
bool
b_force
)
{
/* Do the garbage collection */
pool_GC
(
p_ml
);
/* Process the append queue */
watch_ProcessAppendQueue
(
p_ml
);
/* Do the item update if necessary */
vlc_mutex_lock
(
&
p_ml
->
p_sys
->
p_watch
->
list_mutex
);
item_list_t
*
p_prev
=
NULL
;
il_foreachhashlist
(
p_ml
->
p_sys
->
p_watch
->
p_hlist
,
p_elt
,
ixx
)
{
if
(
(
p_elt
->
i_update
&&
p_elt
->
i_age
>=
ITEM_LOOP_UPDATE
)
||
b_force
)
{
/* This is the automatic delayed update */
watch_update_Item
(
p_ml
,
p_elt
->
i_media_id
,
p_elt
->
p_item
,
(
p_elt
->
i_update
&
2
)
?
true
:
false
,
true
);
/* The item gets older */
p_prev
=
p_elt
;
p_elt
->
i_age
++
;
p_elt
->
i_update
=
false
;
}
else
if
(
p_elt
->
i_refs
==
0
)
{
if
(
p_elt
->
i_update
)
watch_update_Item
(
p_ml
,
p_elt
->
i_media_id
,
p_elt
->
p_item
,
(
p_elt
->
i_update
&
2
)
?
true
:
false
,
true
);
__watch_del_Item
(
p_ml
,
p_elt
->
p_item
,
true
);
/* TODO: Do something about below crazy hack */
if
(
p_prev
!=
NULL
)
p_elt
=
p_prev
;
else
{
ixx
--
;
break
;
}
}
else
{
p_prev
=
p_elt
;
p_elt
->
i_age
++
;
}
}
vlc_mutex_unlock
(
&
p_ml
->
p_sys
->
p_watch
->
list_mutex
);
}
/**
* This function goes through a queue of input_items and checks
* if they are present in ML. All the items we wish to add in the
* watch Queue
*/
static
void
watch_ProcessAppendQueue
(
media_library_t
*
p_ml
)
{
watch_thread_t
*
p_wt
=
p_ml
->
p_sys
->
p_watch
;
vlc_mutex_lock
(
&
p_wt
->
item_append_queue_lock
);
bool
b_add
=
var_CreateGetBool
(
p_ml
,
"ml-auto-add"
);
for
(
int
i
=
0
;
i
<
p_wt
->
item_append_queue_count
;
i
++
)
{
input_item_t
*
p_item
=
p_wt
->
item_append_queue
[
i
];
ml_media_t
*
p_media
=
NULL
;
/* Is this item in ML? */
int
i_media_id
=
GetMediaIdOfURI
(
p_ml
,
p_item
->
psz_uri
);
int
i_ret
=
0
;
if
(
i_media_id
<=
0
)
{
if
(
b_add
)
{
i_ret
=
AddInputItem
(
p_ml
,
p_item
);
/* FIXME: Need to skip? */
if
(
i_ret
!=
VLC_SUCCESS
)
continue
;
i_media_id
=
GetMediaIdOfURI
(
p_ml
,
p_item
->
psz_uri
);
}
else
continue
;
}
vlc_mutex_lock
(
&
p_wt
->
list_mutex
);
p_media
=
media_New
(
p_ml
,
i_media_id
,
ML_MEDIA
,
true
);
if
(
p_media
==
NULL
)
{
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
continue
;
}
/* If duplicate, then it just continues */
i_ret
=
__watch_add_Item
(
p_ml
,
p_item
,
p_media
,
true
);
if
(
i_ret
!=
VLC_SUCCESS
)
{
ml_gc_decref
(
p_media
);
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
continue
;
}
/* Find the new item and increment its ref */
il_foreachlist
(
p_wt
->
p_hlist
[
item_hash
(
p_item
)
],
p_elt
)
{
if
(
p_elt
->
p_item
->
i_id
==
p_item
->
i_id
)
{
p_elt
->
i_refs
++
;
break
;
}
}
vlc_mutex_unlock
(
&
p_wt
->
list_mutex
);
ml_gc_decref
(
p_media
);
}
p_wt
->
item_append_queue_count
=
0
;
FREENULL
(
p_wt
->
item_append_queue
);
vlc_mutex_unlock
(
&
p_wt
->
item_append_queue_lock
);
}
modules/media_library/sql_add.c
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* sql_add.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
||
!
p_input
->
psz_uri
)
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
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* 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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#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_media_library.c
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* 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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "sql_media_library.h"
static
const
char
*
ppsz_AudioExtensions
[]
=
{
EXTENSIONS_AUDIO_CSV
,
NULL
};
static
const
char
*
ppsz_VideoExtensions
[]
=
{
EXTENSIONS_VIDEO_CSV
,
NULL
};
#define MEDIA_LIBRARY_PATH_TEXT N_( "Filename of the SQLite database" )
#define MEDIA_LIBRARY_PATH_LONGTEXT N_( "Path to the file containing " \
"the SQLite database" )
#define IGNORE_TEXT N_( "Ignored extensions in the media library" )
#define IGNORE_LONGTEXT N_( "Files with these extensions will not be added to"\
" the media library when scanning directories." )
#define RECURSIVE_TEXT N_( "Subdirectory recursive scanning" )
#define RECURSIVE_LONGTEXT N_( "When scanning a directory, scan also all its"\
" subdirectories." )
/*****************************************************************************
* Static functions
*****************************************************************************/
/* Module entry point and exit point */
static
int
load
(
vlc_object_t
*
);
static
void
unload
(
vlc_object_t
*
);
static
int
CreateInputItemFromMedia
(
input_item_t
**
pp_item
,
ml_media_t
*
p_media
);
struct
ml_table_elt
{
int
column_id
;
const
char
*
column_name
;
};
static
int
compare_ml_elts
(
const
void
*
a
,
const
void
*
b
)
{
return
strcmp
(
(
(
struct
ml_table_elt
*
)
a
)
->
column_name
,
(
(
struct
ml_table_elt
*
)
b
)
->
column_name
);
}
static
const
struct
ml_table_elt
ml_table_map
[]
=
{
{
ML_ALBUM_COVER
,
"album_cover"
},
{
ML_ALBUM_ID
,
"album_id"
},
{
ML_ALBUM
,
"album_title"
},
{
ML_COMMENT
,
"comment"
},
{
ML_COVER
,
"cover"
},
{
ML_DIRECTORY
,
"directory_id"
},
{
ML_DISC_NUMBER
,
"disc"
},
{
ML_DURATION
,
"duration"
},
{
ML_EXTRA
,
"extra"
},
{
ML_FILESIZE
,
"filesize"
},
{
ML_FIRST_PLAYED
,
"first_played"
},
{
ML_GENRE
,
"genre"
},
{
ML_ID
,
"id"
},
{
ML_IMPORT_TIME
,
"import_time"
},
{
ML_LANGUAGE
,
"language"
},
{
ML_LAST_PLAYED
,
"last_played"
},
{
ML_LAST_SKIPPED
,
"last_skipped"
},
{
ML_ORIGINAL_TITLE
,
"original_title"
},
{
ML_PEOPLE_ID
,
"people_id"
},
{
ML_PEOPLE
,
"people_name"
},
{
ML_PEOPLE_ROLE
,
"people_role"
},
{
ML_PLAYED_COUNT
,
"played_count"
},
{
ML_PREVIEW
,
"preview"
},
{
ML_SCORE
,
"score"
},
{
ML_SKIPPED_COUNT
,
"skipped_count"
},
{
ML_TITLE
,
"title"
},
{
ML_TRACK_NUMBER
,
"track"
},
{
ML_TYPE
,
"type"
},
{
ML_URI
,
"uri"
},
{
ML_VOTE
,
"vote"
},
{
ML_YEAR
,
"year"
}
};
/*****************************************************************************
* Module description
*****************************************************************************/
vlc_module_begin
()
set_shortname
(
"Media Library"
)
set_description
(
_
(
"Media Library based on a SQL based database"
)
)
set_capability
(
"media-library"
,
1
)
set_callbacks
(
load
,
unload
)
set_category
(
CAT_ADVANCED
)
set_subcategory
(
SUBCAT_ADVANCED_MISC
)
add_string
(
"ml-filename"
,
"vlc-media-library.db"
,
MEDIA_LIBRARY_PATH_TEXT
,
MEDIA_LIBRARY_PATH_LONGTEXT
,
false
)
add_string
(
"ml-username"
,
""
,
N_
(
"Username for the database"
),
N_
(
"Username for the database"
),
false
)
add_string
(
"ml-password"
,
""
,
N_
(
"Password for the database"
),
N_
(
"Password for the database"
),
false
)
add_integer
(
"ml-port"
,
0
,
N_
(
"Port for the database"
),
N_
(
"Port for the database"
),
false
)
add_bool
(
"ml-recursive-scan"
,
true
,
RECURSIVE_TEXT
,
RECURSIVE_LONGTEXT
,
false
)
add_bool
(
"ml-auto-add"
,
true
,
N_
(
"Auto add new medias"
),
N_
(
"Automatically add new medias to ML"
),
false
)
add_bool
(
"ml-synchronous"
,
true
,
N_
(
"Use transactions"
),
N_
(
"Disabling transactions saves I/O but can corrupt database in case of crash"
),
false
)
vlc_module_end
()
/**
* @brief Load module
* @param obj Parent object
*/
static
int
load
(
vlc_object_t
*
obj
)
{
msg_Dbg
(
obj
,
"loading media library module"
);
media_library_t
*
p_ml
=
(
media_library_t
*
)
obj
;
p_ml
->
p_sys
=
(
media_library_sys_t
*
)
calloc
(
1
,
sizeof
(
media_library_sys_t
)
);
if
(
!
p_ml
->
p_sys
)
return
VLC_ENOMEM
;
p_ml
->
functions
.
pf_Find
=
FindVa
;
p_ml
->
functions
.
pf_FindAdv
=
FindAdv
;
p_ml
->
functions
.
pf_Control
=
Control
;
p_ml
->
functions
.
pf_InputItemFromMedia
=
GetInputItemFromMedia
;
p_ml
->
functions
.
pf_Update
=
Update
;
p_ml
->
functions
.
pf_Delete
=
Delete
;
p_ml
->
functions
.
pf_GetMedia
=
GetMedia
;
vlc_mutex_init
(
&
p_ml
->
p_sys
->
lock
);
/* Initialise Sql module */
if
(
InitDatabase
(
p_ml
)
!=
VLC_SUCCESS
)
{
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
lock
);
free
(
p_ml
->
p_sys
);
return
VLC_EGENERIC
;
}
/* Initialise the media pool */
ARRAY_INIT
(
p_ml
->
p_sys
->
mediapool
);
vlc_mutex_init
(
&
p_ml
->
p_sys
->
pool_mutex
);
/* Create variables system */
var_Create
(
p_ml
,
"media-added"
,
VLC_VAR_INTEGER
);
var_Create
(
p_ml
,
"media-deleted"
,
VLC_VAR_INTEGER
);
var_Create
(
p_ml
,
"media-meta-change"
,
VLC_VAR_INTEGER
);
/* Launching the directory monitoring thread */
monitoring_thread_t
*
p_mon
=
vlc_object_create
(
p_ml
,
sizeof
(
monitoring_thread_t
)
);
if
(
!
p_mon
)
{
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
lock
);
sql_Destroy
(
p_ml
->
p_sys
->
p_sql
);
free
(
p_ml
->
p_sys
);
return
VLC_ENOMEM
;
}
p_ml
->
p_sys
->
p_mon
=
p_mon
;
p_mon
->
p_ml
=
p_ml
;
if
(
vlc_clone
(
&
p_mon
->
thread
,
RunMonitoringThread
,
p_mon
,
VLC_THREAD_PRIORITY_LOW
)
)
{
msg_Err
(
p_ml
,
"cannot spawn the media library monitoring thread"
);
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
lock
);
sql_Destroy
(
p_ml
->
p_sys
->
p_sql
);
free
(
p_ml
->
p_sys
);
vlc_object_release
(
p_mon
);
return
VLC_EGENERIC
;
}
/* Starting the watching system (starts a thread) */
watch_Init
(
p_ml
);
msg_Dbg
(
p_ml
,
"Media library module loaded successfully"
);
return
VLC_SUCCESS
;
}
/**
* @brief Unload module
*
* @param obj the media library object
* @return Nothing
*/
static
void
unload
(
vlc_object_t
*
obj
)
{
media_library_t
*
p_ml
=
(
media_library_t
*
)
obj
;
/* Stopping the watching system */
watch_Close
(
p_ml
);
/* Stop the monitoring thread */
vlc_cancel
(
p_ml
->
p_sys
->
p_mon
->
thread
);
vlc_join
(
p_ml
->
p_sys
->
p_mon
->
thread
,
NULL
);
vlc_object_release
(
p_ml
->
p_sys
->
p_mon
);
/* Destroy the variable */
var_Destroy
(
p_ml
,
"media-meta-change"
);
var_Destroy
(
p_ml
,
"media-deleted"
);
var_Destroy
(
p_ml
,
"media-added"
);
/* Empty the media pool */
ml_media_t
*
item
;
FOREACH_ARRAY
(
item
,
p_ml
->
p_sys
->
mediapool
)
ml_gc_decref
(
item
);
FOREACH_END
()
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
pool_mutex
);
sql_Destroy
(
p_ml
->
p_sys
->
p_sql
);
vlc_mutex_destroy
(
&
p_ml
->
p_sys
->
lock
);
free
(
p_ml
->
p_sys
);
}
/**
* @brief Get results of an SQL-Query on the database (please : free the result)
*
* @param p_ml the media library object
* @param ppp_res char *** in which to store the table of results (allocated)
* @param pi_rows resulting row number in table
* @param pi_cols resulting column number in table
* @param psz_fmt query command with printf-like format enabled
* @param va_args format the command
* @return VLC_SUCCESS or a VLC error code
*/
int
Query
(
media_library_t
*
p_ml
,
char
***
ppp_res
,
int
*
pi_rows
,
int
*
pi_cols
,
const
char
*
psz_fmt
,
...
)
{
va_list
argp
;
va_start
(
argp
,
psz_fmt
);
int
i_ret
=
QueryVa
(
p_ml
,
ppp_res
,
pi_rows
,
pi_cols
,
psz_fmt
,
argp
);
va_end
(
argp
);
return
i_ret
;
}
/**
* @brief Get results of an SQL-Query on the database (please : free the result)
*
* @param p_ml the media library object
* @param ppp_res char *** in which to store the table of results (allocated)
* @param pi_rows resulting row number in table
* @param pi_cols resulting column number in table
* @param psz_fmt query command with printf-like format enabled
* @param va_args format the command
* @return VLC_SUCCESS or a VLC error code
*/
int
QueryVa
(
media_library_t
*
p_ml
,
char
***
ppp_res
,
int
*
pi_rows
,
int
*
pi_cols
,
const
char
*
psz_fmt
,
va_list
argp
)
{
assert
(
p_ml
);
if
(
!
ppp_res
||
!
psz_fmt
)
return
VLC_EGENERIC
;
char
*
psz_query
=
sql_VPrintf
(
p_ml
->
p_sys
->
p_sql
,
psz_fmt
,
argp
);
if
(
!
psz_query
)
return
VLC_ENOMEM
;
int
i_ret
=
sql_Query
(
p_ml
->
p_sys
->
p_sql
,
psz_query
,
ppp_res
,
pi_rows
,
pi_cols
);
free
(
psz_query
);
return
i_ret
;
}
/**
* @brief Do a SQL-query without any data coming back
*
* @param p_ml the media library object
* @param psz_fmt query command with printf-like format enabled
* @param va_args format the command
* @return VLC_SUCCESS or a VLC error code
*/
int
QuerySimple
(
media_library_t
*
p_ml
,
const
char
*
psz_fmt
,
...
)
{
va_list
argp
;
va_start
(
argp
,
psz_fmt
);
int
i_ret
=
QuerySimpleVa
(
p_ml
,
psz_fmt
,
argp
);
va_end
(
argp
);
return
i_ret
;
}
/**
* @brief Do a SQL-query without any data coming back
*
* @param p_ml the media library object
* @param psz_fmt query command with printf-like format enabled
* @param argp format the command
* @return VLC_SUCCESS or a VLC error code
*/
int
QuerySimpleVa
(
media_library_t
*
p_ml
,
const
char
*
psz_fmt
,
va_list
argp
)
{
assert
(
p_ml
);
int
i_ret
=
VLC_SUCCESS
;
int
i_rows
,
i_cols
;
char
**
pp_results
=
NULL
;
i_ret
=
QueryVa
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
psz_fmt
,
argp
);
FreeSQLResult
(
p_ml
,
pp_results
);
va_end
(
argp
);
return
i_ret
;
}
/**
* @brief Transforms a string to a ml_result_t, with given type and id (as psz)
*
* @param res the result of the function
* @param psz string to transform into a result
* @param psz_id id as a string
* @param result_type type of the result
* @return ID or a VLC error code
*/
int
StringToResult
(
ml_result_t
*
p_result
,
const
char
*
psz
,
const
char
*
psz_id
,
ml_result_type_e
result_type
)
{
memset
(
&
p_result
->
value
,
0
,
sizeof
(
p_result
->
value
)
);
p_result
->
id
=
psz_id
?
atoi
(
psz_id
)
:
0
;
p_result
->
type
=
result_type
;
switch
(
result_type
)
{
case
ML_TYPE_INT
:
p_result
->
value
.
i
=
psz
?
atoi
(
psz
)
:
0
;
break
;
case
ML_TYPE_TIME
:
p_result
->
value
.
time
=
psz
?
(
mtime_t
)
atoi
(
psz
)
:
(
mtime_t
)
0LL
;
break
;
case
ML_TYPE_PSZ
:
p_result
->
value
.
psz
=
psz
?
strdup
(
psz
)
:
NULL
;
break
;
case
ML_TYPE_MEDIA
:
default:
/* This is an error */
return
VLC_EGENERIC
;
}
return
p_result
->
id
;
}
/**
* @brief fills an ml_result_array_t with result of an SQL query
*
* @param p_ml the media library object
* @param p_media ml_result_array_t object to fill
* @param pp_results result of sql query
* @param i_rows row number
* @param i_cols column number
* @param result_type type of the result
* @return VLC_SUCCESS or a VLC error code
**/
int
SQLToResultArray
(
media_library_t
*
p_ml
,
vlc_array_t
*
p_result_array
,
char
**
pp_results
,
int
i_rows
,
int
i_cols
,
ml_result_type_e
result_type
)
{
assert
(
p_ml
);
if
(
!
p_result_array
)
return
VLC_EGENERIC
;
if
(
i_cols
==
0
)
/* No result */
return
VLC_SUCCESS
;
if
(
i_cols
<
0
)
{
msg_Err
(
p_ml
,
"negative number of columns in result ?"
);
return
VLC_EGENERIC
;
}
if
(
i_cols
==
1
)
{
for
(
int
i
=
1
;
i
<=
i_rows
;
i
++
)
{
ml_result_t
*
res
=
(
ml_result_t
*
)
calloc
(
1
,
sizeof
(
ml_result_t
)
);
if
(
!
res
)
return
VLC_ENOMEM
;
StringToResult
(
res
,
pp_results
[
i
],
NULL
,
result_type
);
vlc_array_append
(
p_result_array
,
res
);
}
}
/* FIXME?: Assuming all double column results are id - result pairs */
else
if
(
(
i_cols
==
2
)
)
{
for
(
int
i
=
1
;
i
<=
i_rows
;
i
++
)
{
ml_result_t
*
res
=
(
ml_result_t
*
)
calloc
(
1
,
sizeof
(
ml_result_t
)
);
if
(
!
res
)
return
VLC_ENOMEM
;
StringToResult
(
res
,
pp_results
[
i
*
2
+
1
],
pp_results
[
i
*
2
],
result_type
);
vlc_array_append
(
p_result_array
,
res
);
}
}
else
if
(
result_type
==
ML_TYPE_MEDIA
)
{
return
SQLToMediaArray
(
p_ml
,
p_result_array
,
pp_results
,
i_rows
,
i_cols
);
}
else
{
msg_Err
(
p_ml
,
"unable to convert SQL result to a ml_result_t array"
);
return
VLC_EGENERIC
;
}
return
VLC_SUCCESS
;
}
/**
* @brief fills a vlc_array_t with results of an SQL query
* medias in ml_result_t
*
* @param p_ml the media library object
* @param p_array array to fill with ml_media_t elements (might be initialized)
* @param pp_results result of sql query
* @param i_rows row number
* @param i_cols column number
* @return VLC_SUCCESS or a VLC error code
* Warning: this returns VLC_EGENERIC if i_rows == 0 (empty result)
**/
int
SQLToMediaArray
(
media_library_t
*
p_ml
,
vlc_array_t
*
p_result_array
,
char
**
pp_results
,
int
i_rows
,
int
i_cols
)
{
int
i_ret
=
VLC_SUCCESS
;
assert
(
p_ml
);
#define res( i, j ) ( pp_results[ i * i_cols + j ] )
#define atoinull( a ) ( (a) ? atoi( a ) : 0 )
#define strdupnull( a ) ( (a) ? strdup( a ) : NULL )
if
(
i_rows
==
0
)
return
VLC_EGENERIC
;
if
(
!
p_result_array
||
!
pp_results
||
i_rows
<
0
||
i_cols
<=
0
)
{
msg_Warn
(
p_ml
,
"bad arguments (%s:%d)"
,
__FILE__
,
__LINE__
);
return
VLC_EGENERIC
;
}
vlc_array_t
*
p_intermediate_array
=
vlc_array_new
();
/* Analyze first row */
int
*
indexes
=
(
int
*
)
calloc
(
i_cols
+
1
,
sizeof
(
int
)
);
if
(
!
indexes
)
{
vlc_array_destroy
(
p_intermediate_array
);
return
VLC_ENOMEM
;
}
const
int
count
=
sizeof
(
ml_table_map
)
/
sizeof
(
struct
ml_table_elt
);
for
(
int
col
=
0
;
col
<
i_cols
;
col
++
)
{
struct
ml_table_elt
key
,
*
result
=
NULL
;
key
.
column_name
=
res
(
0
,
col
);
result
=
bsearch
(
&
key
,
ml_table_map
,
count
,
sizeof
(
struct
ml_table_elt
),
compare_ml_elts
);
if
(
!
result
)
msg_Warn
(
p_ml
,
"unknown column: %s"
,
res
(
0
,
col
)
);
else
indexes
[
col
]
=
result
->
column_id
;
}
/* Read rows 1 to i_rows */
ml_media_t
*
p_media
=
NULL
;
ml_result_t
*
p_result
=
NULL
;
for
(
int
row
=
1
;
(
row
<=
i_rows
)
&&
(
i_ret
==
VLC_SUCCESS
);
row
++
)
{
p_media
=
media_New
(
p_ml
,
0
,
ML_MEDIA
,
false
);
if
(
!
p_media
)
{
free
(
indexes
);
i_ret
=
VLC_ENOMEM
;
goto
quit_sqlmediaarray
;
}
p_result
=
(
ml_result_t
*
)
calloc
(
1
,
sizeof
(
ml_result_t
)
);
if
(
!
p_result
)
{
ml_gc_decref
(
p_media
);
free
(
indexes
);
i_ret
=
VLC_ENOMEM
;
goto
quit_sqlmediaarray
;
}
char
*
psz_append_pname
=
NULL
;
char
*
psz_append_prole
=
NULL
;
int
i_append_pid
=
0
;
#define SWITCH_INT( key, value ) case key: \
p_media-> value = atoinull( res( row, col ) );
#define SWITCH_PSZ( key, value ) case key: \
p_media-> value = strdupnull( res( row, col ) );
ml_LockMedia
(
p_media
);
for
(
int
col
=
0
;
(
col
<
i_cols
)
&&
(
i_ret
==
VLC_SUCCESS
);
col
++
)
{
switch
(
indexes
[
col
]
)
{
SWITCH_INT
(
ML_ALBUM_ID
,
i_album_id
);
SWITCH_PSZ
(
ML_ALBUM
,
psz_album
);
SWITCH_PSZ
(
ML_COMMENT
,
psz_comment
);
SWITCH_INT
(
ML_DISC_NUMBER
,
i_disc_number
);
SWITCH_INT
(
ML_DURATION
,
i_duration
);
SWITCH_PSZ
(
ML_EXTRA
,
psz_extra
);
SWITCH_INT
(
ML_FILESIZE
,
i_filesize
);
SWITCH_INT
(
ML_FIRST_PLAYED
,
i_first_played
);
SWITCH_PSZ
(
ML_GENRE
,
psz_genre
);
SWITCH_INT
(
ML_IMPORT_TIME
,
i_import_time
);
SWITCH_PSZ
(
ML_LANGUAGE
,
psz_language
);
SWITCH_INT
(
ML_LAST_PLAYED
,
i_last_played
);
SWITCH_INT
(
ML_LAST_SKIPPED
,
i_last_skipped
);
SWITCH_PSZ
(
ML_ORIGINAL_TITLE
,
psz_orig_title
);
SWITCH_INT
(
ML_PLAYED_COUNT
,
i_played_count
);
SWITCH_PSZ
(
ML_PREVIEW
,
psz_preview
);
SWITCH_INT
(
ML_SCORE
,
i_score
);
SWITCH_INT
(
ML_SKIPPED_COUNT
,
i_skipped_count
);
SWITCH_PSZ
(
ML_TITLE
,
psz_title
);
SWITCH_INT
(
ML_TRACK_NUMBER
,
i_track_number
);
SWITCH_INT
(
ML_TYPE
,
i_type
);
SWITCH_INT
(
ML_VOTE
,
i_vote
);
SWITCH_INT
(
ML_YEAR
,
i_year
);
case
ML_ALBUM_COVER
:
/* See ML_COVER */
// Discard attachment://
if
(
!
p_media
->
psz_cover
||
!*
p_media
->
psz_cover
||
!
strncmp
(
p_media
->
psz_cover
,
"attachment://"
,
13
)
)
{
free
(
p_media
->
psz_cover
);
p_media
->
psz_cover
=
strdupnull
(
res
(
row
,
col
)
);
}
break
;
case
ML_PEOPLE
:
psz_append_pname
=
strdupnull
(
res
(
row
,
col
)
);
break
;
case
ML_PEOPLE_ID
:
i_append_pid
=
atoinull
(
res
(
row
,
col
)
);
break
;
case
ML_PEOPLE_ROLE
:
psz_append_prole
=
strdupnull
(
res
(
row
,
col
)
);
break
;
case
ML_COVER
:
/* See ML_ALBUM_COVER */
if
(
!
p_media
->
psz_cover
||
!*
p_media
->
psz_cover
||
!
strncmp
(
p_media
->
psz_cover
,
"attachment://"
,
13
)
)
{
free
(
p_media
->
psz_cover
);
p_media
->
psz_cover
=
strdupnull
(
res
(
row
,
col
)
);
}
break
;
case
ML_ID
:
p_media
->
i_id
=
atoinull
(
res
(
row
,
col
)
);
if
(
p_media
->
i_id
<=
0
)
msg_Warn
(
p_ml
,
"entry with id null or inferior to zero"
);
break
;
case
ML_URI
:
p_media
->
psz_uri
=
strdupnull
(
res
(
row
,
col
)
);
if
(
!
p_media
->
psz_uri
)
msg_Warn
(
p_ml
,
"entry without uri"
);
break
;
case
ML_DIRECTORY
:
break
;
// The column directory_id is'nt part of the media model
default:
msg_Warn
(
p_ml
,
"unknown element, row %d column %d (of %d) - %s - %s"
,
row
,
col
,
i_cols
,
res
(
0
,
col
),
res
(
row
,
col
)
);
break
;
}
}
#undef SWITCH_INT
#undef SWITCH_PSZ
int
i_appendrow
;
ml_result_t
*
p_append
=
NULL
;
for
(
i_appendrow
=
0
;
i_appendrow
<
vlc_array_count
(
p_intermediate_array
);
i_appendrow
++
)
{
p_append
=
(
ml_result_t
*
)
vlc_array_item_at_index
(
p_intermediate_array
,
i_appendrow
);
if
(
p_append
->
id
==
p_media
->
i_id
)
break
;
}
if
(
i_appendrow
==
vlc_array_count
(
p_intermediate_array
)
)
{
p_result
->
id
=
p_media
->
i_id
;
p_result
->
type
=
ML_TYPE_MEDIA
;
p_result
->
value
.
p_media
=
p_media
;
if
(
psz_append_pname
&&
i_append_pid
&&
psz_append_prole
)
ml_CreateAppendPersonAdv
(
&
(
p_result
->
value
.
p_media
->
p_people
),
psz_append_prole
,
psz_append_pname
,
i_append_pid
);
vlc_array_append
(
p_intermediate_array
,
p_result
);
ml_UnlockMedia
(
p_media
);
}
else
/* This is a repeat row and the people need to be put together */
{
free
(
p_result
);
ml_LockMedia
(
p_append
->
value
.
p_media
);
if
(
psz_append_pname
&&
i_append_pid
&&
psz_append_prole
)
ml_CreateAppendPersonAdv
(
&
(
p_append
->
value
.
p_media
->
p_people
),
psz_append_prole
,
psz_append_pname
,
i_append_pid
);
ml_UnlockMedia
(
p_append
->
value
.
p_media
);
ml_UnlockMedia
(
p_media
);
ml_gc_decref
(
p_media
);
}
FREENULL
(
psz_append_prole
);
FREENULL
(
psz_append_pname
);
i_append_pid
=
0
;
}
p_media
=
NULL
;
free
(
indexes
);
/* Now check if these medias are already on the pool, and sync */
for
(
int
i
=
0
;
i
<
vlc_array_count
(
p_intermediate_array
);
i
++
)
{
p_result
=
(
ml_result_t
*
)
vlc_array_item_at_index
(
p_intermediate_array
,
i
);
p_media
=
p_result
->
value
.
p_media
;
ml_media_t
*
p_poolmedia
=
pool_GetMedia
(
p_ml
,
p_result
->
id
);
/* TODO: Pool_syncMedia might be cleaner? */
p_result
=
(
ml_result_t
*
)
calloc
(
1
,
sizeof
(
ml_result_t
*
)
);
if
(
!
p_result
)
goto
quit_sqlmediaarray
;
if
(
p_poolmedia
)
{
/* TODO: This might cause some weird stuff to occur w/ GC? */
ml_CopyMedia
(
p_poolmedia
,
p_media
);
p_result
->
id
=
p_poolmedia
->
i_id
;
p_result
->
type
=
ML_TYPE_MEDIA
;
p_result
->
value
.
p_media
=
p_poolmedia
;
vlc_array_append
(
p_result_array
,
p_result
);
}
else
{
i_ret
=
pool_InsertMedia
(
p_ml
,
p_media
,
false
);
if
(
i_ret
==
VLC_SUCCESS
)
{
ml_gc_incref
(
p_media
);
p_result
->
id
=
p_media
->
i_id
;
p_result
->
type
=
ML_TYPE_MEDIA
;
p_result
->
value
.
p_media
=
p_media
;
vlc_array_append
(
p_result_array
,
p_result
);
}
}
}
#undef strdupnull
#undef atoinull
#undef res
quit_sqlmediaarray:
for
(
int
k
=
0
;
k
<
vlc_array_count
(
p_intermediate_array
);
k
++
)
{
ml_result_t
*
temp
=
((
ml_result_t
*
)
vlc_array_item_at_index
(
p_intermediate_array
,
k
));
ml_FreeResult
(
temp
);
}
vlc_array_destroy
(
p_intermediate_array
);
return
i_ret
;
}
/**
* @brief Returns (unique) ID of media with specified URI
*
* @param p_ml the media library object
* @param psz_uri URI to look for
* @return i_id: (first) ID found, VLC_EGENERIC in case of error
* NOTE: Normally, there should not be more than one ID with one URI
*/
int
GetMediaIdOfURI
(
media_library_t
*
p_ml
,
const
char
*
psz_uri
)
{
int
i_ret
=
VLC_EGENERIC
;
vlc_array_t
*
p_array
=
vlc_array_new
();
i_ret
=
Find
(
p_ml
,
p_array
,
ML_ID
,
ML_URI
,
psz_uri
,
ML_LIMIT
,
1
,
ML_END
);
if
(
(
i_ret
==
VLC_SUCCESS
)
&&
(
vlc_array_count
(
p_array
)
>
0
)
&&
vlc_array_item_at_index
(
p_array
,
0
)
)
{
i_ret
=
(
(
ml_result_t
*
)
vlc_array_item_at_index
(
p_array
,
0
)
)
->
value
.
i
;
}
else
{
i_ret
=
VLC_EGENERIC
;
}
vlc_array_destroy
(
p_array
);
return
i_ret
;
}
/**
* @brief Control function for media library
*
* @param p_ml Media library handle
* @param i_query query type
* @param args query arguments
* @return VLC_SUCCESS if ok
*/
int
Control
(
media_library_t
*
p_ml
,
int
i_query
,
va_list
args
)
{
switch
(
i_query
)
{
case
ML_ADD_INPUT_ITEM
:
{
input_item_t
*
p_item
=
(
input_item_t
*
)
va_arg
(
args
,
input_item_t
*
);
return
AddInputItem
(
p_ml
,
p_item
);
}
case
ML_ADD_PLAYLIST_ITEM
:
{
playlist_item_t
*
p_item
=
(
playlist_item_t
*
)
va_arg
(
args
,
playlist_item_t
*
);
return
AddPlaylistItem
(
p_ml
,
p_item
);
}
case
ML_ADD_MONITORED
:
{
char
*
psz_dir
=
(
char
*
)
va_arg
(
args
,
char
*
);
return
AddDirToMonitor
(
p_ml
,
psz_dir
);
}
case
ML_GET_MONITORED
:
{
vlc_array_t
*
p_array
=
(
vlc_array_t
*
)
va_arg
(
args
,
vlc_array_t
*
);
return
ListMonitoredDirs
(
p_ml
,
p_array
);
}
case
ML_DEL_MONITORED
:
{
char
*
psz_dir
=
(
char
*
)
va_arg
(
args
,
char
*
);
return
RemoveDirToMonitor
(
p_ml
,
psz_dir
);
}
default:
return
VLC_EGENERIC
;
}
}
/**
* @brief Create a new (empty) database. The database might be initialized
*
* @param p_ml This ML
* @return VLC_SUCCESS or VLC_EGENERIC
* @note This function is transactional
*/
int
CreateEmptyDatabase
(
media_library_t
*
p_ml
)
{
assert
(
p_ml
);
int
i_ret
=
VLC_SUCCESS
;
msg_Dbg
(
p_ml
,
"creating a new (empty) database"
);
Begin
(
p_ml
);
/* Albums */
i_ret
=
QuerySimple
(
p_ml
,
"CREATE TABLE album ( "
"id INTEGER PRIMARY KEY,"
"album_artist_id INTEGER,"
"title VARCHAR(1024),"
"cover VARCHAR(1024) )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
i_ret
=
QuerySimple
(
p_ml
,
"CREATE INDEX album_title_index ON album (title);"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Add "unknown" entry to albums */
i_ret
=
QuerySimple
(
p_ml
,
"INSERT INTO album ( id, title, cover, album_artist_id ) "
"VALUES ( 0, 'Unknown', '', 0 )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Main media table */
i_ret
=
QuerySimple
(
p_ml
,
"CREATE TABLE media ( "
"id INTEGER PRIMARY KEY,"
"timestamp INTEGER,"
/* File timestamp */
"uri VARCHAR(1024),"
"type INTEGER,"
"title VARCHAR(1024),"
"original_title VARCHAR(1024),"
"album_id INTEGER,"
"cover VARCHAR(1024),"
"preview VARCHAR(1024),"
/* Video preview */
"track INTEGER,"
/* Track number */
"disc INTEGER,"
/* Disc number */
"year INTEGER,"
"genre VARCHAR(1024),"
"vote INTEGER,"
/* Rating/Stars */
"score INTEGER,"
/* ML score/rating */
"comment VARCHAR(1024),"
/* Comment */
"filesize INTEGER,"
/* Dates and times */
"duration INTEGER,"
/* Length of media */
"played_count INTEGER,"
"last_played DATE,"
"first_played DATE,"
"import_time DATE,"
"skipped_count INTEGER,"
"last_skipped DATE,"
"directory_id INTEGER,"
"CONSTRAINT associated_album FOREIGN KEY(album_id) "
"REFERENCES album(id) ON DELETE SET DEFAULT ON UPDATE RESTRICT)"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
i_ret
=
QuerySimple
(
p_ml
,
"CREATE INDEX media_ui_index ON media (uri);"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* People */
i_ret
=
QuerySimple
(
p_ml
,
"CREATE TABLE people ( "
"id INTEGER PRIMARY KEY,"
"name VARCHAR(1024) ,"
"role VARCHAR(1024) )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Media to people */
i_ret
=
QuerySimple
(
p_ml
,
"CREATE TABLE media_to_people ( "
"media_id INTEGER, "
"people_id INTEGER, "
"PRIMARY KEY( media_id, people_id ), "
"CONSTRAINT associated_people FOREIGN KEY(people_id) "
"REFERENCES people(id) ON DELETE SET DEFAULT ON UPDATE RESTRICT, "
"CONSTRAINT associated_media FOREIGN KEY(media_id) "
"REFERENCES media(id) ON DELETE CASCADE ON UPDATE RESTRICT )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Add "unknown" entry to people */
i_ret
=
QuerySimple
(
p_ml
,
"INSERT INTO people ( id, name, role ) "
"VALUES ( 0, 'Unknown', NULL )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* recursive is set to 1 if the directory is added to the database
by recursion and 0 if not */
i_ret
=
QuerySimple
(
p_ml
,
"CREATE TABLE directories ( "
"id INTEGER PRIMARY KEY,"
"uri VARCHAR(1024),"
"timestamp INTEGER,"
"recursive INTEGER )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Create information table
* This table should have one row and the version number is the version
* of the database
* Other information may be stored here at later stages */
i_ret
=
QuerySimple
(
p_ml
,
"CREATE TABLE information ( "
"version INTEGER PRIMARY KEY )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Insert current DB version */
i_ret
=
QuerySimple
(
p_ml
,
"INSERT INTO information ( version ) "
"VALUES ( %d )"
,
ML_DBVERSION
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Text data: song lyrics or subtitles */
i_ret
=
QuerySimple
(
p_ml
,
"CREATE TABLE extra ( "
"id INTEGER PRIMARY KEY,"
"extra TEXT,"
"language VARCHAR(256),"
"bitrate INTEGER,"
"samplerate INTEGER,"
"bpm INTEGER )"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
/* Emulating foreign keys with triggers */
/* Warning: Lots of SQL */
if
(
!
strcmp
(
module_get_name
(
p_ml
->
p_sys
->
p_sql
->
p_module
,
false
),
"SQLite"
)
)
{
i_ret
=
QuerySimple
(
p_ml
,
"
\n
CREATE TRIGGER genfkey1_insert_referencing BEFORE INSERT ON
\"
media
\"
WHEN
\n
"
" new.
\"
album_id
\"
IS NOT NULL AND NOT EXISTS (SELECT 1 FROM
\"
album
\"
WHERE new.
\"
album_id
\"
==
\"
id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey1_insert_referencing failed. Cannot insert album_id into media. Album did not exist');
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey1_update_referencing BEFORE
\n
"
" UPDATE OF album_id ON
\"
media
\"
WHEN
\n
"
" new.
\"
album_id
\"
IS NOT NULL AND
\n
"
" NOT EXISTS (SELECT 1 FROM
\"
album
\"
WHERE new.
\"
album_id
\"
==
\"
id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey1_update_referencing failed. Cannot update album_id in media. Album did not exist');
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey1_delete_referenced BEFORE DELETE ON
\"
album
\"
WHEN
\n
"
" EXISTS (SELECT 1 FROM
\"
media
\"
WHERE old.
\"
id
\"
==
\"
album_id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey1_delete_referenced failed. Cannot delete album, media still exist');
\n
"
"END;
\n
"
"
\n
"
"
\n
"
"CREATE TRIGGER genfkey1_update_referenced AFTER
\n
"
" UPDATE OF id ON
\"
album
\"
WHEN
\n
"
" EXISTS (SELECT 1 FROM
\"
media
\"
WHERE old.
\"
id
\"
==
\"
album_id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey1_update_referenced failed. Cannot change album id in album, media still exist');
\n
"
"END;
\n
"
"
\n
"
"
\n
"
"CREATE TRIGGER genfkey2_insert_referencing BEFORE INSERT ON
\"
media_to_people
\"
WHEN
\n
"
" new.
\"
media_id
\"
IS NOT NULL AND NOT EXISTS (SELECT 1 FROM
\"
media
\"
WHERE new.
\"
media_id
\"
==
\"
id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey2_insert_referencing failed. Cannot insert into media_to_people, that media does not exist');
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey2_update_referencing BEFORE
\n
"
" UPDATE OF media_id ON
\"
media_to_people
\"
WHEN
\n
"
" new.
\"
media_id
\"
IS NOT NULL AND
\n
"
" NOT EXISTS (SELECT 1 FROM
\"
media
\"
WHERE new.
\"
media_id
\"
==
\"
id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey2_update_referencing failed. Cannot update media_to_people, that media does not exist');
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey2_delete_referenced BEFORE DELETE ON
\"
media
\"
WHEN
\n
"
" EXISTS (SELECT 1 FROM
\"
media_to_people
\"
WHERE old.
\"
id
\"
==
\"
media_id
\"
)
\n
"
"BEGIN
\n
"
" DELETE FROM
\"
media_to_people
\"
WHERE
\"
media_id
\"
= old.
\"
id
\"
;
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey2_update_referenced AFTER
\n
"
" UPDATE OF id ON
\"
media
\"
WHEN
\n
"
" EXISTS (SELECT 1 FROM
\"
media_to_people
\"
WHERE old.
\"
id
\"
==
\"
media_id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey2_update_referenced failed. Cannot update media id, refs still exist in media_to_people');
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey3_insert_referencing BEFORE INSERT ON
\"
media_to_people
\"
WHEN
\n
"
" new.
\"
people_id
\"
IS NOT NULL AND NOT EXISTS (SELECT 1 FROM
\"
people
\"
WHERE new.
\"
people_id
\"
==
\"
id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey3_insert_referencing failed. Cannot insert into media_to_people, people does not exist');
\n
"
"END;
\n
"
"CREATE TRIGGER genfkey3_update_referencing BEFORE
\n
"
" UPDATE OF people_id ON
\"
media_to_people
\"
WHEN
\n
"
" new.
\"
people_id
\"
IS NOT NULL AND
\n
"
" NOT EXISTS (SELECT 1 FROM
\"
people
\"
WHERE new.
\"
people_id
\"
==
\"
id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey3_update_referencing failed. Cannot update media_to_people, people does not exist');
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey3_delete_referenced BEFORE DELETE ON
\"
people
\"
WHEN
\n
"
" EXISTS (SELECT 1 FROM
\"
media_to_people
\"
WHERE old.
\"
id
\"
==
\"
people_id
\"
)
\n
"
"BEGIN
\n
"
" UPDATE media_to_people SET people_id = 0 WHERE people_id == old.
\"
id
\"
;
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER genfkey3_update_referenced AFTER
\n
"
" UPDATE OF id ON
\"
people
\"
WHEN
\n
"
" EXISTS (SELECT 1 FROM
\"
media_to_people
\"
WHERE old.
\"
id
\"
==
\"
people_id
\"
)
\n
"
"BEGIN
\n
"
" SELECT RAISE(ABORT, 'constraint genfkey3_update_referenced failed. Cannot update people_id, people does not exist');
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER keep_people_clean AFTER
\n
"
" DELETE ON
\"
media_to_people
\"\n
"
" WHEN NOT EXISTS( SELECT 1 from
\"
media_to_people
\"
WHERE old.
\"
people_id
\"
==
\"
people_id
\"
)
\n
"
"BEGIN
\n
"
" DELETE FROM people WHERE people.id = old.
\"
people_id
\"
AND people.id != 0;
\n
"
"END;
\n
"
"
\n
"
"CREATE TRIGGER keep_album_clean AFTER
\n
"
" DELETE ON
\"
media
\"\n
"
" WHEN NOT EXISTS( SELECT 1 FROM
\"
media
\"
WHERE old.
\"
album_id
\"
==
\"
album_id
\"
)
\n
"
"BEGIN
\n
"
" DELETE FROM album WHERE album.id = old.
\"
album_id
\"
AND album.id != 0;
\n
"
"END;"
);
if
(
i_ret
!=
VLC_SUCCESS
)
goto
quit_createemptydatabase
;
}
quit_createemptydatabase:
if
(
i_ret
==
VLC_SUCCESS
)
Commit
(
p_ml
);
else
Rollback
(
p_ml
);
return
VLC_SUCCESS
;
}
/**
* @brief Journal and synchronous disc and writes
*
* @param p_ml media library object
* @param b_sync boolean
* @return <= 0 on error.
*/
static
int
SetSynchronous
(
media_library_t
*
p_ml
,
bool
b_sync
)
{
int
i_rows
,
i_cols
;
char
**
pp_results
;
int
i_return
;
if
(
b_sync
)
i_return
=
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"PRAGMA synchronous = ON;PRAGMA journal_mode = TRUNCATE"
);
else
i_return
=
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"PRAGMA synchronous = OFF;PRAGMA journal_mode = MEMORY"
);
if
(
i_return
!=
VLC_SUCCESS
)
i_return
=
-
1
;
else
i_return
=
atoi
(
pp_results
[
1
]
);
FreeSQLResult
(
p_ml
,
pp_results
);
return
i_return
;
}
/**
* @brief Initiates database (create the database and the tables if needed)
*
* @param p_ml This ML
* @return VLC_SUCCESS or an error code
*/
int
InitDatabase
(
media_library_t
*
p_ml
)
{
assert
(
p_ml
);
msg_Dbg
(
p_ml
,
"initializing database"
);
/* Select database name */
char
*
psz_dbhost
=
NULL
,
*
psz_user
=
NULL
,
*
psz_pass
=
NULL
;
int
i_port
=
0
;
bool
b_sync
=
false
;
psz_dbhost
=
config_GetPsz
(
p_ml
,
"ml-filename"
);
psz_user
=
config_GetPsz
(
p_ml
,
"ml-username"
);
psz_pass
=
config_GetPsz
(
p_ml
,
"ml-password"
);
i_port
=
config_GetInt
(
p_ml
,
"ml-port"
);
b_sync
=
config_GetInt
(
p_ml
,
"ml-synchronous"
);
/* Let's consider that a filename with a DIR_SEP is a full URL */
if
(
strchr
(
psz_dbhost
,
DIR_SEP_CHAR
)
==
NULL
)
{
char
*
psz_datadir
=
config_GetUserDir
(
VLC_DATA_DIR
);
char
*
psz_tmp
=
psz_dbhost
;
if
(
asprintf
(
&
psz_dbhost
,
"%s"
DIR_SEP
"%s"
,
psz_datadir
,
psz_tmp
)
==
-
1
)
{
free
(
psz_datadir
);
free
(
psz_tmp
);
return
VLC_ENOMEM
;
}
free
(
psz_datadir
);
free
(
psz_tmp
);
}
p_ml
->
p_sys
->
p_sql
=
sql_Create
(
p_ml
,
NULL
,
psz_dbhost
,
i_port
,
psz_user
,
psz_pass
);
if
(
!
p_ml
->
p_sys
->
p_sql
)
return
VLC_EGENERIC
;
/* Let's check if tables exist */
int
i_version
=
GetDatabaseVersion
(
p_ml
);
if
(
i_version
<=
0
)
CreateEmptyDatabase
(
p_ml
);
else
if
(
i_version
!=
ML_DBVERSION
)
return
VLC_EGENERIC
;
/**
* The below code ensures that correct code is written
* when database versions are changed
*/
#if ML_DBVERSION != 1
#error "ML versioning code needs to be updated. Is this done correctly?"
#endif
SetSynchronous
(
p_ml
,
b_sync
);
msg_Dbg
(
p_ml
,
"ML initialized"
);
return
VLC_SUCCESS
;
}
/**
* @brief Gets the current version number from the database
*
* @param p_ml media library object
* @return version number of the current db. <= 0 on error.
*/
int
GetDatabaseVersion
(
media_library_t
*
p_ml
)
{
int
i_rows
,
i_cols
;
char
**
pp_results
;
int
i_return
;
i_return
=
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT version FROM information ORDER BY version DESC LIMIT 1"
);
if
(
i_return
!=
VLC_SUCCESS
)
i_return
=
-
1
;
else
i_return
=
atoi
(
pp_results
[
1
]
);
FreeSQLResult
(
p_ml
,
pp_results
);
return
i_return
;
}
/**
* @brief Object constructor for ml_media_t
* @param p_ml The media library object
* @param id If 0, this item isn't in database. If non zero, it is and
* it will be a singleton
* @param select Type of object
* @param reload Whether to reload from database
*/
ml_media_t
*
GetMedia
(
media_library_t
*
p_ml
,
int
id
,
ml_select_e
select
,
bool
reload
)
{
assert
(
id
>
0
);
assert
(
select
==
ML_MEDIA
||
select
==
ML_MEDIA_SPARSE
);
int
i_ret
=
VLC_SUCCESS
;
ml_media_t
*
p_media
=
NULL
;
if
(
!
reload
)
{
p_media
=
pool_GetMedia
(
p_ml
,
id
);
if
(
!
p_media
)
reload
=
true
;
else
{
ml_LockMedia
(
p_media
);
if
(
p_media
->
b_sparse
&&
select
==
ML_MEDIA
)
reload
=
true
;
/* Utilise ML_MEDIA_EXTRA load? TODO */
ml_UnlockMedia
(
p_media
);
ml_gc_incref
(
p_media
);
}
}
else
{
vlc_array_t
*
p_array
=
vlc_array_new
();
i_ret
=
ml_Find
(
p_ml
,
p_array
,
select
,
ML_ID
,
id
);
assert
(
vlc_array_count
(
p_array
)
==
1
);
if
(
(
i_ret
==
VLC_SUCCESS
)
&&
(
vlc_array_count
(
p_array
)
>
0
)
&&
vlc_array_item_at_index
(
p_array
,
0
)
)
{
p_media
=
((
ml_result_t
*
)
vlc_array_item_at_index
(
p_array
,
0
))
->
value
.
p_media
;
ml_gc_incref
(
p_media
);
ml_FreeResult
(
vlc_array_item_at_index
(
p_array
,
0
)
);
}
vlc_array_destroy
(
p_array
);
if
(
select
==
ML_MEDIA
)
p_media
->
b_sparse
=
false
;
else
p_media
->
b_sparse
=
true
;
}
return
p_media
;
}
/**
* @brief Create an input item from media (given its ID)
*
* @param p_ml This media_library_t object
* @param i_media Media ID
* @return input_item_t* created
*
* @note This is a public function (pf_InputItemFromMedia)
* The input_item will have a refcount at 2 (1 for the ML, 1 for you)
*/
input_item_t
*
GetInputItemFromMedia
(
media_library_t
*
p_ml
,
int
i_media
)
{
input_item_t
*
p_item
=
NULL
;
p_item
=
watch_get_itemOfMediaId
(
p_ml
,
i_media
);
if
(
!
p_item
)
{
ml_media_t
*
p_media
=
media_New
(
p_ml
,
i_media
,
ML_MEDIA
,
true
);
if
(
p_media
==
NULL
)
return
NULL
;
CreateInputItemFromMedia
(
&
p_item
,
p_media
);
watch_add_Item
(
p_ml
,
p_item
,
p_media
);
ml_gc_decref
(
p_media
);
}
return
p_item
;
}
/**
* @brief Copy an input_item_t to a ml_media_t
* @param p_media Destination
* @param p_item Source
* @note Media ID will not be set! This function is threadsafe. Leaves
* unsyncable items alone
*/
void
CopyInputItemToMedia
(
ml_media_t
*
p_media
,
input_item_t
*
p_item
)
{
ml_LockMedia
(
p_media
);
#if 0
// unused meta :
input_item_GetCopyright( item )
input_item_GetRating( item ) /* TODO */
input_item_GetGetting( item )
input_item_GetNowPlaying( item )
input_item_GetTrackID( item )
input_item_GetSetting( item )
#endif
p_media
->
psz_title
=
input_item_GetTitle
(
p_item
);
p_media
->
psz_uri
=
input_item_GetURL
(
p_item
);
if
(
!
p_media
->
psz_uri
)
p_media
->
psz_uri
=
input_item_GetURI
(
p_item
);
p_media
->
psz_album
=
input_item_GetAlbum
(
p_item
);
p_media
->
psz_cover
=
input_item_GetArtURL
(
p_item
);
p_media
->
psz_genre
=
input_item_GetGenre
(
p_item
);
p_media
->
psz_language
=
input_item_GetLanguage
(
p_item
);
p_media
->
psz_comment
=
input_item_GetDescription
(
p_item
);
char
*
psz_track
=
input_item_GetTrackNum
(
p_item
);
p_media
->
i_track_number
=
psz_track
?
atoi
(
psz_track
)
:
0
;
free
(
psz_track
);
char
*
psz_date
=
input_item_GetDate
(
p_item
);
p_media
->
i_year
=
psz_date
?
atoi
(
psz_date
)
:
0
;
free
(
psz_date
);
p_media
->
i_duration
=
p_item
->
i_duration
;
/* People */
char
*
psz_tmp
=
input_item_GetArtist
(
p_item
);
if
(
psz_tmp
)
ml_CreateAppendPersonAdv
(
&
p_media
->
p_people
,
ML_PERSON_ARTIST
,
psz_tmp
,
0
);
free
(
psz_tmp
);
psz_tmp
=
input_item_GetPublisher
(
p_item
);
if
(
psz_tmp
)
ml_CreateAppendPersonAdv
(
&
p_media
->
p_people
,
ML_PERSON_PUBLISHER
,
psz_tmp
,
0
);
free
(
psz_tmp
);
psz_tmp
=
input_item_GetEncodedBy
(
p_item
);
if
(
psz_tmp
)
ml_CreateAppendPersonAdv
(
&
p_media
->
p_people
,
ML_PERSON_ENCODER
,
psz_tmp
,
0
);
free
(
psz_tmp
);
/* Determine input type: audio, video, stream */
/* First read input type */
switch
(
p_item
->
i_type
)
{
case
ITEM_TYPE_FILE
:
p_media
->
i_type
|=
0
;
break
;
case
ITEM_TYPE_DISC
:
case
ITEM_TYPE_CARD
:
p_media
->
i_type
|=
ML_REMOVABLE
;
break
;
case
ITEM_TYPE_CDDA
:
case
ITEM_TYPE_NET
:
p_media
->
i_type
|=
ML_STREAM
;
break
;
case
ITEM_TYPE_PLAYLIST
:
case
ITEM_TYPE_NODE
:
case
ITEM_TYPE_DIRECTORY
:
p_media
->
i_type
|=
ML_NODE
;
break
;
case
ITEM_TYPE_NUMBER
:
case
ITEM_TYPE_UNKNOWN
:
default:
p_media
->
i_type
|=
ML_UNKNOWN
;
break
;
}
/* Then try to guess if this is a video or not */
/* Check file extension, and guess if this is a video or an audio media
Note: this test is not very good, but it's OK for normal files */
char
*
psz_ext
=
strrchr
(
p_item
->
psz_uri
,
'.'
);
if
(
psz_ext
&&
strlen
(
psz_ext
)
<
5
)
{
bool
b_ok
=
false
;
psz_ext
++
;
for
(
unsigned
i
=
0
;
ppsz_AudioExtensions
[
i
];
i
++
)
{
if
(
strcasecmp
(
psz_ext
,
ppsz_AudioExtensions
[
i
]
)
==
0
)
{
p_media
->
i_type
|=
ML_AUDIO
;
b_ok
=
true
;
break
;
}
}
if
(
!
b_ok
)
{
for
(
unsigned
i
=
0
;
ppsz_VideoExtensions
[
i
];
i
++
)
{
if
(
strcasecmp
(
psz_ext
,
ppsz_VideoExtensions
[
i
]
)
==
0
)
{
p_media
->
i_type
|=
ML_VIDEO
;
break
;
}
}
}
}
ml_UnlockMedia
(
p_media
);
}
/**
* @brief Copy a ml_media_t to an input_item_t
* @param p_item Destination
* @param p_media Source
*/
void
CopyMediaToInputItem
(
input_item_t
*
p_item
,
ml_media_t
*
p_media
)
{
ml_LockMedia
(
p_media
);
if
(
p_media
->
psz_title
&&
*
p_media
->
psz_title
)
input_item_SetTitle
(
p_item
,
p_media
->
psz_title
);
if
(
p_media
->
psz_uri
&&
*
p_media
->
psz_uri
&&
!
strncmp
(
p_media
->
psz_uri
,
"http"
,
4
)
)
input_item_SetURL
(
p_item
,
p_media
->
psz_uri
);
if
(
p_media
->
psz_album
&&
*
p_media
->
psz_album
)
input_item_SetAlbum
(
p_item
,
p_media
->
psz_album
);
if
(
p_media
->
psz_cover
&&
*
p_media
->
psz_cover
)
input_item_SetArtURL
(
p_item
,
p_media
->
psz_cover
);
if
(
p_media
->
psz_genre
&&
*
p_media
->
psz_genre
)
input_item_SetGenre
(
p_item
,
p_media
->
psz_genre
);
if
(
p_media
->
psz_language
&&
*
p_media
->
psz_language
)
input_item_SetLanguage
(
p_item
,
p_media
->
psz_language
);
if
(
p_media
->
psz_comment
&&
*
p_media
->
psz_comment
)
input_item_SetDescription
(
p_item
,
p_media
->
psz_comment
);
if
(
p_media
->
i_track_number
)
{
char
*
psz_track
;
if
(
asprintf
(
&
psz_track
,
"%d"
,
p_media
->
i_track_number
)
!=
-
1
)
input_item_SetTrackNum
(
p_item
,
psz_track
);
free
(
psz_track
);
}
if
(
p_media
->
i_year
)
{
char
*
psz_date
;
if
(
asprintf
(
&
psz_date
,
"%d"
,
p_media
->
i_year
)
!=
-
1
)
input_item_SetDate
(
p_item
,
psz_date
);
free
(
psz_date
);
}
p_item
->
i_duration
=
p_media
->
i_duration
;
ml_person_t
*
person
=
p_media
->
p_people
;
while
(
person
)
{
if
(
!
strcmp
(
person
->
psz_role
,
ML_PERSON_ARTIST
)
)
input_item_SetArtist
(
p_item
,
person
->
psz_name
);
else
if
(
!
strcmp
(
person
->
psz_role
,
ML_PERSON_PUBLISHER
)
)
input_item_SetPublisher
(
p_item
,
person
->
psz_name
);
else
if
(
!
strcmp
(
person
->
psz_role
,
ML_PERSON_ENCODER
)
)
input_item_SetEncodedBy
(
p_item
,
person
->
psz_name
);
person
=
person
->
p_next
;
}
ml_UnlockMedia
(
p_media
);
}
/**
* @brief Copy a ml_media_t to an input_item_t
* @param pp_item A pointer to a new input_item (return value)
* @param p_media The media to copy as an input item
* @note This function is threadsafe
*/
static
int
CreateInputItemFromMedia
(
input_item_t
**
pp_item
,
ml_media_t
*
p_media
)
{
*
pp_item
=
input_item_New
(
p_media
->
psz_uri
,
p_media
->
psz_title
);
/* ITEM_TYPE_FILE ); */
if
(
!*
pp_item
)
return
VLC_EGENERIC
;
CopyMediaToInputItem
(
*
pp_item
,
p_media
);
return
VLC_SUCCESS
;
}
/**
* @brief Find the media_id associated to an input item
* @param p_ml This
* @param p_item Input item to look for
* @return Media ID or <= 0 if not found
*/
int
GetMediaIdOfInputItem
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
)
{
int
i_media_id
=
watch_get_mediaIdOfItem
(
p_ml
,
p_item
);
if
(
i_media_id
<=
0
)
{
i_media_id
=
GetMediaIdOfURI
(
p_ml
,
p_item
->
psz_uri
);
}
return
i_media_id
;
}
modules/media_library/sql_media_library.h
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* sql_media_library.h : Media Library Interface
*****************************************************************************
* 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.
*****************************************************************************/
#ifndef SQL_MEDIA_LIBRARY_H
#define SQL_MEDIA_LIBRARY_H
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
#include <sys/stat.h>
#include <vlc_common.h>
#include <vlc_sql.h>
#include <vlc_media_library.h>
#include <vlc_playlist.h>
#include <vlc_input.h>
#include <vlc_arrays.h>
#include <vlc_charset.h>
#include <vlc_plugin.h>
#include <vlc_interface.h>
#include <vlc_modules.h>
#include "item_list.h"
/*****************************************************************************
* Static parameters
*****************************************************************************/
#define THREAD_SLEEP_DELAY 2
/* Time between two calls to item_list_loop */
#define MONITORING_DELAY 30
/* Media library updates interval */
#define ITEM_LOOP_UPDATE 1
/* An item is updated after 1 loop */
#define ITEM_LOOP_MAX_AGE 10
/* An item is deleted after 10 loops */
#define ML_DBVERSION 1
/* The current version of the database */
#define ML_MEDIAPOOL_HASH_LENGTH 100
/* The length of the media pool hash */
/*****************************************************************************
* Structures and types definitions
*****************************************************************************/
typedef
struct
monitoring_thread_t
monitoring_thread_t
;
typedef
struct
ml_poolobject_t
ml_poolobject_t
;
struct
ml_poolobject_t
{
ml_media_t
*
p_media
;
ml_poolobject_t
*
p_next
;
};
struct
media_library_sys_t
{
/* Lock on the ML object */
vlc_mutex_t
lock
;
/* SQL object */
sql_t
*
p_sql
;
/* Monitoring thread */
monitoring_thread_t
*
p_mon
;
/* Watch thread */
watch_thread_t
*
p_watch
;
/* Holds all medias */
DECL_ARRAY
(
ml_media_t
*
)
mediapool
;
ml_poolobject_t
*
p_mediapool
[
ML_MEDIAPOOL_HASH_LENGTH
];
vlc_mutex_t
pool_mutex
;
/* Info on update/collection rebuilding */
bool
b_updating
;
bool
b_rebuilding
;
};
/* Directory Monitoring thread */
struct
monitoring_thread_t
{
VLC_COMMON_MEMBERS
;
vlc_cond_t
wait
;
vlc_mutex_t
lock
;
vlc_thread_t
thread
;
media_library_t
*
p_ml
;
};
/* Media status Watching thread */
struct
watch_thread_t
{
media_library_t
*
p_ml
;
vlc_thread_t
thread
;
vlc_cond_t
cond
;
vlc_mutex_t
lock
;
/* Input items watched */
struct
item_list_t
*
p_hlist
[
ML_ITEMLIST_HASH_LENGTH
];
vlc_mutex_t
list_mutex
;
/* List of items to check */
input_item_t
**
item_append_queue
;
vlc_mutex_t
item_append_queue_lock
;
int
item_append_queue_count
;
};
/*****************************************************************************
* Function headers
*****************************************************************************/
/* General functions */
int
CreateEmptyDatabase
(
media_library_t
*
p_ml
);
int
InitDatabase
(
media_library_t
*
p_ml
);
/* Module Control */
int
Control
(
media_library_t
*
p_ml
,
int
i_query
,
va_list
args
);
/* Add functions */
int
AddMedia
(
media_library_t
*
p_ml
,
ml_media_t
*
p_media
);
int
AddAlbum
(
media_library_t
*
p_ml
,
const
char
*
psz_title
,
const
char
*
psz_cover
,
const
int
i_album_artist
);
int
AddPeople
(
media_library_t
*
p_ml
,
const
char
*
psz_name
,
const
char
*
psz_role
);
int
AddPlaylistItem
(
media_library_t
*
p_ml
,
playlist_item_t
*
p_playlist_item
);
int
AddInputItem
(
media_library_t
*
p_ml
,
input_item_t
*
p_input
);
/* Create and Copy functions */
ml_media_t
*
GetMedia
(
media_library_t
*
p_ml
,
int
id
,
ml_select_e
select
,
bool
reload
);
input_item_t
*
GetInputItemFromMedia
(
media_library_t
*
p_ml
,
int
i_media
);
void
CopyInputItemToMedia
(
ml_media_t
*
p_media
,
input_item_t
*
p_item
);
void
CopyMediaToInputItem
(
input_item_t
*
p_item
,
ml_media_t
*
p_media
);
/* Get functions */
int
GetDatabaseVersion
(
media_library_t
*
p_ml
);
int
GetMediaIdOfInputItem
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
);
int
GetMediaIdOfURI
(
media_library_t
*
p_ml
,
const
char
*
psz_uri
);
/* Search in the database */
int
BuildSelectVa
(
media_library_t
*
p_ml
,
char
**
ppsz_query
,
ml_result_type_e
*
p_result_type
,
va_list
criterias
);
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
);
int
Find
(
media_library_t
*
p_ml
,
vlc_array_t
*
results
,
...
);
int
FindVa
(
media_library_t
*
p_ml
,
vlc_array_t
*
results
,
va_list
criterias
);
int
FindAdv
(
media_library_t
*
p_ml
,
vlc_array_t
*
results
,
ml_select_e
selected_type
,
const
char
*
psz_lvalue
,
ml_ftree_t
*
tree
);
/* Update the database */
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
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
);
int
UpdateMedia
(
media_library_t
*
p_ml
,
ml_media_t
*
p_media
);
int
SetArtCover
(
media_library_t
*
p_ml
,
int
i_album_id
,
const
char
*
psz_cover
);
/* Delete medias in the database */
int
Delete
(
media_library_t
*
p_ml
,
vlc_array_t
*
p_array
);
/* Do some query on the database */
int
QuerySimple
(
media_library_t
*
p_ml
,
const
char
*
psz_fmt
,
...
);
int
Query
(
media_library_t
*
p_ml
,
char
***
ppp_res
,
int
*
pi_rows
,
int
*
pi_cols
,
const
char
*
psz_fmt
,
...
);
int
QueryVa
(
media_library_t
*
p_ml
,
char
***
ppp_res
,
int
*
pi_rows
,
int
*
pi_cols
,
const
char
*
psz_fmt
,
va_list
args
);
int
QuerySimpleVa
(
media_library_t
*
p_ml
,
const
char
*
psz_fmt
,
va_list
argp
);
/* Convert SQL results to ML results */
int
StringToResult
(
ml_result_t
*
res
,
const
char
*
psz
,
const
char
*
psz_id
,
ml_result_type_e
result_type
);
int
SQLToMediaArray
(
media_library_t
*
p_ml
,
vlc_array_t
*
p_result_array
,
char
**
pp_results
,
int
i_rows
,
int
i_cols
);
int
SQLToResultArray
(
media_library_t
*
p_ml
,
vlc_array_t
*
p_result_array
,
char
**
pp_results
,
int
i_rows
,
int
i_cols
,
ml_result_type_e
result_type
);
/* Database locking functions */
/**
* @brief Begin a transaction
* @param p_ml The Media Library object
* @return VLC_SUCCESS and VLC_EGENERIC
* @note This creates a SHARED lock in SQLITE. All queries made between
* a Begin and Commit/Rollback will be transactional.
*/
static
inline
int
Begin
(
media_library_t
*
p_ml
)
{
return
sql_BeginTransaction
(
p_ml
->
p_sys
->
p_sql
);
}
/**
* @brief Commits the transaction
* @param p_ml The Media Library object
*/
static
inline
void
Commit
(
media_library_t
*
p_ml
)
{
sql_CommitTransaction
(
p_ml
->
p_sys
->
p_sql
);
}
/**
* @brief Rollback the transaction
* @param p_ml The Media Library Object
*/
static
inline
void
Rollback
(
media_library_t
*
p_ml
)
{
sql_RollbackTransaction
(
p_ml
->
p_sys
->
p_sql
);
}
/****************************************************************************
* Scanning/monitoring functions
*****************************************************************************/
void
*
RunMonitoringThread
(
void
*
p_mon
);
int
AddDirToMonitor
(
media_library_t
*
p_ml
,
const
char
*
psz_dir
);
int
ListMonitoredDirs
(
media_library_t
*
p_ml
,
vlc_array_t
*
p_array
);
int
RemoveDirToMonitor
(
media_library_t
*
p_ml
,
const
char
*
psz_dir
);
/*****************************************************************************
* Media pool functions
*****************************************************************************/
ml_media_t
*
pool_GetMedia
(
media_library_t
*
p_ml
,
int
media_id
);
int
pool_InsertMedia
(
media_library_t
*
p_ml
,
ml_media_t
*
media
,
bool
locked
);
void
pool_GC
(
media_library_t
*
p_ml
);
/*****************************************************************************
* Items watching system
*****************************************************************************/
/* Watching thread */
#define watch_add_Item( a, b, c ) __watch_add_Item( a, b, c, false )
int
watch_Init
(
media_library_t
*
p_ml
);
void
watch_Close
(
media_library_t
*
p_ml
);
int
__watch_add_Item
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
,
ml_media_t
*
p_media
,
bool
locked
);
#define watch_del_Item( a, b ) __watch_del_Item( a, b, false )
int
__watch_del_Item
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
,
bool
locked
);
int
watch_del_MediaById
(
media_library_t
*
p_ml
,
int
i_media_id
);
input_item_t
*
watch_get_itemOfMediaId
(
media_library_t
*
p_ml
,
int
i_media_id
);
ml_media_t
*
watch_get_mediaOfMediaId
(
media_library_t
*
p_ml
,
int
i_media_id
);
int
watch_get_mediaIdOfItem
(
media_library_t
*
p_ml
,
input_item_t
*
p_item
);
void
watch_Force_Update
(
media_library_t
*
p_ml
);
/*****************************************************************************
* Free result of ml_Query
*****************************************************************************/
static
inline
void
FreeSQLResult
(
media_library_t
*
p_ml
,
char
**
ppsz_result
)
{
if
(
ppsz_result
)
{
sql_Free
(
p_ml
->
p_sys
->
p_sql
,
ppsz_result
);
}
}
#endif
/* SQL_MEDIA_LIBRARY_H */
modules/media_library/sql_monitor.c
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* sql_monitor.c: SQL-based media library: directory scanning and monitoring
*****************************************************************************
* 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.
*****************************************************************************/
/** **************************************************************************
* MONITORING AND DIRECTORY SCANNING FUNCTIONS
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "sql_media_library.h"
#include "vlc_playlist.h"
#include "vlc_url.h"
#include "vlc_fs.h"
static
const
char
*
ppsz_MediaExtensions
[]
=
{
EXTENSIONS_AUDIO_CSV
,
EXTENSIONS_VIDEO_CSV
,
NULL
};
/* Monitoring and directory scanning private functions */
typedef
struct
stat_list_t
stat_list_t
;
typedef
struct
preparsed_item_t
preparsed_item_t
;
static
void
UpdateLibrary
(
monitoring_thread_t
*
p_mon
);
static
void
ScanFiles
(
monitoring_thread_t
*
,
int
,
bool
,
stat_list_t
*
stparent
);
static
int
Sort
(
const
char
**
,
const
char
**
);
/* Struct used to verify there are no recursive directory */
struct
stat_list_t
{
stat_list_t
*
parent
;
struct
stat
st
;
};
struct
preparsed_item_t
{
monitoring_thread_t
*
p_mon
;
char
*
psz_uri
;
int
i_dir_id
;
int
i_mtime
;
int
i_update_id
;
bool
b_update
;
};
/**
* @brief Remove a directory to monitor
* @param p_ml A media library object
* @param psz_dir the directory to remove
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int
RemoveDirToMonitor
(
media_library_t
*
p_ml
,
const
char
*
psz_dir
)
{
assert
(
p_ml
);
char
**
pp_results
=
NULL
;
int
i_cols
=
0
,
i_rows
=
0
,
i_ret
=
VLC_SUCCESS
;
int
i
;
bool
b_recursive
=
var_CreateGetBool
(
p_ml
,
"ml-recursive-scan"
);
if
(
b_recursive
)
{
i_ret
=
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT media.id FROM media JOIN directories ON "
"(media.directory_id = directories.id) WHERE "
"directories.uri LIKE '%q%%'"
,
psz_dir
);
if
(
i_ret
!=
VLC_SUCCESS
)
{
msg_Err
(
p_ml
,
"Error occurred while making a query to the database"
);
return
i_ret
;
}
QuerySimple
(
p_ml
,
"DELETE FROM directories WHERE uri LIKE '%q%%'"
,
psz_dir
);
}
else
{
i_ret
=
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT media.id FROM media JOIN directories ON "
"(media.directory_id = directories.id) WHERE "
"directories.uri = %Q"
,
psz_dir
);
if
(
i_ret
!=
VLC_SUCCESS
)
{
msg_Err
(
p_ml
,
"Error occurred while making a query to the database"
);
return
i_ret
;
}
QuerySimple
(
p_ml
,
"DELETE FROM directories WHERE uri = %Q"
,
psz_dir
);
}
vlc_array_t
*
p_where
=
vlc_array_new
();
for
(
i
=
1
;
i
<=
i_rows
;
i
++
)
{
int
id
=
atoi
(
pp_results
[
i
*
i_cols
]
);
ml_element_t
*
p_find
=
(
ml_element_t
*
)
calloc
(
1
,
sizeof
(
ml_element_t
)
);
p_find
->
criteria
=
ML_ID
;
p_find
->
value
.
i
=
id
;
vlc_array_append
(
p_where
,
p_find
);
}
Delete
(
p_ml
,
p_where
);
FreeSQLResult
(
p_ml
,
pp_results
);
for
(
i
=
0
;
i
<
vlc_array_count
(
p_where
);
i
++
)
{
free
(
vlc_array_item_at_index
(
p_where
,
i
)
);
}
vlc_array_destroy
(
p_where
);
return
VLC_SUCCESS
;
}
/**
* @brief Get the list of the monitored directories
* @param p_ml A media library object
* @param p_array An initialized array where the list will be put in
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int
ListMonitoredDirs
(
media_library_t
*
p_ml
,
vlc_array_t
*
p_array
)
{
char
**
pp_results
;
int
i_cols
,
i_rows
;
int
i
;
if
(
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT uri AS directory_uri FROM directories WHERE recursive=0"
)
!=
VLC_SUCCESS
)
return
VLC_EGENERIC
;
for
(
i
=
1
;
i
<=
i_rows
;
i
++
)
{
vlc_array_append
(
p_array
,
strdup
(
pp_results
[
i
]
)
);
}
FreeSQLResult
(
p_ml
,
pp_results
);
return
VLC_SUCCESS
;
}
/**
* @brief Add a directory to monitor
* @param p_ml This media_library_t object
* @param psz_dir the directory to add
* @return VLC_SUCCESS or VLC_EGENERIC
*/
int
AddDirToMonitor
(
media_library_t
*
p_ml
,
const
char
*
psz_dir
)
{
assert
(
p_ml
);
/* Verify if we can open the directory */
DIR
*
dir
=
vlc_opendir
(
psz_dir
);
if
(
!
dir
)
{
int
err
=
errno
;
if
(
err
!=
ENOTDIR
)
msg_Err
(
p_ml
,
"%s: %m"
,
psz_dir
);
else
msg_Dbg
(
p_ml
,
"`%s' is not a directory"
,
psz_dir
);
errno
=
err
;
return
VLC_EGENERIC
;
}
closedir
(
dir
);
msg_Dbg
(
p_ml
,
"Adding directory `%s' to be monitored"
,
psz_dir
);
QuerySimple
(
p_ml
,
"INSERT INTO directories ( uri, timestamp, "
"recursive ) VALUES( %Q, 0, 0 )"
,
psz_dir
);
vlc_cond_signal
(
&
p_ml
->
p_sys
->
p_mon
->
wait
);
return
VLC_SUCCESS
;
}
static
int
Sort
(
const
char
**
a
,
const
char
**
b
)
{
#ifdef HAVE_STRCOLL
return
strcoll
(
*
a
,
*
b
);
#else
return
strcmp
(
*
a
,
*
b
);
#endif
}
/**
* @brief Directory Monitoring thread loop
*/
void
*
RunMonitoringThread
(
void
*
p_this
)
{
monitoring_thread_t
*
p_mon
=
(
monitoring_thread_t
*
)
p_this
;
vlc_cond_init
(
&
p_mon
->
wait
);
vlc_mutex_init
(
&
p_mon
->
lock
);
var_Create
(
p_mon
,
"ml-recursive-scan"
,
VLC_VAR_BOOL
|
VLC_VAR_DOINHERIT
);
while
(
vlc_object_alive
(
p_mon
)
)
{
vlc_mutex_lock
(
&
p_mon
->
lock
);
/* Update */
UpdateLibrary
(
p_mon
);
/* We wait MONITORING_DELAY seconds or wait that the media library
signals us to do something */
vlc_cond_timedwait
(
&
p_mon
->
wait
,
&
p_mon
->
lock
,
mdate
()
+
1000000
*
MONITORING_DELAY
);
vlc_mutex_unlock
(
&
p_mon
->
lock
);
}
vlc_cond_destroy
(
&
p_mon
->
wait
);
vlc_mutex_destroy
(
&
p_mon
->
lock
);
return
NULL
;
}
/**
* @brief Update library if new files found or updated
*/
static
void
UpdateLibrary
(
monitoring_thread_t
*
p_mon
)
{
int
i_rows
,
i_cols
,
i
;
char
**
pp_results
;
media_library_t
*
p_ml
=
p_mon
->
p_ml
;
struct
stat
s_stat
;
bool
b_recursive
=
var_GetBool
(
p_mon
,
"ml-recursive-scan"
);
msg_Dbg
(
p_mon
,
"Scanning directories"
);
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT id AS directory_id, uri AS directory_uri, "
"timestamp AS directory_ts FROM directories"
);
msg_Dbg
(
p_mon
,
"%d directories to scan"
,
i_rows
);
for
(
i
=
1
;
i
<=
i_rows
;
i
++
)
{
int
id
=
atoi
(
pp_results
[
i
*
i_cols
]
);
char
*
psz_dir
=
pp_results
[
i
*
i_cols
+
1
];
int
timestamp
=
atoi
(
pp_results
[
i
*
i_cols
+
2
]
);
if
(
vlc_stat
(
psz_dir
,
&
s_stat
)
==
-
1
)
{
int
err
=
errno
;
if
(
err
==
ENOTDIR
||
err
==
ENOENT
)
{
msg_Dbg
(
p_mon
,
"Removing `%s'"
,
psz_dir
);
RemoveDirToMonitor
(
p_ml
,
psz_dir
);
}
else
{
msg_Err
(
p_mon
,
"%s: %m"
,
psz_dir
);
FreeSQLResult
(
p_ml
,
pp_results
);
return
;
}
errno
=
err
;
}
if
(
!
S_ISDIR
(
s_stat
.
st_mode
)
)
{
msg_Dbg
(
p_mon
,
"Removing `%s'"
,
psz_dir
);
RemoveDirToMonitor
(
p_ml
,
psz_dir
);
}
if
(
timestamp
<
s_stat
.
st_mtime
)
{
msg_Dbg
(
p_mon
,
"Adding `%s'"
,
psz_dir
);
ScanFiles
(
p_mon
,
id
,
b_recursive
,
NULL
);
}
}
FreeSQLResult
(
p_ml
,
pp_results
);
}
/**
* @brief Callback for input item preparser to directory monitor
*/
static
void
PreparseComplete
(
const
vlc_event_t
*
p_event
,
void
*
p_data
)
{
int
i_ret
=
VLC_SUCCESS
;
preparsed_item_t
*
p_itemobject
=
(
preparsed_item_t
*
)
p_data
;
monitoring_thread_t
*
p_mon
=
p_itemobject
->
p_mon
;
media_library_t
*
p_ml
=
(
media_library_t
*
)
p_mon
->
p_ml
;
input_item_t
*
p_input
=
(
input_item_t
*
)
p_event
->
p_obj
;
if
(
input_item_IsPreparsed
(
p_input
)
)
{
if
(
p_itemobject
->
b_update
)
{
//TODO: Perhaps we don't have to load everything?
ml_media_t
*
p_media
=
GetMedia
(
p_ml
,
p_itemobject
->
i_update_id
,
ML_MEDIA_SPARSE
,
true
);
CopyInputItemToMedia
(
p_media
,
p_input
);
i_ret
=
UpdateMedia
(
p_ml
,
p_media
);
ml_gc_decref
(
p_media
);
}
else
i_ret
=
AddInputItem
(
p_ml
,
p_input
);
}
if
(
i_ret
!=
VLC_SUCCESS
)
msg_Dbg
(
p_mon
,
"Item could not be correctly added"
" or updated during scan: %s"
,
p_input
->
psz_uri
);
QuerySimple
(
p_ml
,
"UPDATE media SET directory_id=%d, timestamp=%d "
"WHERE id=%d"
,
p_itemobject
->
i_dir_id
,
p_itemobject
->
i_mtime
,
GetMediaIdOfURI
(
p_ml
,
p_input
->
psz_uri
)
);
vlc_event_detach
(
&
p_input
->
event_manager
,
vlc_InputItemPreparsedChanged
,
PreparseComplete
,
p_itemobject
);
vlc_gc_decref
(
p_input
);
free
(
p_itemobject
->
psz_uri
);
}
/**
* @brief Scan files in a particular directory
*/
static
void
ScanFiles
(
monitoring_thread_t
*
p_mon
,
int
i_dir_id
,
bool
b_recursive
,
stat_list_t
*
stparent
)
{
int
i_rows
,
i_cols
,
i_dir_content
,
i
,
i_mon_rows
,
i_mon_cols
;
char
**
ppsz_monitored_files
;
char
**
pp_results
,
*
psz_dir
;
char
**
pp_dir_content
;
bool
*
pb_processed
;
input_item_t
*
p_input
;
struct
stat
s_stat
;
media_library_t
*
p_ml
=
(
media_library_t
*
)
p_mon
->
p_ml
;
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT uri AS directory_uri FROM directories WHERE id = '%d'"
,
i_dir_id
);
if
(
i_rows
<
1
)
{
msg_Dbg
(
p_mon
,
"query returned no directory for dir_id: %d (%s:%d)"
,
i_dir_id
,
__FILE__
,
__LINE__
);
return
;
}
psz_dir
=
strdup
(
pp_results
[
1
]
);
FreeSQLResult
(
p_ml
,
pp_results
);
struct
stat_list_t
stself
;
if
(
vlc_stat
(
psz_dir
,
&
stself
.
st
)
==
-
1
)
{
msg_Err
(
p_ml
,
"Cannot stat `%s': %m"
,
psz_dir
);
free
(
psz_dir
);
return
;
}
#ifndef WIN32
for
(
stat_list_t
*
stats
=
stparent
;
stats
!=
NULL
;
stats
=
stats
->
parent
)
{
if
(
(
stself
.
st
.
st_ino
==
stats
->
st
.
st_ino
)
&&
(
stself
.
st
.
st_dev
==
stats
->
st
.
st_dev
)
)
{
msg_Warn
(
p_ml
,
"Ignoring infinitely recursive directory `%s'"
,
psz_dir
);
free
(
psz_dir
);
return
;
}
}
#else
/* Windows has st_dev (driver letter - 'A'), but it zeroes st_ino,
* so that the test above will always incorrectly succeed.
* Besides, Windows does not have dirfd(). */
#endif
stself
.
parent
=
stparent
;
QuerySimple
(
p_ml
,
"UPDATE directories SET timestamp=%d WHERE id = %d"
,
stself
.
st
.
st_mtime
,
i_dir_id
);
Query
(
p_ml
,
&
ppsz_monitored_files
,
&
i_mon_rows
,
&
i_mon_cols
,
"SELECT id AS media_id, timestamp AS media_ts, uri AS media_uri "
"FROM media WHERE directory_id = %d"
,
i_dir_id
);
pb_processed
=
malloc
(
sizeof
(
bool
)
*
i_mon_rows
);
for
(
i
=
0
;
i
<
i_mon_rows
;
i
++
)
pb_processed
[
i
]
=
false
;
i_dir_content
=
vlc_scandir
(
psz_dir
,
&
pp_dir_content
,
NULL
,
Sort
);
if
(
i_dir_content
==
-
1
)
{
msg_Err
(
p_mon
,
"Cannot read `%s': %m"
,
psz_dir
);
free
(
pb_processed
);
free
(
psz_dir
);
return
;
}
else
if
(
i_dir_content
==
0
)
{
msg_Dbg
(
p_mon
,
"Nothing in directory `%s'"
,
psz_dir
);
free
(
pb_processed
);
free
(
psz_dir
);
return
;
}
for
(
i
=
0
;
i
<
i_dir_content
;
i
++
)
{
const
char
*
psz_entry
=
pp_dir_content
[
i
];
if
(
psz_entry
[
0
]
!=
'.'
)
{
/* 7 is the size of "file://" */
char
psz_uri
[
strlen
(
psz_dir
)
+
strlen
(
psz_entry
)
+
2
+
7
];
sprintf
(
psz_uri
,
"%s/%s"
,
psz_dir
,
psz_entry
);
if
(
vlc_stat
(
psz_uri
,
&
s_stat
)
==
-
1
)
{
msg_Err
(
p_mon
,
"%s: %m"
,
psz_uri
);
free
(
pb_processed
);
free
(
psz_dir
);
return
;
}
if
(
S_ISREG
(
s_stat
.
st_mode
)
)
{
const
char
*
psz_dot
=
strrchr
(
psz_uri
,
'.'
);
if
(
psz_dot
++
&&
*
psz_dot
)
{
int
i_is_media
=
0
;
for
(
int
a
=
0
;
ppsz_MediaExtensions
[
a
];
a
++
)
{
if
(
!
strcasecmp
(
psz_dot
,
ppsz_MediaExtensions
[
a
]
)
)
{
i_is_media
=
1
;
break
;
}
}
if
(
!
i_is_media
)
{
msg_Dbg
(
p_mon
,
"ignoring file %s"
,
psz_uri
);
continue
;
}
}
char
*
psz_tmp
=
encode_URI_component
(
psz_uri
);
char
*
psz_encoded_uri
=
(
char
*
)
calloc
(
strlen
(
psz_tmp
)
+
9
,
1
);
strcpy
(
psz_encoded_uri
,
"file:///"
);
strcat
(
psz_encoded_uri
,
psz_tmp
);
free
(
psz_tmp
);
/* Check if given media is already in DB and it has been updated */
bool
b_skip
=
false
;
bool
b_update
=
false
;
int
j
=
1
;
for
(
j
=
1
;
j
<=
i_mon_rows
;
j
++
)
{
if
(
strcasecmp
(
ppsz_monitored_files
[
j
*
i_mon_cols
+
2
],
psz_encoded_uri
)
!=
0
)
continue
;
b_update
=
true
;
pb_processed
[
j
-
1
]
=
true
;
if
(
atoi
(
ppsz_monitored_files
[
j
*
i_mon_cols
+
1
]
)
<
s_stat
.
st_mtime
)
{
b_skip
=
false
;
break
;
}
else
{
b_skip
=
true
;
break
;
}
}
msg_Dbg
(
p_ml
,
"Checking if %s is in DB. Found: %d"
,
psz_encoded_uri
,
b_skip
?
1
:
0
);
if
(
b_skip
)
continue
;
p_input
=
input_item_New
(
psz_encoded_uri
,
psz_entry
);
playlist_t
*
p_pl
=
pl_Get
(
p_mon
);
preparsed_item_t
*
p_itemobject
;
p_itemobject
=
malloc
(
sizeof
(
preparsed_item_t
)
);
p_itemobject
->
i_dir_id
=
i_dir_id
;
p_itemobject
->
psz_uri
=
psz_encoded_uri
;
p_itemobject
->
i_mtime
=
s_stat
.
st_mtime
;
p_itemobject
->
p_mon
=
p_mon
;
p_itemobject
->
b_update
=
b_update
;
p_itemobject
->
i_update_id
=
b_update
?
atoi
(
ppsz_monitored_files
[
j
*
i_mon_cols
+
0
]
)
:
0
;
vlc_event_manager_t
*
p_em
=
&
p_input
->
event_manager
;
vlc_event_attach
(
p_em
,
vlc_InputItemPreparsedChanged
,
PreparseComplete
,
p_itemobject
);
playlist_PreparseEnqueue
(
p_pl
,
p_input
);
}
else
if
(
S_ISDIR
(
s_stat
.
st_mode
)
&&
b_recursive
)
{
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT id AS directory_id FROM directories "
"WHERE uri=%Q"
,
psz_uri
);
FreeSQLResult
(
p_ml
,
pp_results
);
if
(
i_rows
<=
0
)
{
msg_Dbg
(
p_mon
,
"New directory `%s' in dir of id %d"
,
psz_uri
,
i_dir_id
);
QuerySimple
(
p_ml
,
"INSERT INTO directories (uri, timestamp, "
"recursive) VALUES(%Q, 0, 1)"
,
psz_uri
);
// We get the id of the directory we've just added
Query
(
p_ml
,
&
pp_results
,
&
i_rows
,
&
i_cols
,
"SELECT id AS directory_id FROM directories WHERE uri=%Q"
,
psz_uri
);
if
(
i_rows
<=
0
)
{
msg_Err
(
p_mon
,
"Directory `%s' was not sucessfully"
" added to the database"
,
psz_uri
);
FreeSQLResult
(
p_ml
,
pp_results
);
continue
;
}
ScanFiles
(
p_mon
,
atoi
(
pp_results
[
1
]
),
b_recursive
,
&
stself
);
FreeSQLResult
(
p_ml
,
pp_results
);
}
}
}
}
vlc_array_t
*
delete_ids
=
vlc_array_new
();
for
(
i
=
0
;
i
<
i_mon_rows
;
i
++
)
{
if
(
!
pb_processed
[
i
]
)
{
/* This file doesn't exist anymore. Let's...urm...delete it. */
ml_element_t
*
find
=
(
ml_element_t
*
)
calloc
(
1
,
sizeof
(
ml_element_t
)
);
find
->
criteria
=
ML_ID
;
find
->
value
.
i
=
atoi
(
ppsz_monitored_files
[
(
i
+
1
)
*
i_mon_cols
]
);
vlc_array_append
(
delete_ids
,
find
);
}
}
/* Delete the unfound media */
if
(
Delete
(
p_ml
,
delete_ids
)
!=
VLC_SUCCESS
)
msg_Dbg
(
p_ml
,
"Something went wrong in multi delete"
);
for
(
i
=
0
;
i
<
vlc_array_count
(
delete_ids
);
i
++
)
{
free
(
vlc_array_item_at_index
(
delete_ids
,
i
)
);
}
vlc_array_destroy
(
delete_ids
);
FreeSQLResult
(
p_ml
,
ppsz_monitored_files
);
for
(
i
=
0
;
i
<
i_dir_content
;
i
++
)
free
(
pp_dir_content
[
i
]
);
free
(
pp_dir_content
);
free
(
psz_dir
);
free
(
pb_processed
);
}
modules/media_library/sql_search.c
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* 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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#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 occurred 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 occurred 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
=
(
char
*
)
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
)
{
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 occurred (%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 %s '%s%q%s'", fmt, \
(ML_COMP_EQUAL)?"=":"LIKE", \
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 */
*
ppsz_where
=
strdup
(
psz_left
?
psz_left
:
""
);
if
(
!*
ppsz_where
)
{
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
deleted
100644 → 0
View file @
8e3288f9
/*****************************************************************************
* 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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#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
;
i_ret
=
VLC_ENOMEM
;
/** 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
]
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
]
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
)
{
i_album_id
=
ml_GetAlbumId
(
p_ml
,
psz_album
);
if
(
i_album_id
<
0
)
//0 is Unknown
{
i_ret
=
AddAlbum
(
p_ml
,
psz_album
,
psz_cover
,
0
);
if
(
i_ret
!=
VLC_SUCCESS
)
{
msg_Err
(
p_ml
,
"Couldn't AddAlbum at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
[
ML_ALBUM_ID
]
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
;
msg_Err
(
p_ml
,
"Nothing found to create update at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
goto
quit_buildupdate
;
}
if
(
psz_fullset
){
if
(
asprintf
(
ppsz_query
,
"UPDATE %s SET %s WHERE %s"
,
psz_table
,
psz_fullset
,
psz_where
)
==
-
1
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
);
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
);
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
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
)
{
msg_Err
(
p_ml
,
"Couldn't create string at BuildUpdate():(%s, %d)"
,
__FILE__
,
__LINE__
);
goto
quit_buildupdate
;
}
#ifndef NDEBUG
msg_Dbg
(
p_ml
,
"updated media where %s"
,
psz_where
);
#endif
goto
quit_buildupdate_success
;
quit_buildupdate:
msg_Warn
(
p_ml
,
"BuildUpdate() could not generate update sql query"
);
quit_buildupdate_success:
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
;
}
po/POTFILES.in
View file @
79990b94
...
...
@@ -935,7 +935,6 @@ modules/lua/libs/volume.c
modules/lua/meta.c
modules/lua/vlc.c
modules/lua/vlc.h
modules/media_library/sql_media_library.c
modules/meta_engine/folder.c
modules/meta_engine/taglib.cpp
modules/misc/audioscrobbler.c
...
...
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