From 0958de4a12e6a57bdcc25a5db74608e0ea2b3dcb Mon Sep 17 00:00:00 2001
From: Srikanth Raju <srikiraju@gmail.com>
Date: Thu, 20 Aug 2009 01:54:20 +0530
Subject: [PATCH] Media Library module

This module adds functionality that allows vlc to store and manage a
large amount of media
---
 configure.ac                              |   17 +
 include/vlc_interface.h                   |   25 +-
 modules/LIST                              |    1 +
 modules/Makefile.am                       |    1 +
 modules/media_library/.gitignore          |    1 +
 modules/media_library/Modules.am          |    9 +
 modules/media_library/item_list.c         |  291 ++++++
 modules/media_library/item_list.h         |   83 ++
 modules/media_library/media_pool.c        |  140 +++
 modules/media_library/ml_watch.c          |  549 +++++++++++
 modules/media_library/sql_add.c           |  301 ++++++
 modules/media_library/sql_delete.c        |  122 +++
 modules/media_library/sql_media_library.c | 1511 +++++++++++++++++++++++++++++
 modules/media_library/sql_media_library.h |  318 ++++++
 modules/media_library/sql_monitor.c       |  551 +++++++++++
 modules/media_library/sql_search.c        | 1140 ++++++++++++++++++++++
 modules/media_library/sql_update.c        |  847 ++++++++++++++++
 17 files changed, 5903 insertions(+), 4 deletions(-)
 create mode 100644 modules/media_library/.gitignore
 create mode 100644 modules/media_library/Modules.am
 create mode 100644 modules/media_library/item_list.c
 create mode 100644 modules/media_library/item_list.h
 create mode 100644 modules/media_library/media_pool.c
 create mode 100644 modules/media_library/ml_watch.c
 create mode 100644 modules/media_library/sql_add.c
 create mode 100644 modules/media_library/sql_delete.c
 create mode 100644 modules/media_library/sql_media_library.c
 create mode 100644 modules/media_library/sql_media_library.h
 create mode 100644 modules/media_library/sql_monitor.c
 create mode 100644 modules/media_library/sql_search.c
 create mode 100644 modules/media_library/sql_update.c

diff --git a/configure.ac b/configure.ac
index c3ea404..2d33235 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4671,6 +4671,22 @@ PKG_ENABLE_MODULES_VLC([SQLITE], [], [sqlite3], [sqlite3], [auto])
 
 
 dnl
+dnl media library
+dnl
+AC_ARG_ENABLE(media-library,
+  [  --enable-media-library media library (default disabled)])
+if test "${enable_media_library}" == "yes"
+then
+  if test "${enable_sqlite}" != "yes"
+  then
+    AC_MSG_ERROR([SQLite module is required for the media library])
+  fi
+  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])
+fi
+
+dnl
 dnl  Endianness check
 dnl
 AC_C_BIGENDIAN
@@ -5207,6 +5223,7 @@ AC_CONFIG_FILES([
   modules/misc/stats/Makefile
   modules/misc/xml/Makefile
   modules/misc/probe/Makefile
+  modules/media_library/Makefile
   modules/mux/Makefile
   modules/mux/mpeg/Makefile
   modules/packetizer/Makefile
diff --git a/include/vlc_interface.h b/include/vlc_interface.h
index b8a43ae..2ab453c 100644
--- a/include/vlc_interface.h
+++ b/include/vlc_interface.h
@@ -173,17 +173,34 @@ typedef enum vlc_dialog {
                          "*.oga;*.ogg;*.oma;*.s3m;*.spx;" \
                          "*.wav;*.wma;*.wv;*.xm"
 
+#define EXTENSIONS_AUDIO_CSV "a52", "aac", "ac3", "ape", "dts", "flac", "it", \
+                         "m4a", "m4p", "mka", "mlp", "mod", "mp1", "mp2", "mp3",\
+                         "oga", "ogg", "oma", "s3m", "spx" \
+                         "wav", "wma", "wv", "xm"
+
 #define EXTENSIONS_VIDEO "*.asf;*.avi;*.divx;*.dv;*.flv;*.gxf;*.iso;*.m1v;*.m2v;" \
-                         "*.m2t;*.m2ts;*.m4v;*.mkv;*.mov;*.mp2;*.mp4;*.mpeg;*.mpeg1;" \
-                         "*.mpeg2;*.mpeg4;*.mpg;*.mts;*.mxf;*.nuv;" \
-                         "*.ogg;*.ogm;*.ogv;*.ogx;*.ps;" \
-                         "*.rec;*.rm;*.rmvb;*.tod;*.ts;*.vob;*.wmv"
+                          "*.m2t;*.m2ts;*.m4v;*.mkv;*.mov;*.mp2;*.mp4;*.mpeg;*.mpeg1;" \
+                          "*.mpeg2;*.mpeg4;*.mpg;*.mts;*.mxf;*.nuv;" \
+                          "*.ogg;*.ogm;*.ogv;*.ogx;*.ps;" \
+                          "*.rec;*.rm;*.rmvb;*.ts;*.vob;*.wmv"
+
+#define EXTENSIONS_VIDEO_CSV "asf", "avi", "divx", "dv", "flv", "gxf", "iso", \
+                             "m1v", "m2v", "m2t", "m2ts", "m4v", "mkv", "mov",\
+                             "mp2", "mp4", "mpeg", "mpeg1", \
+                             "mpeg2", "mpeg4", "mpg", "mts", "mxf", "nuv", \
+                             "ogg", "ogm", "ogv", "ogx", "ps", \
+                             "rec", "rm", "rmvb", "ts", "vob", "wmv"
 
 #define EXTENSIONS_PLAYLIST "*.asx;*.b4s;*.m3u;*.pls;*.ram;*.rar;*.vlc;*.xspf;*.zip"
 
+#define EXTENSIONS_PLAYLIST_CSV "asx", "b4s", "m3u", "pls", "ram", "vlc", "xspf"
+
 #define EXTENSIONS_MEDIA EXTENSIONS_VIDEO ";" EXTENSIONS_AUDIO ";" \
                           EXTENSIONS_PLAYLIST
 
+#define EXTENSIONS_MEDIA_CSV EXTENSTIONS_VIDEO_CSV , EXTENSIONS_AUDIO_CSV \
+                                , EXTENSIONS_PLAYLIST_CSV
+
 #define EXTENSIONS_SUBTITLE "*.cdg;*.idx;*.srt;*.sub;*.utf;*.ass;*.ssa;*.aqt;" \
                             "*.jss;*.psb;*.rt;*.smi"
 
diff --git a/modules/LIST b/modules/LIST
index 2039c08..9cff2c0 100644
--- a/modules/LIST
+++ b/modules/LIST
@@ -178,6 +178,7 @@ $Id$
  * magnify: zoom video filter
  * marq: Overlays a marquee on the video
  * mash: OpenMash based decoder
+ * media_library: a media library
  * memcpy3dn: 3D Now! accelerated version of memcpy
  * memcpy: memory chunk copying module
  * memcpyaltivec: Altivec accelerated version of memcpy
diff --git a/modules/Makefile.am b/modules/Makefile.am
index 4ac47bd..e9183fb 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -7,6 +7,7 @@ BASE_SUBDIRS = \
 	control \
 	demux \
 	gui \
+    media_library \
 	meta_engine \
 	misc \
 	packetizer \
diff --git a/modules/media_library/.gitignore b/modules/media_library/.gitignore
new file mode 100644
index 0000000..08a6d72
--- /dev/null
+++ b/modules/media_library/.gitignore
@@ -0,0 +1 @@
+Makefile.am
diff --git a/modules/media_library/Modules.am b/modules/media_library/Modules.am
new file mode 100644
index 0000000..417fc3c
--- /dev/null
+++ b/modules/media_library/Modules.am
@@ -0,0 +1,9 @@
+SOURCES_media_library = sql_media_library.c \
+                        sql_monitor.c \
+                        sql_search.c \
+                        sql_add.c \
+                        sql_update.c \
+                        sql_delete.c \
+                        item_list.c \
+                        ml_watch.c \
+                        media_pool.c
diff --git a/modules/media_library/item_list.c b/modules/media_library/item_list.c
new file mode 100644
index 0000000..81ab647
--- /dev/null
+++ b/modules/media_library/item_list.c
@@ -0,0 +1,291 @@
+/*****************************************************************************
+ * item_list.c: An input_item_t+media_id couple list for the media library
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN Team
+ * $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@gmail.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_list_hash( p_item->i_id ) ], 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_list_hash( p_item->i_id ) ];
+    p_new->i_refs = 0;
+    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_list_hash( p_item->i_id ) ] = 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_list_hash( p_item->i_id ) ], 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_list_hash( p_item->i_id ) ] = 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_list_hash( p_item->i_id ) ], 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_list_hash( p_item->i_id ) ], p_elt )
+    {
+        if( p_elt->p_item == p_item )
+        {
+            p_wt->p_hlist[ item_list_hash( p_item->i_id ) ] = 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_list_hash( p_item->i_id ) ], 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 );
+}
diff --git a/modules/media_library/item_list.h b/modules/media_library/item_list.h
new file mode 100644
index 0000000..7a593fa
--- /dev/null
+++ b/modules/media_library/item_list.h
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * item_list.h : Item list data structure for Watching system
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Srikanth Raju <srikiraju@gmail.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>
+
+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 {
+    int           i_media_id; /**< Media id */
+    ml_media_t   *p_media;    /**< Media item */
+    input_item_t *p_item;     /**< Input item */
+    int           i_age;      /**< Time spent in this list without activity */
+    int           i_refs;     /**< Number of important refs */
+    item_list_t  *p_next;     /**< Next element in the list */
+    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_list_hash( int item_id )
+{
+    return item_id % ML_ITEMLIST_HASH_LENGTH;
+}
+
+#endif /* ML_ITEM_LIST_H */
diff --git a/modules/media_library/media_pool.c b/modules/media_library/media_pool.c
new file mode 100644
index 0000000..0d772d8
--- /dev/null
+++ b/modules/media_library/media_pool.c
@@ -0,0 +1,140 @@
+/*****************************************************************************
+ * media_pool.c : Media pool for watching system
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Srikanth Raju <srikiraju@gmail.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 )
+{
+    ml_LockMedia( p_media );
+    assert( p_media );
+    assert( p_media->i_id > 0 );
+    if( p_media->b_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->b_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 );
+    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;
+            gc_object_t* p_gc = &p_media->vlc_gc_data;
+            if( p_gc->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;
+                vlc_gc_decref( p_item->p_media );
+                free( p_item );
+            }
+            p_prev = p_item;
+        }
+    }
+    vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
+}
diff --git a/modules/media_library/ml_watch.c b/modules/media_library/ml_watch.c
new file mode 100644
index 0000000..9f807d9
--- /dev/null
+++ b/modules/media_library/ml_watch.c
@@ -0,0 +1,549 @@
+/*****************************************************************************
+ * ml_watch.c: SQL-based media library: Medias watching system
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN team
+ * $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@gmail.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_Thread( vlc_object_t *p_this );
+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 );
+
+/**
+ * @brief Watching thread
+ */
+static void* watch_Thread( vlc_object_t *p_this )
+{
+    watch_thread_t *p_watch = ( watch_thread_t* ) p_this;
+    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 );
+    while( vlc_object_alive( p_watch ) )
+    {
+        watch_loop( p_ml, !i_ret );
+        if( !vlc_object_alive( p_watch ) )
+            break;
+        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 = vlc_object_create( p_ml, sizeof(watch_thread_t) );
+    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_object_attach( p_wt, p_ml );
+
+    vlc_cond_init( &p_wt->cond );
+    vlc_mutex_init( &p_wt->lock );
+
+    if( vlc_thread_create( p_wt, "Media Library Auto-Update",
+                            watch_Thread, VLC_THREAD_PRIORITY_LOW ) )
+    {
+        msg_Dbg( p_ml, "unable to launch the auto-updating thread" );
+        vlc_object_release( p_wt );
+        return VLC_EGENERIC;
+    }
+
+    /* Wait on playlist events
+     * playlist-item-append -> entry to playlist
+     * item-current -> 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_Hold( p_ml );
+    var_AddCallback( p_pl, "item-current", 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 );
+    pl_Release( 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
+ */
+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_Hold( p_ml );
+    var_DelCallback( p_pl, "item-current", watch_PlaylistItemCurrent, p_ml );
+    var_DelCallback( p_pl, "playlist-item-append", watch_PlaylistItemAppend, p_ml );
+    var_DelCallback( p_pl, "playlist-item-deleted", watch_PlaylistItemDeleted, p_ml );
+    pl_Release( p_ml );
+
+    /* Stop the watch thread and join in */
+    vlc_object_kill( p_ml->p_sys->p_watch );
+    vlc_thread_join( p_ml->p_sys->p_watch );
+
+    /* Flush item list */
+    vlc_mutex_lock( &p_ml->p_sys->p_watch->list_mutex );
+    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 );
+    }
+    vlc_mutex_unlock( &p_ml->p_sys->p_watch->list_mutex );
+    item_list_destroy( p_ml->p_sys->p_watch );
+
+    /* 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 );
+    vlc_object_release( p_ml->p_sys->p_watch );
+
+    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;
+    vlc_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 );
+    ml_media_t* p_media = NULL;
+    media_library_t* p_ml = ( media_library_t* ) data;
+    playlist_t* p_playlist = pl_Hold( 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;
+
+    vlc_mutex_lock( &p_ml->p_sys->p_watch->list_mutex );
+    /* Check if we are already watching this item */
+    il_foreachlist( p_ml->p_sys->p_watch->p_hlist[ item_list_hash( p_item->i_id ) ], p_elt )
+    {
+        if( p_elt->p_item->i_id == p_item->i_id )
+        {
+            p_elt->i_refs++;
+            vlc_mutex_unlock( &p_ml->p_sys->p_watch->list_mutex );
+            goto quit_playlistitemappend;
+        }
+    }
+    vlc_mutex_unlock( &p_ml->p_sys->p_watch->list_mutex );
+
+    /* Is this item in ML? */
+    int i_media_id = GetIdOfURI( p_ml, p_item->psz_uri );
+    int i_ret = 0;
+    if( i_media_id <= 0 )
+    {
+        bool b_add = var_CreateGetBool( p_ml, "ml-auto-add" );
+        if( b_add )
+        {
+            i_ret = AddInputItem( p_ml, p_item );
+            if( i_ret != VLC_SUCCESS )
+                goto quit_playlistitemappend;
+            i_media_id = GetIdOfURI( p_ml, p_item->psz_uri );
+        }
+        else
+            goto quit_playlistitemappend;
+    }
+    vlc_mutex_lock( &p_ml->p_sys->p_watch->list_mutex );
+    p_media = media_New( p_ml, i_media_id, ML_MEDIA, true );
+    if( p_media == NULL )
+        goto quit_playlistitemappend;
+    i_ret = __watch_add_Item( p_ml, p_item, p_media, true );
+    if( i_ret != VLC_SUCCESS )
+        goto quit_playlistitemappend;
+
+    /* Find the new item and increment its ref */
+    il_foreachlist( p_ml->p_sys->p_watch->p_hlist[ item_list_hash( p_item->i_id ) ], p_elt )
+    {
+        if( p_elt->p_item->i_id == p_item->i_id )
+        {
+            p_elt->i_refs++;
+            vlc_mutex_unlock( &p_ml->p_sys->p_watch->list_mutex );
+            goto quit_playlistitemappend;
+        }
+    }
+    vlc_mutex_unlock( &p_ml->p_sys->p_watch->list_mutex );
+
+quit_playlistitemappend:
+    if( p_media )
+        ml_gc_decref( p_media );
+    pl_Release( p_ml );
+    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_Hold( 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_list_hash( p_item->i_id ) ], p_elt )
+    {
+        if( p_elt->p_item->i_id == p_item->i_id )
+        {
+            p_elt->i_refs--;
+            break;
+        }
+    }
+
+    pl_Release( p_ml );
+    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;
+
+    if( strcmp( psz_var, "item-current" ) != 0 )
+        /* This case should not happen... */
+        return VLC_EGENERIC;
+
+    /* 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 informations 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 )
+    {
+        SetAlbumCover( 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 )
+{
+    pool_GC( p_ml );
+    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 );
+}
+
+
diff --git a/modules/media_library/sql_add.c b/modules/media_library/sql_add.c
new file mode 100644
index 0000000..6358c9b
--- /dev/null
+++ b/modules/media_library/sql_add.c
@@ -0,0 +1,301 @@
+/*****************************************************************************
+ * sql_media_library.c: SQL-based media library
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN Team
+ * $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 = GetIdOfURI( 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;
+
+    var_SetInteger( p_ml, "media-added", id );
+
+quit_addmedia:
+    if( i_ret == VLC_SUCCESS )
+        Commit( p_ml );
+    else
+        Rollback( p_ml );
+    ml_UnlockMedia( p_media );
+    return i_ret;
+}
+
+
+/**
+ * @brief Add generic album to ML
+ *
+ * @param p_ml this Media Library
+ * @param psz_title album title, cannot be null
+ * @param psz_cover album cover, can be null
+ * @return VLC_SUCCESS or a VLC error code
+ *
+ * This will add a new in the album table, without checking if album is
+ * already present (or another album with same title)
+ */
+int AddAlbum( media_library_t *p_ml, const char *psz_title,
+        const char *psz_cover, const int i_album_artist )
+{
+    assert( p_ml );
+
+    if( !psz_title || !*psz_title )
+    {
+        msg_Warn( p_ml, "tried to add an album without title" );
+        return VLC_EGENERIC;
+    }
+    msg_Dbg( p_ml, "New album: '%s'", psz_title );
+
+    int i_ret = QuerySimple( p_ml,
+                        "INSERT INTO album ( title, cover, album_artist_id ) "
+                        "VALUES ( %Q, %Q, '%d' )",
+                        psz_title , psz_cover, i_album_artist );
+
+    return i_ret;
+}
+
+
+/**
+ * @brief Add generic people to ML
+ *
+ * @param p_ml this Media Library
+ * @param psz_title name
+ * @param i_role role: 1 for artist, 2 for publisher
+ * @return VLC_SUCCESS or a VLC error code
+ *
+ * This will add a new in the album table, without checking if album is
+ * already present (or another album with same title)
+ */
+int AddPeople( media_library_t *p_ml, const char *psz_name,
+        const char* psz_role )
+{
+    assert( p_ml );
+    assert( psz_role && *psz_role );
+
+    if( !psz_name || !*psz_name )
+    {
+        msg_Warn( p_ml, "tried to add an artist without name" );
+        return VLC_EGENERIC;
+    }
+    msg_Dbg( p_ml, "New people: (%s) '%s'", psz_role, psz_name );
+
+    int i_ret = QuerySimple( p_ml,
+                             "INSERT INTO people ( name, role ) "
+                             "VALUES ( %Q, %Q )",
+                             psz_name, psz_role );
+
+    return i_ret;
+}
+
+/**
+ * @brief Add element to ML based on an Input Item
+ * @param p_ml This media_library_t object
+ * @param p_input input item to add
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+int AddInputItem( media_library_t *p_ml, input_item_t *p_input )
+{
+    assert( p_ml );
+    if( !p_input )
+        return VLC_EGENERIC;
+    int i_ret = VLC_SUCCESS;
+
+    vlc_gc_incref( p_input );
+
+    /* Check input item is not already in the ML */
+    i_ret = GetIdOfInputItem( 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 = GetMedia( 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 );
+}
+
diff --git a/modules/media_library/sql_delete.c b/modules/media_library/sql_delete.c
new file mode 100644
index 0000000..c7d9992
--- /dev/null
+++ b/modules/media_library/sql_delete.c
@@ -0,0 +1,122 @@
+/*****************************************************************************
+ * sql_delete.c: SQL-based media library: all database delete functions
+ *****************************************************************************
+ * Copyright (C) 2008-2009 The VideoLAN Team
+ *
+ * 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@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "sql_media_library.h"
+
+
+/**
+ * @brief Generic DELETE function for many medias
+ * Delete a media and all its referencies which don't point
+ * an anything else.
+ *
+ * @param p_ml This media_library_t object
+ * @param p_array list of ids to delete
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ * TODO: Expand to delete media/artist/album given any params
+ */
+int Delete( media_library_t *p_ml, vlc_array_t *p_array )
+{
+    char *psz_idlist = NULL, *psz_tmp = NULL;
+    int i_return = VLC_ENOMEM;
+
+    int i_rows = 0, i_cols = 0;
+    char **pp_results = NULL;
+
+    if( vlc_array_count( p_array ) <= 0 )
+    {
+        i_return = VLC_SUCCESS;
+        goto quit_delete_final;
+    }
+    for( int i = 0; i < vlc_array_count( p_array ); i++ )
+    {
+        ml_element_t* find = ( ml_element_t * )
+            vlc_array_item_at_index( p_array, i );
+        assert( find->criteria == ML_ID );
+        if( !psz_idlist )
+        {
+            if( asprintf( &psz_tmp, "( %d", find->value.i ) == -1)
+            {
+                goto quit_delete_final;
+            }
+        }
+        else
+        {
+            if( asprintf( &psz_tmp, "%s, %d", psz_idlist,
+                    find->value.i ) == -1)
+            {
+                goto quit_delete_final;
+            }
+        }
+        free( psz_idlist );
+        psz_idlist = psz_tmp;
+        psz_tmp = NULL;
+    }
+    free( psz_tmp );
+    if( asprintf( &psz_tmp, "%s )", psz_idlist ? psz_idlist : "(" ) == -1 )
+    {
+        goto quit_delete_final;
+    }
+    psz_idlist = psz_tmp;
+    psz_tmp = NULL;
+
+    msg_Dbg( p_ml, "Multi Delete id list: %s", psz_idlist );
+
+    /**
+     * Below ensures you are emitting media-deleted only
+     * for existant media
+     */
+    Begin( p_ml );
+    i_return = Query( p_ml, &pp_results, &i_rows, &i_cols,
+            "SELECT id FROM media WHERE id IN %s", psz_idlist );
+    if( i_return != VLC_SUCCESS )
+        goto quit;
+
+    i_return = QuerySimple( p_ml,
+            "DELETE FROM media WHERE media.id IN %s", psz_idlist );
+    if( i_return != VLC_SUCCESS )
+        goto quit;
+
+    i_return = QuerySimple( p_ml,
+            "DELETE FROM extra WHERE extra.id IN %s", psz_idlist );
+    if( i_return != VLC_SUCCESS )
+        goto quit;
+
+    /* 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] ) );
+    }
+quit:
+    if( i_return == VLC_SUCCESS )
+        Commit( p_ml );
+    else
+        Rollback( p_ml );
+quit_delete_final:
+    FreeSQLResult( p_ml, pp_results );
+    free( psz_tmp );
+    free( psz_idlist );
+    return i_return;
+}
diff --git a/modules/media_library/sql_media_library.c b/modules/media_library/sql_media_library.c
new file mode 100644
index 0000000..70a2466
--- /dev/null
+++ b/modules/media_library/sql_media_library.c
@@ -0,0 +1,1511 @@
+/*****************************************************************************
+ * sql_media_library.c: SQL-based media library
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN Team
+ * $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@gmail.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"
+
+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( media_library_t *p_ml,
+                              input_item_t **pp_item,
+                              ml_media_t *p_media );
+
+/*****************************************************************************
+ * Module description
+ *****************************************************************************/
+vlc_module_begin()
+    set_shortname( "Media Library" )
+    set_description( _("Media Library based on a SQL database") )
+    set_capability( "media-library", 1 )
+    set_callbacks( load, unload )
+    set_category( CAT_ADVANCED )
+    set_subcategory( SUBCAT_ADVANCED_MISC )
+
+    add_string( "ml-filename", "media-library.db", NULL,
+            MEDIA_LIBRARY_PATH_TEXT, MEDIA_LIBRARY_PATH_LONGTEXT, false )
+    add_bool( "ml-recursive-scan", true, NULL, RECURSIVE_TEXT,
+            RECURSIVE_LONGTEXT, false )
+    add_bool( "ml-auto-add", true, NULL, "Auto add new medias",
+            "Automatically add new medias to ML", false )
+vlc_module_end()
+
+
+/**
+ * @brief Load module
+ * @param obj Parent object
+ */
+static int load( vlc_object_t *obj )
+{
+    msg_Dbg( obj, "loading media library" );
+
+    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 );
+
+    p_ml->p_sys->p_sql = sql_Create( p_ml, NULL );
+    if( !p_ml->p_sys->p_sql )
+    {
+        vlc_mutex_destroy( &p_ml->p_sys->lock );
+        free( p_ml->p_sys );
+        return VLC_EGENERIC;
+    }
+
+    InitDatabase( p_ml );
+
+    /* 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 );
+        vlc_object_release( p_ml->p_sys->p_sql );
+        free( p_ml->p_sys );
+        msg_Err( p_ml, "out of memory" );
+        return VLC_EGENERIC;
+    }
+    p_ml->p_sys->p_mon = p_mon;
+
+    p_mon->p_ml = p_ml;
+
+    if( vlc_thread_create( p_mon, "Media Library Monitoring",
+            RunMonitoringThread, VLC_THREAD_PRIORITY_LOW ) )
+    {
+        msg_Err( p_ml, "cannot spawn the media library monitoring thread" );
+        vlc_mutex_destroy( &p_ml->p_sys->lock );
+        vlc_object_release( 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, "module loaded" );
+
+    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 );
+
+    /* Free memory */
+    free( p_ml->p_sys->psz_host );
+    free( p_ml->p_sys->psz_user );
+    free( p_ml->p_sys->psz_password );
+
+    /* Stop the monitoring thread */
+    vlc_object_kill( p_ml->p_sys->p_mon );
+    vlc_thread_join( p_ml->p_sys->p_mon );
+    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 */
+    vlc_mutex_lock( &p_ml->p_sys->pool_mutex );
+    ml_media_t* item;
+    FOREACH_ARRAY( item, p_ml->p_sys->mediapool )
+        vlc_gc_decref( item );
+    FOREACH_END()
+    vlc_mutex_unlock( &p_ml->p_sys->pool_mutex );
+    vlc_mutex_destroy( &p_ml->p_sys->pool_mutex );
+
+    vlc_object_release( 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 )
+        return VLC_ENOMEM;
+
+#define IF_CMP( psz, cst )      if( !strcmp( res( 0, col ), psz ) )           \
+                                    indexes[ col ] = cst;
+#define ELSE_CMP( psz, cst )    else if( !strcmp( res( 0, col ), psz ) )      \
+                                    indexes[ col ] = cst;
+    for( int col = 0; col < i_cols; col++ )
+    {
+        /* Media */
+        IF_CMP(   "directory_id",         ML_DIRECTORY )
+        ELSE_CMP( "cover",                ML_COVER )
+        ELSE_CMP( "comment",              ML_COMMENT )
+        ELSE_CMP( "preview",              ML_PREVIEW )
+        ELSE_CMP( "duration",             ML_DURATION )
+        ELSE_CMP( "genre",                ML_GENRE )
+        ELSE_CMP( "id",                   ML_ID )
+        ELSE_CMP( "first_played",         ML_FIRST_PLAYED )
+        ELSE_CMP( "last_played",          ML_LAST_PLAYED )
+        ELSE_CMP( "played_count",         ML_PLAYED_COUNT )
+        ELSE_CMP( "skipped_count",        ML_SKIPPED_COUNT )
+        ELSE_CMP( "last_skipped",         ML_LAST_SKIPPED )
+        ELSE_CMP( "import_time",          ML_IMPORT_TIME )
+        ELSE_CMP( "score",                ML_SCORE )
+        ELSE_CMP( "title",                ML_TITLE )
+        ELSE_CMP( "original_title",       ML_ORIGINAL_TITLE )
+        ELSE_CMP( "track",                ML_TRACK_NUMBER )
+        ELSE_CMP( "disc",                 ML_DISC_NUMBER )
+        ELSE_CMP( "type",                 ML_TYPE )
+        ELSE_CMP( "uri",                  ML_URI )
+        ELSE_CMP( "vote",                 ML_VOTE )
+        ELSE_CMP( "year",                 ML_YEAR )
+        ELSE_CMP( "filesize",             ML_FILESIZE )
+        ELSE_CMP( "directory_id",         ML_DIRECTORY )
+        /* Album */
+        ELSE_CMP( "album_title",          ML_ALBUM )
+        ELSE_CMP( "album_id",             ML_ALBUM_ID )
+        ELSE_CMP( "album_cover",          ML_ALBUM_COVER )
+        /* People */
+        ELSE_CMP( "people_name",          ML_PEOPLE )
+        ELSE_CMP( "people_id",            ML_PEOPLE_ID )
+        ELSE_CMP( "people_role",          ML_PEOPLE_ROLE )
+        /* Extras */
+        ELSE_CMP( "language",             ML_LANGUAGE )
+        ELSE_CMP( "extra",                ML_EXTRA )
+        else
+        {
+            msg_Warn( p_ml, "unknown column: %s", res( 0, col ) );
+        }
+    }
+#undef ELSE_CMP
+#undef IF_CMP
+
+    /* 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 = GetMedia( p_ml, 0, ML_MEDIA, false );
+        if( !p_media )
+        {
+            free( indexes );
+            return VLC_ENOMEM;
+        }
+        p_result = ( ml_result_t * ) calloc( 1, sizeof( ml_result_t ) );
+        if( !p_result )
+        {
+            ml_gc_decref( p_media );
+            free( indexes );
+            return VLC_ENOMEM;
+        }
+
+        char* psz_append_pname = NULL;
+        char* psz_append_prole = NULL;
+        int i_append_pid = 0;
+
+        ml_LockMedia( p_media );
+        for( int col = 0; ( col < i_cols ) && ( i_ret == VLC_SUCCESS ); col++ )
+        {
+            switch( indexes[ col ] )
+            {
+            case ML_ALBUM_ID:
+                p_media->i_album_id = atoinull( res( row, col ) );
+                break;
+            case ML_ALBUM:
+                p_media->psz_album = strdupnull( res( row, col ) );
+                break;
+            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_COMMENT:
+                p_media->psz_comment = 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_DISC_NUMBER:
+                p_media->i_disc_number = atoinull( res( row, col ) );
+                break;
+            case ML_DURATION:
+                p_media->i_duration = atoinull( res( row, col ) );
+                break;
+            case ML_EXTRA:
+                p_media->psz_extra = strdupnull( res( row, col ) );
+                break;
+            case ML_FILESIZE:
+                p_media->i_filesize = atoinull( res( row, col ) );
+                break;
+            case ML_FIRST_PLAYED:
+                p_media->i_first_played = atoinull( res( row, col ) );
+                break;
+            case ML_GENRE:
+                p_media->psz_genre = 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_IMPORT_TIME:
+                p_media->i_import_time = atoinull( res( row, col ) );
+                break;
+            case ML_LANGUAGE:
+                p_media->psz_language = strdupnull( res( row, col ) );
+                break;
+            case ML_LAST_PLAYED:
+                p_media->i_last_played = atoinull( res( row, col ) );
+                break;
+            case ML_LAST_SKIPPED:
+                p_media->i_last_skipped = atoinull( res( row, col ) );
+                break;
+            case ML_ORIGINAL_TITLE:
+                p_media->psz_orig_title = strdupnull( res( row, col ) );
+                break;
+            case ML_PLAYED_COUNT:
+                p_media->i_played_count = atoinull( res( row, col ) );
+                break;
+            case ML_PREVIEW:
+                p_media->psz_preview = strdupnull( res( row, col ) );
+                break;
+            case ML_SCORE:
+                p_media->i_score = atoinull( res( row, col ) );
+                break;
+            case ML_SKIPPED_COUNT:
+                 p_media->i_skipped_count = atoinull( res ( row, col ) );
+                break;
+            case ML_TITLE:
+                p_media->psz_title = strdupnull( res( row, col ) );
+                break;
+            case ML_TRACK_NUMBER:
+                p_media->i_track_number = atoinull( res( row, col ) );
+                break;
+            case ML_TYPE:
+                p_media->i_type = atoinull( res( row, col ) );
+                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_VOTE:
+                p_media->i_vote = atoinull( res( row, col ) );
+                break;
+            case ML_YEAR:
+                p_media->i_year = atoinull( res( row, col ) );
+                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;
+            }
+        }
+
+        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 */
+        {
+            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 );
+            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 GetIdOfURI( 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_SET_DATABASE:
+    {
+        char *psz_host = (char *)va_arg( args, char * );
+        int i_port = (int)va_arg( args, int );
+        char *psz_user = (char *)va_arg( args, char * );
+        char *psz_pass = (char *)va_arg( args, char * );
+        return SetDatabase( p_ml, psz_host, i_port, psz_user, psz_pass );
+    }
+
+    case ML_INIT_DATABASE:
+        return InitDatabase( p_ml );
+
+    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 Set current database
+ *
+ * @param p_ml the media library object
+ * @param __psz_host DB host or filename if local file (SQLite...)
+ * @param port DB port
+ * @param psz_user DB user
+ * @param psz_pass DB password
+ * @return VLC_SUCCESS or vlc error code
+ */
+int SetDatabase( media_library_t *p_ml, const char *psz_host,
+                 int port, const char *psz_user, const char *psz_pass )
+{
+    assert( p_ml );
+
+    if( p_ml->p_sys->psz_host )
+    {
+        free( p_ml->p_sys->psz_host );
+        free( p_ml->p_sys->psz_user );
+        free( p_ml->p_sys->psz_password );
+        p_ml->p_sys->i_port = 0;
+    }
+
+    /* Select database name */
+    char *psz_dbhost = NULL;
+    if( !psz_host || !*psz_host )
+    {
+        psz_dbhost = config_GetPsz( p_ml, "ml-filename" );
+
+        /* 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 );
+        }
+    }
+    else
+    {
+        psz_dbhost = strdup( psz_host );
+    }
+
+    /* Preconfigure the database */
+    sql_SetDatabase( p_ml->p_sys->p_sql, psz_dbhost, port, psz_user, psz_pass );
+
+    p_ml->p_sys->psz_host     = psz_dbhost;
+    p_ml->p_sys->i_port       = port;
+    p_ml->p_sys->psz_user     = ( psz_user ? strdup( psz_user ) : NULL );
+    p_ml->p_sys->psz_password = ( psz_pass ? strdup( psz_pass ) : NULL );
+
+    return VLC_SUCCESS;
+}
+
+
+/**
+ * @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;
+
+    /* Add "unknown" entry to albums */
+    i_ret = QuerySimple( p_ml,
+                        "INSERT INTO album ( id, title, cover ) "
+                        "VALUES ( 0, 'Unknown', '' )" );
+    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;
+
+    /* 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,
+    "\nCREATE 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');\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');\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');\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');\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');\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');\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');\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');\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');\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');\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\";\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\";\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 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" );
+
+    /* Set the database if needed (arguments might be NULL or 0) */
+    SetDatabase( p_ml, p_ml->p_sys->psz_host,
+                 p_ml->p_sys->i_port, p_ml->p_sys->psz_user,
+                 p_ml->p_sys->psz_password );
+
+    int i_opened = sql_OpenDatabase( p_ml->p_sys->p_sql );
+    if( i_opened != VLC_SUCCESS )
+        return i_opened;
+
+    /* 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
+
+    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( select == ML_MEDIA || select == ML_MEDIA_SPARSE );
+    int i_ret = VLC_SUCCESS;
+    ml_media_t* p_media = NULL;
+    if( id == 0 )
+    {
+        p_media = ( ml_media_t* )calloc( 1, sizeof( ml_media_t ) );
+        vlc_gc_init( p_media, media_Destroy );
+        vlc_mutex_init( &p_media->lock );
+    }
+    else
+    {
+        if( !reload )
+        {
+            p_media = pool_GetMedia( p_ml, id );
+            if( !p_media )
+                reload = true;
+            else
+            {
+                ml_LockMedia( p_media );
+                if( p_media->b_sparse == true && select == ML_MEDIA )
+                    reload = true;
+                /* Utilise ML_MEDIA_EXTRA load? TODO */
+                ml_UnlockMedia( p_media );
+                ml_gc_incref( p_media );
+            }
+        }
+        if( reload )
+        {
+            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_ml, &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    = strdup( p_item->psz_uri );
+    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 )
+        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 p_ml The Media Library object
+ * @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( media_library_t *p_ml,
+                              input_item_t **pp_item,
+                              ml_media_t *p_media )
+{
+    playlist_t *p_pl = pl_Hold( p_ml );
+    *pp_item = input_item_New( VLC_OBJECT( p_pl ),
+                               p_media->psz_uri,
+                               p_media->psz_title );
+                               /* ITEM_TYPE_FILE ); */
+    pl_Release( p_ml );
+    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 GetIdOfInputItem( 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 = GetIdOfURI( p_ml, p_item->psz_uri );
+    }
+    return i_media_id;
+}
+
+/**
+ * @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.
+ */
+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
+ */
+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
+ */
+void Rollback( media_library_t* p_ml )
+{
+    sql_RollbackTransaction( p_ml->p_sys->p_sql );
+}
diff --git a/modules/media_library/sql_media_library.h b/modules/media_library/sql_media_library.h
new file mode 100644
index 0000000..11bc878
--- /dev/null
+++ b/modules/media_library/sql_media_library.h
@@ -0,0 +1,318 @@
+/*****************************************************************************
+ * sql_media_library.h : Media Library Interface
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN team
+ * $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@gmail.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
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc/vlc.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 <stdarg.h>
+#include <assert.h>
+
+#include "item_list.h"
+
+#ifdef HAVE_SYS_STAT_H
+#   include <sys/stat.h>
+#endif
+
+
+/*****************************************************************************
+ * 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
+{
+    /* SQL object */
+    sql_t *p_sql;
+
+    /* Monitoring thread */
+    monitoring_thread_t *p_mon;
+
+    /* Database description */
+    char *psz_host;
+    int  i_port;
+    char *psz_user;
+    char *psz_password;
+
+    /* Info on update/collection rebuilding */
+    bool b_updating;
+    bool b_rebuilding;
+
+    /* Watch thread */
+    watch_thread_t *p_watch;
+
+    /* Lock on the ML object */
+    vlc_mutex_t lock;
+
+    /* Holds all medias */
+    DECL_ARRAY( ml_media_t* ) mediapool;
+    ml_poolobject_t* p_mediapool[ ML_MEDIAPOOL_HASH_LENGTH ];
+    vlc_mutex_t pool_mutex;
+};
+
+/* Directory Monitoring thread */
+struct monitoring_thread_t
+{
+    VLC_COMMON_MEMBERS;
+
+    media_library_t *p_ml;
+    vlc_cond_t wait;
+    vlc_mutex_t lock;
+};
+
+/* Media status Watching thread */
+struct watch_thread_t
+{
+    VLC_COMMON_MEMBERS;
+    media_library_t *p_ml;
+    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;
+};
+
+
+
+/*****************************************************************************
+ * Function headers
+ *****************************************************************************/
+/* General functions */
+int SetDatabase( media_library_t *p_ml,
+                 const char *psz_host,
+                 int port,
+                 const char *psz_user,
+                 const char *psz_pass );
+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 GetIdOfInputItem( media_library_t *p_ml,
+                      input_item_t *p_item );
+int GetIdOfURI( 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 SetAlbumCover( 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 */
+int Begin( media_library_t* p_ml );
+void Commit( media_library_t* p_ml );
+void Rollback( media_library_t* p_ml );
+
+/****************************************************************************
+ * Scanning/monitoring functions
+ *****************************************************************************/
+void *RunMonitoringThread( vlc_object_t *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 );
+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 */
diff --git a/modules/media_library/sql_monitor.c b/modules/media_library/sql_monitor.c
new file mode 100644
index 0000000..34452b6
--- /dev/null
+++ b/modules/media_library/sql_monitor.c
@@ -0,0 +1,551 @@
+/*****************************************************************************
+ * sql_monitor.c: SQL-based media library: directory scanning and monitoring
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN team
+ * $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@gmail.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
+ *****************************************************************************/
+
+#include "sql_media_library.h"
+#include "vlc_playlist.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
+{
+    char* psz_uri;
+    int i_dir_id;
+    int i_mtime;
+    int i_update_id;
+    bool b_update;
+    monitoring_thread_t *p_mon;
+};
+
+/**
+ * @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 occured 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 occured 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 = utf8_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 )
+{
+    return strcoll( *a, *b );
+}
+
+/**
+ * @brief Directory Monitoring thread loop
+ */
+void *RunMonitoringThread( vlc_object_t *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, "Updating library" );
+
+    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( utf8_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,
+                    GetIdOfURI( p_ml, p_input->psz_uri ) );
+    vlc_event_detach( &p_input->event_manager, vlc_InputItemPreparsedChanged,
+                  PreparseComplete, p_itemobject );
+    vlc_gc_decref( p_input );
+}
+
+/**
+ * @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( utf8_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 = utf8_scandir( psz_dir, &pp_dir_content, NULL, Sort );
+    if( i_dir_content == -1 )
+    {
+        msg_Err( p_mon, "Cannot read `%s': %m", psz_dir );
+        free( psz_dir );
+        return;
+    }
+    else if( i_dir_content == 0 )
+    {
+        msg_Dbg( p_mon, "Nothing in directory `%s'", psz_dir );
+        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( utf8_stat( psz_uri, &s_stat ) == -1 )
+            {
+                msg_Err( p_mon, "%s: %m", psz_uri );
+                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;
+                    }
+                }
+
+                memmove( psz_uri + 7, psz_uri, sizeof( psz_uri ) -7 );
+                memcpy( psz_uri, "file://", 7 );
+
+                /* 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_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_uri,
+                         b_skip? 1 : 0 );
+                if( b_skip )
+                    continue;
+
+                p_input = input_item_New( VLC_OBJECT(p_mon), psz_uri,
+                                          psz_entry );
+
+                playlist_t* p_pl = pl_Hold( 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_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, false );
+                pl_Release( p_mon );
+            }
+            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] == false )
+        {
+            /* 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 );
+}
diff --git a/modules/media_library/sql_search.c b/modules/media_library/sql_search.c
new file mode 100644
index 0000000..1740816
--- /dev/null
+++ b/modules/media_library/sql_search.c
@@ -0,0 +1,1140 @@
+/*****************************************************************************
+ * sql_search.c: SQL-based media library: all find/get functions
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN team
+ * $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@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "sql_media_library.h"
+
+int Find( media_library_t *p_ml, vlc_array_t *p_result_array, ... )
+{
+    va_list args;
+    int returned;
+
+    va_start( args, p_result_array );
+    returned = FindVa( p_ml, p_result_array, args );
+    va_end( args );
+
+    return returned;
+}
+
+/**
+ * @brief Generic find in Media Library, returns arrays of psz or int
+ *
+ * @param p_ml the media library object
+ * @param result A pointer to a result array
+ * @param criterias list of criterias used in SELECT
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+int FindVa( media_library_t *p_ml,
+            vlc_array_t *p_result_array, va_list criterias )
+{
+    int i_ret = VLC_SUCCESS;
+    char *psz_query;
+    ml_result_type_e result_type;
+    char **pp_results = NULL;
+    int i_cols, i_rows;
+
+    if( !p_result_array )
+        return VLC_EGENERIC;
+
+    i_ret = BuildSelectVa( p_ml, &psz_query, &result_type, criterias );
+    if( i_ret != VLC_SUCCESS )
+        return i_ret;
+
+    if( Query( p_ml, &pp_results, &i_rows, &i_cols, psz_query )
+        != VLC_SUCCESS )
+    {
+        msg_Err( p_ml, "Error occured while making the query to the database" );
+        return VLC_EGENERIC;
+    }
+
+    i_ret = SQLToResultArray( p_ml, p_result_array, pp_results, i_rows, i_cols,
+                              result_type );
+
+    free( psz_query);
+    FreeSQLResult( p_ml, pp_results );
+
+    return i_ret;
+}
+
+/**
+ * @brief Generic find in Media Library, returns arrays of psz or int
+ *
+ * @param p_ml the media library object
+ * @param result a pointer to a result array
+ * @param selected_type the type of the element we're selecting
+ * @param criterias list of criterias used in SELECT
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+
+int FindAdv( media_library_t *p_ml, vlc_array_t *p_result_array,
+             ml_select_e selected_type, const char* psz_lvalue, ml_ftree_t *tree )
+{
+    int i_ret = VLC_SUCCESS;
+    char *psz_query;
+    ml_result_type_e result_type;
+    char **pp_results = NULL;
+    int i_cols, i_rows;
+
+    if( !p_result_array )
+        return VLC_EGENERIC;
+
+    i_ret = BuildSelect( p_ml, &psz_query, &result_type, psz_lvalue,
+                         selected_type, tree );
+
+    if( i_ret != VLC_SUCCESS )
+        return i_ret;
+
+    if( Query( p_ml, &pp_results, &i_rows, &i_cols, psz_query )
+        != VLC_SUCCESS )
+    {
+        msg_Err( p_ml, "Error occured while making the query to the database" );
+        return VLC_EGENERIC;
+    }
+
+    i_ret = SQLToResultArray( p_ml, p_result_array, pp_results, i_rows, i_cols,
+                              result_type );
+
+    free( psz_query);
+    FreeSQLResult( p_ml, pp_results );
+
+    return i_ret;
+}
+
+/**
+ * @brief Generic SELECT query builder with va_list parameter
+ *
+ * @param p_ml This media_library_t object
+ * @param ppsz_query *ppsz_query will contain query
+ * @param p_result_type see enum ml_result_type_e
+ * @param criterias list of criterias used in SELECT
+ * @return VLC_SUCCESS or a VLC error code
+ * NOTE va_list criterias must end with ML_END or this will fail (segfault)
+ *
+ * This function handles results of only one column (or two if ID is included),
+ * of 'normal' types: int and strings
+ */
+int BuildSelectVa( media_library_t *p_ml, char **ppsz_query,
+                   ml_result_type_e *p_result_type, va_list criterias )
+{
+    int i_continue = 1;
+    ml_ftree_t* p_ftree = NULL;
+    char* psz_lvalue = NULL;
+
+    /* Get the name of the data we want */
+    ml_select_e selected_type = va_arg( criterias, int );
+    if( selected_type == ML_PEOPLE || selected_type == ML_PEOPLE_ID ||
+            selected_type == ML_PEOPLE_ROLE )
+        psz_lvalue = va_arg( criterias, char * );
+
+    /* Loop on every arguments */
+    while( i_continue )
+    {
+        ml_ftree_t *p_find = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
+        if( !p_find )
+            return VLC_ENOMEM;
+        p_find->criteria = va_arg( criterias, int );
+        p_find->comp = ML_COMP_EQUAL;
+        switch( p_find->criteria )
+        {
+            case ML_SORT_ASC:
+                p_ftree = ml_FtreeSpecAsc( p_ftree, va_arg( criterias, char* ) ); break;
+            case ML_SORT_DESC:
+                p_ftree = ml_FtreeSpecDesc( p_ftree, va_arg( criterias, char* ) ); break;
+            case ML_DISTINCT:
+                p_ftree = ml_FtreeSpecDistinct( p_ftree ); break;
+            case ML_LIMIT:
+                p_ftree = ml_FtreeSpecLimit( p_ftree, va_arg( criterias, int ) );
+                break;
+            case ML_ARTIST:
+                /* This is OK because of a shallow free find */
+                p_find->lvalue.str = ML_PERSON_ARTIST;
+                p_find->value.str = va_arg( criterias, char* );
+                p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
+                break;
+            case ML_PEOPLE:
+                p_find->lvalue.str = va_arg( criterias, char* );
+                p_find->value.str = va_arg( criterias, char* );
+                p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
+                break;
+            case ML_PEOPLE_ID:
+                p_find->lvalue.str = va_arg( criterias, char* );
+                p_find->value.i = va_arg( criterias, int );
+                p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
+                break;
+            case ML_END:
+                i_continue = 0;
+                break;
+            default:
+                switch( ml_AttributeIsString( p_find->criteria ) )
+                {
+                    case 0:
+                        p_find->value.i = va_arg( criterias, int );
+                        break;
+                    case 1:
+                        p_find->value.str = va_arg( criterias, char* );
+                        break;
+                }
+                p_ftree = ml_FtreeFastAnd( p_ftree, p_find );
+                break;
+        }
+    }
+
+    int i_ret = BuildSelect( p_ml, ppsz_query, p_result_type, psz_lvalue,
+                             selected_type, p_ftree );
+
+    ml_ShallowFreeFindTree( p_ftree );
+    return i_ret;
+}
+
+/**
+ * @brief Append a string and format it using SQL vmprintf
+ **/
+static int AppendStringFmtVa( media_library_t *p_ml,
+                              char **ppsz_dst, const char *psz_fmt,
+                              va_list args )
+{
+    char *psz_tmp = NULL, *psz_fullexp = NULL;
+
+    assert( ppsz_dst != NULL );
+
+    if( !( *ppsz_dst ) )
+    {
+        /* New expression */
+        *ppsz_dst = sql_VPrintf( p_ml->p_sys->p_sql, psz_fmt, args );
+        if( !( *ppsz_dst ) )
+            return VLC_ENOMEM;
+    }
+    else
+    {
+        /* Create new expression B */
+        psz_tmp = sql_VPrintf( p_ml->p_sys->p_sql, psz_fmt, args );
+        if( !psz_tmp )
+            return VLC_ENOMEM;
+
+        if( asprintf( &psz_fullexp, "%s%s", *ppsz_dst, psz_tmp ) == -1 )
+        {
+            free( psz_tmp );
+            return VLC_ENOMEM;
+        }
+
+        free( *ppsz_dst );
+        *ppsz_dst = psz_fullexp;
+    }
+
+    return VLC_SUCCESS;
+}
+
+static int AppendStringFmt( media_library_t *p_ml,
+                            char **ppsz_dst, const char *psz_fmt, ... )
+{
+    va_list args;
+    va_start( args, psz_fmt );
+    int i_ret = AppendStringFmtVa( p_ml, ppsz_dst, psz_fmt, args );
+    va_end( args );
+    return i_ret;
+}
+
+/**
+ * @brief Helper for BuildSelect: append an expression to a WHERE clause
+ * @param p_ml This media_library_t object
+ * @param ppsz_where Part of a WHERE expression (may point to NULL)
+ * @param psz_fmt Format string (SQL printf)
+ * @param ... Variable argument list
+ * @private
+ *
+static int AppendWhere( media_library_t *p_ml,
+                        char **ppsz_where, const char *psz_fmt, ... )
+{
+    int i_ret;
+    va_list args;
+    va_start( args, psz_fmt );
+    if( !( *ppsz_where ) )
+    {
+        i_ret = AppendStringFmtVa( p_ml, ppsz_where, psz_fmt, args );
+    }
+    else
+    {
+        i_ret = AppendStringFmt( p_ml, ppsz_where, " OR " );
+        if( i_ret != VLC_SUCCESS )
+            goto exit;
+        i_ret = AppendStringFmtVa( p_ml, ppsz_where, psz_fmt, args );
+    }
+exit:
+    va_end( args );
+    return i_ret;
+}*/
+
+
+/* Early Declaration of Where String Generator */
+static int BuildWhere( media_library_t* p_ml, char **ppsz_where, ml_ftree_t* tree,
+       char** sort, int* limit, const char** distinct, char*** pppsz_frompersons,
+       int* i_frompersons, int* join );
+
+#   define table_media             (1 << 0)
+#   define table_album             (1 << 1)
+#   define table_people            (1 << 2)
+#   define table_extra             (1 << 3)
+
+static void PackFromPersons( char*** pppsz_frompersons, int i_num_frompersons )
+{
+    for( int i = 0; i < i_num_frompersons; i++ )
+    {
+        if( *pppsz_frompersons[i] == NULL )
+            continue;
+        for( int j = i+1; j < i_num_frompersons; j++ )
+        {
+            if( strcmp( *pppsz_frompersons[i], *pppsz_frompersons[j] ) == 0 )
+            {
+                *pppsz_frompersons[j] = NULL;
+            }
+        }
+    }
+}
+/**
+ * @brief Generic SELECT query builder
+ *
+ * @param p_ml This media_library_t object
+ * @param ppsz_query *ppsz_query will contain query
+ * @param p_result_type see enum ml_result_type_e
+ * @param selected_type the type of the element we're selecting
+ * @param tree the find tree
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+int BuildSelect( media_library_t *p_ml,
+                 char **ppsz_query, ml_result_type_e *p_result_type,
+                 const char *psz_selected_type_lvalue, ml_select_e selected_type,
+                 ml_ftree_t *tree )
+{
+    /* Basic verification */
+    if( !ppsz_query )
+        return VLC_EGENERIC;
+
+    int i_ret = VLC_SUCCESS;
+    char *psz_query = NULL;
+
+    /* Building psz_query :
+    psz_query = "SELECT psz_distinct psz_select
+                 FROM psz_from [JOIN psz_join ON psz_on]
+                 [JOIN psz_join2 ON psz_on2]
+                 [WHERE psz_where[i] [AND psz_where[j] ...]]
+                 [LIMIT psz_limit] [ORDER BY psz_select psz_sort] ;"
+    */
+    char *psz_select             = NULL;
+    const char *psz_distinct     = ""; /* "DISTINCT" or "" */
+
+    /* FROM */
+    char *psz_from               = NULL;
+    int i_from                   = 0; /* Main select table */
+
+    char **ppsz_frompersons      = NULL;
+    int  i_num_frompersons       = 0;
+    char *psz_peoplerole         = NULL; /* Person to get selected */
+
+    /* JOIN ... ON ... */
+    char *psz_join               = NULL;
+    char *psz_join2              = NULL;
+    char *psz_on                 = NULL;
+    char *psz_on2                = NULL;
+    int i_join                   = 0;    /* Tables that need to be joined */
+
+    /* String buffers */
+    char *psz_where              = NULL;
+    char *psz_sort               = NULL; /* ASC or DESC or NULL */
+    char *psz_tmp                = NULL;
+
+    int i_limit                  = 0;
+
+    /* Build the WHERE condition */
+    BuildWhere( p_ml, &psz_where, tree, &psz_sort, &i_limit,
+            &psz_distinct, &ppsz_frompersons, &i_num_frompersons, &i_join );
+
+    PackFromPersons( &ppsz_frompersons, i_num_frompersons );
+
+    /* What is the result type? */
+    ml_result_type_e res_type   = ML_TYPE_PSZ;
+
+    /* SELECT, FROM */
+    /* Note that a DISTINCT select makes id of result non sense */
+    switch( selected_type )
+    {
+    case ML_ALBUM:
+        psz_select   = ( !*psz_distinct ) ?
+                        strdup( "album.id, album.title AS album_title" )
+                        : strdup( "album.title AS album_title" );
+        i_from       = table_album;
+        break;
+    case ML_ALBUM_COVER:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "album.id, album.cover" ) : strdup( "album.cover" );
+        i_from       = table_album;
+        break;
+    case ML_ALBUM_ID:
+        psz_select   = strdup( "album.id" );
+        psz_distinct = "DISTINCT";
+        i_from       = table_album;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_ARTIST:
+        psz_select   = ( !*psz_distinct ) ?
+                        strdup( "people_Artist.id, people_Artist.name" )
+                        : strdup( "people_Artist.name" );
+        i_from       = table_people;
+        psz_peoplerole = strdup( ML_PERSON_ARTIST );
+        break;
+    case ML_ARTIST_ID:
+        psz_select   = strdup( "people_Artist.id" );
+        psz_distinct = "DISTINCT";
+        i_from       = table_people;
+        res_type     = ML_TYPE_INT;
+        psz_peoplerole = strdup( ML_PERSON_ARTIST );
+        break;
+    case ML_COVER:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "media.id, media.cover" ) : strdup( "media.cover" );
+        i_from       = table_media;
+        break;
+    case ML_COMMENT:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "media.id, extra.comment" ) : strdup( "extra.comment" );
+        i_from       = table_extra;
+        break;
+    case ML_GENRE:
+        psz_select   = ( !*psz_distinct ) ?
+               strdup( "media.id, media.genre" ) : strdup( "media.genre" );
+        i_from       = table_media;
+        break;
+    case ML_COUNT_MEDIA:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "COUNT()" ) : strdup( "COUNT( DISTINCT media.id )" );
+        i_from       = table_media;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_COUNT_ALBUM:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "COUNT()" ) : strdup( "COUNT( DISTINCT album.id )" );
+        i_from       = table_album;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_COUNT_PEOPLE:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "COUNT()" ) : strdup( "COUNT( DISTINCT people.id )" );
+        i_from       = table_people;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_FILESIZE:
+        psz_select   = strdup( "media.filesize" );
+        i_from       = table_media;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_ID:
+        psz_select   = strdup( "media.id" ); /* ID: must be distinct */
+        psz_distinct = "DISTINCT";
+        i_from       = table_media;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_LANGUAGE:
+        psz_select   = strdup( "extra.language" );
+        psz_distinct = "DISTINCT";
+        i_from       = table_extra;
+        break;
+    case ML_MEDIA_SPARSE:
+        i_ret = AppendStringFmt( p_ml, &psz_select, "media.id AS id,"
+                "media.uri AS uri,"
+                "media.type AS type,"
+                "media.title AS title,"
+                "media.duration AS duration,"
+                "media.original_title AS original_title,"
+                "media.album_id AS album_id,"
+                "media.cover AS cover,"
+                "media.preview AS preview,"
+                "media.disc AS disc,"
+                "media.track AS track,"
+                "media.year AS year,"
+                "media.genre AS genre,"
+                "media.played_count AS played_count,"
+                "media.last_played AS last_played,"
+                "media.first_played AS first_played,"
+                "media.import_time AS import_time,"
+                "media.skipped_count AS skipped_count,"
+                "media.last_skipped AS last_skipped,"
+                "media.vote AS vote,"
+                "media.score AS score,"
+                "media.comment AS comment,"
+                "media.filesize AS filesize,"
+                "album.title AS album_title,"
+                "album.cover AS album_cover,"
+        "(SELECT name FROM media_to_people JOIN people "
+        "ON (people_id = id) WHERE media_id = media.id AND role = %Q LIMIT 1) AS people_%s",
+        ML_PERSON_ARTIST, ML_PERSON_ARTIST );
+        if( i_ret != VLC_SUCCESS )
+            goto exit;
+        i_from       = table_media;
+        i_join      |= ( table_album | table_people );
+        psz_distinct = "DISTINCT";
+        res_type     = ML_TYPE_MEDIA;
+        break;
+    case ML_MEDIA:
+        /* Who said this was over-complicated ?? */
+        /* Yea right. */
+        psz_select   = strdup( "media.id AS id,"
+                "media.uri AS uri,"
+                "media.type AS type,"
+                "media.title AS title,"
+                "media.duration AS duration,"
+                "media.original_title AS original_title,"
+                "media.album_id AS album_id,"
+                "media.cover AS cover,"
+                "media.preview AS preview,"
+                "media.disc as disc,"
+                "media.track AS track,"
+                "media.year AS year,"
+                "media.genre AS genre,"
+                "media.played_count AS played_count,"
+                "media.last_played AS last_played,"
+                "media.first_played AS first_played,"
+                "media.import_time AS import_time,"
+                "media.last_skipped AS last_skipped,"
+                "media.skipped_count AS skipped_count,"
+                "media.vote AS vote,"
+                "media.score AS score,"
+                "media.comment AS comment,"
+                "media.filesize AS filesize,"
+                "album.title AS album_title,"
+                "album.cover AS album_cover,"
+                "people.id AS people_id,"
+                "people.name AS people_name,"
+                "people.role AS people_role,"
+                "extra.language AS language,"
+                "extra.extra AS extra" );
+        i_from       = table_media;
+        i_join      |= ( table_album | table_people | table_extra );
+        psz_distinct = "DISTINCT";
+        res_type     = ML_TYPE_MEDIA;
+        break;
+    case ML_MEDIA_EXTRA:
+        psz_select = strdup( "media.id AS id,"
+                "people.id AS people_id,"
+                "people.name AS people_name,"
+                "people.role AS people_role,"
+                "extra.extra AS extra,"
+                "extra.language AS language" );
+        i_from       = table_media;
+        i_join      |= ( table_album | table_people | table_extra );
+        psz_distinct = "DISTINCT";
+        res_type     = ML_TYPE_MEDIA;
+        break;
+    case ML_ORIGINAL_TITLE:
+        psz_select   = ( !*psz_distinct ) ?
+               strdup( "media.id, media.original_title" ) : strdup( "media.original_title" );
+        i_from       = table_media;
+        break;
+    /* For people, if lvalue = "", then we want ANY people. */
+    case ML_PEOPLE:
+        assert( psz_selected_type_lvalue );
+        i_ret = AppendStringFmt( p_ml, &psz_select, "people%s%s.name",
+           *psz_selected_type_lvalue ? "_" : "",
+           *psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
+        if( i_ret != VLC_SUCCESS )
+            goto exit;
+        if( *psz_distinct )
+        {
+            i_ret = AppendStringFmt( p_ml, &psz_select, ", people%s%s.name",
+               *psz_selected_type_lvalue ? "_" : "",
+               *psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
+            if( i_ret != VLC_SUCCESS )
+                goto exit;
+        }
+        i_from         = table_people;
+        psz_peoplerole = strdup( psz_selected_type_lvalue );
+        break;
+    case ML_PEOPLE_ID:
+        assert( psz_selected_type_lvalue );
+        i_ret = AppendStringFmt( p_ml, &psz_select, "people%s%s.id",
+           *psz_selected_type_lvalue ? "_" : "",
+           *psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
+        if( i_ret != VLC_SUCCESS )
+            goto exit;
+        if( *psz_distinct )
+        {
+            i_ret = AppendStringFmt( p_ml, &psz_select, ", people%s%s.id",
+               *psz_selected_type_lvalue ? "_" : "",
+               *psz_selected_type_lvalue ? psz_selected_type_lvalue : "" );
+            if( i_ret != VLC_SUCCESS )
+                goto exit;
+        }
+        psz_distinct   = "DISTINCT";
+        i_from         = table_people;
+        psz_peoplerole = strdup( psz_selected_type_lvalue );
+        res_type       = ML_TYPE_INT;
+        break;
+    case ML_PEOPLE_ROLE:
+        psz_select   = strdup( "people.role" );
+        psz_distinct = "DISTINCT";
+        i_from       = table_people;
+        break;
+    case ML_TITLE:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "media.id, media.title" ) : strdup( "media.title" );
+        i_from       = table_media;
+        break;
+    case ML_TYPE:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "media.id, media.type" ): strdup( "media.type" );
+        i_from       = table_media;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_URI:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "media.id, media.uri" ) : strdup( "media.uri" );
+        i_from       = table_media;
+        break;
+    case ML_VOTE:
+        psz_select   = ( !*psz_distinct ) ?
+                strdup( "media.id, media.vote" ) : strdup( "media.vote" );
+        i_from       = table_media;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_YEAR:
+        psz_select   = ( !*psz_distinct ) ?
+               strdup( "media.id, media.year" ) : strdup( "media.year" );
+        i_from       = table_media;
+        res_type     = ML_TYPE_INT;
+        break;
+    case ML_LIMIT:
+    case ML_SORT_DESC:
+    case ML_SORT_ASC:
+    case ML_END:
+    default:
+        msg_Dbg( p_ml, "unknown select (%d) in BuildSelect", selected_type );
+        return VLC_EGENERIC;
+    }
+
+    /* Let's build full psz_query ! */
+    i_ret = VLC_SUCCESS;
+
+    /* Figure out select and join tables */
+    switch( i_from )
+    {
+        case table_media:
+            break;
+        case table_album:
+            switch( i_join )
+            {
+                case 0: break;
+                case 2: i_join = 0; break;
+                case 1:
+                case 3: i_from = table_media; i_join = table_album; break;
+                case 4:
+                case 5:
+                case 6:
+                case 7: i_from = table_media; i_join = table_album | table_people; break;
+                case 8:
+                case 9:
+                case 10:
+                case 11: i_from = table_media; i_join = table_extra | table_album; break;
+                case 12:
+                case 13:
+                case 14:
+                case 15: i_from = table_media; i_join = table_extra | table_album | table_people; break;
+                default: break;
+            }
+            break;
+        case table_people:
+            switch( i_join )
+            {
+                case 0: break;
+                case 1: i_from = table_media; i_join = table_people; break;
+                case 2:
+                case 3: i_from = table_media; i_join = table_album | table_people; break;
+                case 4:
+                    /* Determine if a join from media is required */
+                    if( i_num_frompersons > 1 )
+                        i_from = table_media;
+                    else
+                        i_join = 0;
+                    break;
+                case 5: i_from = table_media; i_join = table_people; break;
+                case 6:
+                case 7: i_from = table_media; i_join = table_album | table_people; break;
+                case 8:
+                case 9: i_from = table_media; i_join = table_people | table_extra; break;
+                case 10:
+                case 11: i_from = table_media; i_join = table_people | table_album | table_extra; break;
+                case 12:
+                case 13: i_from = table_media; i_join = table_people | table_extra; break;
+                case 14:
+                case 15: i_from = table_media; i_join = table_people | table_album | table_extra; break;
+                default: break;
+            }
+            break;
+        case table_extra:
+            switch( i_join )
+            {
+                case 0: break;
+                case 1: i_from = table_media; i_join = table_extra; break;
+                case 2:
+                case 3: i_from = table_media; i_join = table_extra | table_album; break;
+                case 4:
+                case 5: i_from = table_media; i_join = table_extra | table_people; break;
+                case 6:
+                case 7: i_from = table_media; i_join = table_extra | table_people | table_album; break;
+                case 8: i_from = table_extra; i_join = 0; break;
+                case 9: i_from = table_media; i_join = table_extra; break;
+                case 10:
+                case 11: i_from = table_media; i_join = table_extra | table_album; break;
+                case 12:
+                case 13: i_from = table_media; i_join = table_extra | table_people; break;
+                case 14:
+                case 15: i_from = table_media; i_join = table_extra | table_people | table_album; break;
+                default: break;
+            }
+            break;
+        default: msg_Warn( p_ml, "You can't be selecting from this table!!" );
+                 i_ret = VLC_EGENERIC;
+                 goto exit;
+    }
+
+    assert( !( i_from & table_album && i_join & table_album ) );
+    assert( !( i_from & table_people && i_join & table_people ) );
+    assert( !( i_from & table_extra && i_join & table_extra ) );
+
+    /* Generate FROM - psz_from */
+    if( i_from == table_media )
+        i_ret = AppendStringFmt( p_ml, &psz_from, "media" );
+    else if( i_from == table_album )
+        i_ret = AppendStringFmt( p_ml, &psz_from, "album" );
+    else if( i_from == table_extra )
+        i_ret = AppendStringFmt( p_ml, &psz_from, "extra" );
+    else if( i_from == table_people )
+    {
+        i_ret = AppendStringFmt( p_ml, &psz_from, "people AS people%s%s",
+                psz_peoplerole ? "_" : "", psz_peoplerole );
+        if( i_ret < 0 ) goto exit;
+
+        /* The ugly next statement is only required if persons are being
+         * selected. Otherwise the joins will handle this */
+        if( psz_peoplerole && *psz_peoplerole )
+        {
+            i_ret = AppendStringFmt( p_ml, &psz_where, "%s people_%s.role = %Q ",
+                                     ( psz_where && *psz_where ) ? " AND" : "",
+                                     psz_peoplerole, psz_peoplerole );
+            if( i_ret < 0 ) goto exit;
+        }
+    }
+    if( i_ret < 0 ) goto exit;
+
+    i_ret = AppendStringFmt( p_ml, &psz_query,
+                             "SELECT %s %s ", psz_distinct, psz_select );
+    if( i_ret < 0 ) goto exit;
+
+    i_ret = AppendStringFmt( p_ml, &psz_query, "FROM %s ", psz_from );
+    if( i_ret < 0 ) goto exit;
+
+    /* Create join conditions */
+    if( i_join & table_people )
+    {
+        /* we can join psz_peoplerole safely because
+         * if i_join = people, then i_from != people */
+        bool join = true;
+        for( int i = 0; i < i_num_frompersons ; i++ )
+        {
+            /* We assume ppsz_frompersons has unique entries and
+             * if ppsz_frompersons[i] is empty(but not NULL), then it
+             * means we accept any role */
+            if( ppsz_frompersons[i] && *ppsz_frompersons[i] )
+            {
+                if( strcmp( psz_peoplerole, ppsz_frompersons[i] ) == 0 )
+                    join = false;
+                AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS people_%sx ",
+                        psz_join == NULL ? "" : ",", ppsz_frompersons[i] );
+                /* This is possible because from is usually the media table */
+                AppendStringFmt( p_ml, &psz_on, "%speople_%sx.media_id = media.id ",
+                        psz_on == NULL ? "" : " AND ", ppsz_frompersons[i] );
+                AppendStringFmt( p_ml, &psz_join2, "%speople AS people_%s ",
+                        psz_join2 == NULL ? "" : ",", ppsz_frompersons[i] );
+                AppendStringFmt( p_ml, &psz_on2, "%s ( people_%sx.people_id = people_%s.id AND "
+                        "people_%s.role = %Q )", psz_on2 == NULL ? "" : " AND ",
+                        ppsz_frompersons[i], ppsz_frompersons[i],
+                        ppsz_frompersons[i], ppsz_frompersons[i]  );
+            }
+            else if( ppsz_frompersons[i] )
+            {
+                if( strcmp( psz_peoplerole, ppsz_frompersons[i] ) == 0 )
+                    join = false;
+                AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS peoplex ",
+                        psz_join == NULL ? "" : "," );
+                /* This is possible because from is usually the media table */
+                AppendStringFmt( p_ml, &psz_on, "%speoplex.media_id = media.id ",
+                        psz_on == NULL ? "" : " AND " );
+                AppendStringFmt( p_ml, &psz_join2, "%speople AS people ",
+                        psz_join2 == NULL ? "" : "," );
+                AppendStringFmt( p_ml, &psz_on2, "%s peoplex.people_id = people.id",
+                        psz_on2 == NULL ? "" : " AND " );
+            }
+        }
+        if( join == true )
+        {
+            if( psz_peoplerole && *psz_peoplerole )
+            {
+                AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS people_%sx ",
+                        psz_join == NULL ? "" : ",", psz_peoplerole );
+                /* This is possible because from is always the media table */
+                AppendStringFmt( p_ml, &psz_on, "%speople_%sx.media_id = media.id ",
+                        psz_on == NULL ? "" : " AND ", psz_peoplerole );
+                AppendStringFmt( p_ml, &psz_join2, "%speople AS people_%s ",
+                        psz_join2 == NULL ? "" : ",", psz_peoplerole );
+                AppendStringFmt( p_ml, &psz_on2, "%s ( people_%sx.people_id = people_%s.id AND "
+                        "people_%s.role = %Q )", psz_on2 == NULL ? "" : " AND ",
+                        psz_peoplerole, psz_peoplerole,
+                        psz_peoplerole, psz_peoplerole );
+            }
+            else
+            {
+                AppendStringFmt( p_ml, &psz_join, "%smedia_to_people AS peoplex ",
+                        psz_join == NULL ? "" : "," );
+                /* This is possible because from is usually the media table */
+                AppendStringFmt( p_ml, &psz_on, "%speoplex.media_id = media.id ",
+                        psz_on == NULL ? "" : " AND " );
+                AppendStringFmt( p_ml, &psz_join2, "%speople ",
+                        psz_join2 == NULL ? "" : "," );
+                AppendStringFmt( p_ml, &psz_on2, "%s peoplex.people_id = people.id",
+                        psz_on2 == NULL ? "" : " AND " );
+
+            }
+        }
+    }
+    if( i_join & table_album )
+    {
+        AppendStringFmt( p_ml, &psz_join, "%salbum", psz_join == NULL ? "" : "," );
+        AppendStringFmt( p_ml, &psz_on, "%s album.id = media.album_id ",
+                psz_on == NULL ? "" : " AND " );
+    }
+    if( i_join & table_extra )
+    {
+        AppendStringFmt( p_ml, &psz_join, "%sextra", psz_join == NULL ? "" : "," );
+        AppendStringFmt( p_ml, &psz_on, "%s extra.id = media.id ",
+                psz_on == NULL ? "" : " AND " );
+    }
+
+    /* Complete the join clauses */
+    if( psz_join )
+    {
+        AppendStringFmt( p_ml, &psz_query,
+                         "JOIN %s ON %s ", psz_join, psz_on );
+    }
+    if( psz_join2 )
+    {
+        AppendStringFmt( p_ml, &psz_query,
+                         "JOIN %s ON %s ", psz_join2, psz_on2 );
+    }
+    if( psz_where && *psz_where )
+    {
+        AppendStringFmt( p_ml, &psz_query,
+                         "WHERE %s ", psz_where );
+    }
+    /* TODO: FIXME: Limit on media objects doesn't work! */
+    if( i_limit )
+    {
+        AppendStringFmt( p_ml, &psz_query,
+                         "LIMIT %d ", i_limit );
+    }
+
+    if( psz_sort )
+    {
+        AppendStringFmt( p_ml, &psz_query,
+                         "ORDER BY %s %s", psz_select, psz_sort );
+    }
+
+    if( i_ret > 0 ) i_ret = VLC_SUCCESS;
+
+    if( p_result_type ) *p_result_type = res_type;
+    if( !psz_query )    i_ret = VLC_EGENERIC;
+    else                *ppsz_query = strdup( psz_query );
+
+exit:
+    free( psz_query );
+    free( psz_where );
+    free( psz_tmp   );
+    free( psz_from  );
+    free( psz_join  );
+    free( psz_select );
+    free( psz_join2 );
+    free( psz_on    );
+    free( psz_on2   );
+    free( psz_peoplerole );
+    free( ppsz_frompersons );
+
+    if( i_ret != VLC_SUCCESS )
+        msg_Warn( p_ml, "an unknown error occured (%d)", i_ret );
+
+    return i_ret;
+}
+
+#undef CASE_INT
+#define CASE_INT( casestr, fmt, table )                                     \
+case casestr:                                                               \
+assert( tree->comp != ML_COMP_HAS && tree->comp != ML_COMP_STARTS_WITH      \
+        && tree->comp != ML_COMP_ENDS_WITH );                               \
+*ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "%s %s %d", fmt,              \
+    tree->comp == ML_COMP_LESSER ? "<" :                                    \
+    tree->comp == ML_COMP_LESSER_OR_EQUAL ? "<=" :                          \
+    tree->comp == ML_COMP_GREATER ? ">" :                                   \
+    tree->comp == ML_COMP_GREATER_OR_EQUAL ? ">=" : "=", tree->value.i );   \
+if( *ppsz_where == NULL )                                                   \
+    goto parsefail;                                                         \
+*join |= table;                                                             \
+break
+
+#undef CASE_PSZ
+#define CASE_PSZ( casestr, fmt, table )                                       \
+case casestr:                                                                 \
+    assert( tree->comp == ML_COMP_HAS || tree->comp == ML_COMP_EQUAL          \
+        || tree->comp == ML_COMP_STARTS_WITH                                  \
+        || tree->comp == ML_COMP_ENDS_WITH );                                 \
+    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "%s LIKE '%s%q%s'", fmt,    \
+        tree->comp == ML_COMP_HAS                                             \
+        || tree->comp == ML_COMP_STARTS_WITH? "%%" : "",                      \
+            tree->value.str,                                                  \
+        tree->comp == ML_COMP_HAS                                             \
+        || tree->comp == ML_COMP_ENDS_WITH? "%%" : "" );                      \
+    if( *ppsz_where == NULL )                                                 \
+        goto parsefail;                                                       \
+    *join |= table;                                                           \
+    break
+
+#define SLDPJ sort, limit, distinct, pppsz_frompersons, i_frompersons, join
+static int BuildWhere( media_library_t* p_ml, char **ppsz_where, ml_ftree_t* tree,
+       char** sort, int* limit, const char** distinct,
+       char*** pppsz_frompersons, int* i_frompersons, int* join )
+{
+    assert( ppsz_where && sort && distinct );
+    if( !tree ) /* Base case */
+    {
+        return VLC_SUCCESS;
+    }
+
+    int i_ret = VLC_EGENERIC;
+    char* psz_left = NULL;
+    char* psz_right = NULL;
+
+    switch( tree->op )
+    {
+        case ML_OP_AND:
+        case ML_OP_OR:
+            i_ret = BuildWhere( p_ml, &psz_left, tree->left, SLDPJ );
+            if( i_ret != VLC_SUCCESS )
+                goto parsefail;
+            i_ret = BuildWhere( p_ml, &psz_right, tree->right, SLDPJ );
+            if( i_ret != VLC_SUCCESS )
+                goto parsefail;
+            if( psz_left == NULL || psz_right == NULL )
+            {
+                msg_Err( p_ml, "Parsing failed for AND/OR" );
+                i_ret = VLC_EGENERIC;
+                goto parsefail;
+            }
+            if( asprintf( ppsz_where, "( %s %s %s )", psz_left,
+                ( tree->op == ML_OP_AND ? "AND" : "OR" ), psz_right ) == -1 )
+            {
+                i_ret = VLC_ENOMEM;
+                goto parsefail;
+            }
+            break;
+        case ML_OP_NOT:
+            i_ret = BuildWhere( p_ml, &psz_left, tree->left, SLDPJ );
+            if( i_ret != VLC_SUCCESS )
+                goto parsefail;
+            if( psz_left == NULL )
+            {
+                msg_Err( p_ml, "Parsing failed at NOT" );
+                i_ret = VLC_EGENERIC;
+                goto parsefail;
+            }
+            if( asprintf( ppsz_where, "( NOT %s )", psz_left ) == -1 )
+            {
+                i_ret = VLC_ENOMEM;
+                goto parsefail;
+            }
+            break;
+        case ML_OP_SPECIAL:
+            i_ret = BuildWhere( p_ml, &psz_right, tree->right, SLDPJ );
+            if( i_ret != VLC_SUCCESS )
+                goto parsefail;
+            i_ret = BuildWhere( p_ml, &psz_left, tree->left, SLDPJ );
+            if( i_ret != VLC_SUCCESS )
+                goto parsefail;
+            /* Ignore right parse tree as this is a special node */
+            if( asprintf( ppsz_where, "%s", psz_left ? psz_left : "" ) == -1 )
+            {
+                i_ret = VLC_ENOMEM;
+                goto parsefail;
+            }
+            break;
+        case ML_OP_NONE:
+            switch( tree->criteria )
+            {
+                case ML_PEOPLE:
+                    assert( tree->comp == ML_COMP_HAS
+                            || tree->comp == ML_COMP_EQUAL
+                            || tree->comp == ML_COMP_STARTS_WITH
+                            || tree->comp == ML_COMP_ENDS_WITH );
+                    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
+                            "people%s%s.name LIKE '%s%q%s'",
+                            tree->lvalue.str ? "_" : "",
+                            tree->lvalue.str ? tree->lvalue.str : "",
+                            tree->comp == ML_COMP_HAS
+                            || tree->comp == ML_COMP_STARTS_WITH ? "%%" : "",
+                            tree->value.str,
+                            tree->comp == ML_COMP_HAS
+                            || tree->comp == ML_COMP_ENDS_WITH ? "%%" : "" );
+                    if( *ppsz_where == NULL )
+                        goto parsefail;
+                    *pppsz_frompersons = realloc( *pppsz_frompersons,
+                            ++*i_frompersons * sizeof( char* ) );
+                    *pppsz_frompersons[ *i_frompersons - 1 ] = tree->lvalue.str;
+                    *join |= table_people;
+                    break;
+                case ML_PEOPLE_ID:
+                    assert( tree->comp == ML_COMP_EQUAL );
+                    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
+                                "( people%s%s.id = %d )", tree->lvalue.str ? "_":"",
+                                tree->lvalue.str ? tree->lvalue.str:"",
+                                tree->value.i );
+                    if( *ppsz_where == NULL )
+                        goto parsefail;
+                    *pppsz_frompersons = realloc( *pppsz_frompersons,
+                            ++*i_frompersons * sizeof( char* ) );
+                    *pppsz_frompersons[ *i_frompersons - 1 ] = tree->lvalue.str;
+                    *join |= table_people;
+                    break;
+                case ML_PEOPLE_ROLE:
+                    assert( tree->comp == ML_COMP_HAS
+                            || tree->comp == ML_COMP_EQUAL
+                            || tree->comp == ML_COMP_STARTS_WITH
+                            || tree->comp == ML_COMP_ENDS_WITH );
+                    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
+                            "people%s%s.role LIKE '%s%q%s'",
+                            tree->lvalue.str ? "_" : "",
+                            tree->lvalue.str ? tree->lvalue.str : "",
+                            tree->comp == ML_COMP_HAS
+                            || tree->comp == ML_COMP_STARTS_WITH ? "%%" : "",
+                            tree->value.str,
+                            tree->comp == ML_COMP_HAS
+                            || tree->comp == ML_COMP_ENDS_WITH ? "%%" : "" );
+                    if( *ppsz_where == NULL )
+                        goto parsefail;
+                    *pppsz_frompersons = realloc( *pppsz_frompersons,
+                            ++*i_frompersons * sizeof( char* ) );
+                    *pppsz_frompersons[ *i_frompersons - 1 ] = tree->lvalue.str;
+                    *join |= table_people;
+                    break;
+                CASE_PSZ( ML_ALBUM, "album.title", table_album );
+                CASE_PSZ( ML_ALBUM_COVER, "album.cover", table_album );
+                case ML_ALBUM_ID:
+                    assert( tree->comp == ML_COMP_EQUAL );
+                    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
+                            "album.id = %d", tree->value.i );
+                    if( *ppsz_where == NULL )
+                        goto parsefail;
+                    *join |= table_album;
+                    break;
+                CASE_PSZ( ML_COMMENT, "media.comment", table_media );
+                CASE_PSZ( ML_COVER, "media.cover", table_media );
+                CASE_INT( ML_DURATION, "media.duration", table_media );
+                CASE_PSZ( ML_EXTRA, "extra.extra", table_extra );
+                CASE_INT( ML_FILESIZE, "media.filesize", table_media );
+                CASE_PSZ( ML_GENRE, "media.genre", table_media );
+                case ML_ID:
+                    assert( tree->comp == ML_COMP_EQUAL );
+                    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql,
+                            "media.id = %d", tree->value.i );
+                    if( *ppsz_where == NULL )
+                        goto parsefail;
+                    *join |= table_media;
+                    break;
+                CASE_PSZ( ML_LANGUAGE, "extra.language", table_extra );
+                CASE_INT( ML_LAST_PLAYED, "media.last_played", table_media );
+                CASE_PSZ( ML_ORIGINAL_TITLE, "media.original_title", table_media );
+                   msg_Warn( p_ml, "Deprecated Played Count tags" );
+                CASE_INT( ML_PLAYED_COUNT, "media.played_count", table_media );
+                CASE_INT( ML_SCORE, "media.score", table_media );
+                CASE_PSZ( ML_TITLE, "media.title", table_media );
+                CASE_INT( ML_TRACK_NUMBER, "media.track", table_media);
+                CASE_INT( ML_TYPE, "media.type", table_media );
+                CASE_PSZ( ML_URI, "media.uri", table_media );
+                CASE_INT( ML_VOTE, "media.vote", table_media );
+                CASE_INT( ML_YEAR, "media.year", table_media );
+                case ML_LIMIT:
+                    if( !*limit )
+                        *limit = tree->value.i;
+                    else
+                        msg_Warn( p_ml, "Double LIMIT found" );
+                    break;
+                case ML_SORT_DESC:
+                    *sort = sql_Printf( p_ml->p_sys->p_sql, "%s%s%s DESC ",
+                                        sort ? *sort : "", sort ? ", " : "",
+                                        tree->value.str );
+                    if( *sort == NULL )
+                        goto parsefail;
+                    break;
+                case ML_SORT_ASC:
+                    *sort = sql_Printf( p_ml->p_sys->p_sql, "%s%s%s ASC ",
+                                        sort ? *sort : "", sort ? ", " : "",
+                                        tree->value.str );
+                    if( *sort == NULL )
+                        goto parsefail;
+                    break;
+                case ML_DISTINCT:
+                    if( !**distinct )
+                        *distinct = "DISTINCT";
+                    else
+                        msg_Warn( p_ml, "Double DISTINCT found!" );
+                    break;
+                default:
+                    msg_Err( p_ml, "Invalid select type or unsupported: %d", tree->criteria );
+            }
+            break;
+        default:
+            msg_Err( p_ml, "Broken find tree!" );
+            i_ret = VLC_EGENERIC;
+            goto parsefail;
+    }
+
+    i_ret = VLC_SUCCESS;
+parsefail:
+    free( psz_left );
+    free( psz_right );
+    return i_ret;
+}
+
+
+#   undef CASE_INT
+#   undef CASE_PSZ
+
+#   undef table_media
+#   undef table_album
+#   undef table_people
+#   undef table_extra
diff --git a/modules/media_library/sql_update.c b/modules/media_library/sql_update.c
new file mode 100644
index 0000000..cd54854
--- /dev/null
+++ b/modules/media_library/sql_update.c
@@ -0,0 +1,847 @@
+/*****************************************************************************
+ * sql_update.c: SQL-based media library: all database update functions
+ *****************************************************************************
+ * Copyright (C) 2008-2009 the VideoLAN team
+ * $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@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "sql_media_library.h"
+
+
+/**
+ * @brief Generic update in Media Library database
+ *
+ * @param p_ml the media library object
+ * @param selected_type the type of the element we're selecting
+ * @param where the list of ids or uri to change
+ * @param changes list of changes to make in the entries
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ * @note This function is transactional
+ */
+int Update( media_library_t *p_ml, ml_select_e selected_type,
+        const char* psz_lvalue, ml_ftree_t *where, vlc_array_t *changes )
+{
+    int i_ret = VLC_EGENERIC;
+    char *psz_query = NULL;
+    char *psz_id_query = NULL;
+    char **pp_results = NULL;
+    int i_rows = 0, i_cols = 0;
+
+    i_ret = BuildUpdate( p_ml, &psz_query, &psz_id_query,
+                         psz_lvalue, selected_type, where, changes );
+
+    if( i_ret != VLC_SUCCESS )
+    {
+        msg_Err(p_ml,"Failed to generate update query" );
+        return i_ret;
+    }
+    i_ret = VLC_EGENERIC;
+
+    Begin( p_ml );
+    if( QuerySimple( p_ml, "%s", psz_query ) != VLC_SUCCESS )
+    {
+        msg_Err( p_ml, "Couldn't run the generated update query successfully" );
+        goto quitdelete;
+    }
+
+    /* Get the updated IDs to send events! */
+    if( Query( p_ml, &pp_results, &i_rows, &i_cols, psz_id_query )
+            != VLC_SUCCESS )
+        goto quitdelete;
+
+    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] ) );
+        }
+    }
+
+    i_ret = VLC_SUCCESS;
+quitdelete:
+    if( i_ret != VLC_SUCCESS )
+        Rollback( p_ml );
+    else
+        Commit( p_ml );
+    FreeSQLResult( p_ml, pp_results );
+    free( psz_id_query );
+    free( psz_query );
+    return i_ret;
+}
+
+#define SET_STR( a )                                                        \
+if( !psz_set[i_type] )                                                      \
+{                                                                           \
+    psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql, a, find->value.str ); \
+    if( !psz_set[i_type] )                                                  \
+        goto quit_buildupdate;                                              \
+}                                                                           \
+break;
+
+#define SET_INT( a )                                                        \
+if( !psz_set[i_type] )                                                      \
+{                                                                           \
+    psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql, a, find->value.i );   \
+    if( !psz_set[i_type] )                                                  \
+        goto quit_buildupdate;                                              \
+}                                                                           \
+break;
+
+/* TODO: Build smarter updates by using IN () */
+static int BuildWhere( media_library_t* p_ml, char **ppsz_where, ml_ftree_t *tree )
+{
+    assert( ppsz_where );
+    char* psz_left = NULL;
+    char* psz_right = NULL;
+    int i_ret = VLC_SUCCESS;
+    switch( tree->op )
+    {
+        case ML_OP_AND:
+        case ML_OP_OR:
+            i_ret = BuildWhere( p_ml, &psz_left, tree->left );
+            if( i_ret != VLC_SUCCESS )
+                goto quit_buildwhere;
+            i_ret = BuildWhere( p_ml, &psz_right, tree->right );
+            if( i_ret != VLC_SUCCESS )
+                goto quit_buildwhere;
+            if( psz_left == NULL || psz_right == NULL )
+            {
+                msg_Err( p_ml, "Couldn't build AND/OR for Update statement" );
+                i_ret = VLC_EGENERIC;
+                goto quit_buildwhere;
+            }
+            if( asprintf( ppsz_where, "( %s %s %s )", psz_left,
+                ( tree->op == ML_OP_AND ? "AND" : "OR" ), psz_right ) == -1 )
+            {
+                i_ret = VLC_ENOMEM;
+                goto quit_buildwhere;
+            }
+            break;
+        case ML_OP_NOT:
+            i_ret = BuildWhere( p_ml, &psz_left, tree->left );
+            if( i_ret != VLC_SUCCESS )
+                goto quit_buildwhere;
+            if( psz_left == NULL )
+            {
+                msg_Err( p_ml, "Couldn't build NOT for Update statement" );
+                i_ret = VLC_EGENERIC;
+                goto quit_buildwhere;
+            }
+            if( asprintf( ppsz_where, "( NOT %s )", psz_left ) == -1 )
+            {
+                i_ret = VLC_ENOMEM;
+                goto quit_buildwhere;
+            }
+            break;
+        case ML_OP_SPECIAL:
+            msg_Err( p_ml, "Couldn't build special for Update statement" );
+            break;
+        case ML_OP_NONE:
+            switch( tree->criteria )
+            {
+                case ML_ID:
+                    assert( tree->comp == ML_COMP_EQUAL );
+                    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "media.id = %d",
+                                                tree->value.i );
+                    if( *ppsz_where == NULL )
+                        goto quit_buildwhere;
+                    break;
+                case ML_URI:
+                    assert( tree->comp == ML_COMP_EQUAL );
+                    *ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "media.uri = %q",
+                                                tree->value.str );
+                    if( *ppsz_where == NULL )
+                        goto quit_buildwhere;
+                    break;
+                default:
+                    msg_Err( p_ml, "Trying to update with unsupported condition" );
+                    break;
+            }
+    }
+quit_buildwhere:
+    return i_ret;
+}
+
+/**
+ * @brief Generic UPDATE query builder
+ *
+ * @param p_ml This media_library_t object
+ * @param ppsz_query *ppsz_query will contain query for update
+ * @param ppsz_id_query will contain query to get the ids of updated files
+ * @param selected_type the type of the element we're selecting
+ * @param where parse tree of where condition
+ * @param changes list of changes to make in the entries
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+int BuildUpdate( media_library_t *p_ml,
+                 char **ppsz_query, char **ppsz_id_query,
+                 const char *psz_lvalue,
+                 ml_select_e selected_type,
+                 ml_ftree_t *where, vlc_array_t *changes )
+{
+    assert( ppsz_query );
+    assert( ppsz_id_query );
+
+    *ppsz_query = NULL;
+    int i_type;
+    int i_index;
+    int i_ret = VLC_ENOMEM;
+
+    char *psz_table = NULL;
+    /* TODO: Hack? */
+    char *psz_set[ ML_DIRECTORY + 1 ] = { NULL };
+    char *psz_fullset = NULL;
+    char *psz_extra = NULL; /*<< For an update to extra table */
+
+    char *psz_where = NULL;
+    char *psz_tmp = NULL;
+
+    int *pi_padd_ids = NULL;
+    int i_people_add = 0;
+
+    int i_album_id = 0;
+    char *psz_album = NULL;
+    char *psz_cover = NULL;
+
+
+    if( !where )
+    {
+        msg_Warn( p_ml, "You're trying to update no rows."
+               "Trying to guess update based on uri" );
+    }
+
+    /* Create the id/uri lists for WHERE part of the query */
+    i_ret = BuildWhere( p_ml, &psz_where, where );
+    if( i_ret != VLC_SUCCESS )
+        goto quit_buildupdate;
+
+    /** Firstly, choose the right table */
+    switch( selected_type )
+    {
+        case ML_ALBUM:
+            psz_table = strdup( "album" );
+            break;
+        case ML_PEOPLE:
+            psz_table = strdup( "people" );
+            break;
+        case ML_MEDIA:
+            psz_table = strdup( "media" );
+            break;
+        default:
+            msg_Err( p_ml, "Not a valid element to Update!" );
+            i_ret = VLC_EGENERIC;
+            goto quit_buildupdate;
+            break;
+    }
+
+    if( !psz_table )
+        return VLC_ENOMEM;
+
+    /** Secondly, build the SET part of the query */
+    for( i_index = 0; i_index < vlc_array_count( changes ); i_index++ )
+    {
+        ml_element_t *find = ( ml_element_t * )
+                vlc_array_item_at_index( changes, i_index );
+        i_type = find->criteria;
+
+        switch( i_type )
+        {
+        case ML_ALBUM:
+            if( selected_type == ML_ALBUM )
+            {
+                if( !psz_set[i_type] )
+                {
+                    psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
+                                                  "title = %Q",
+                                                  find->value.str );
+                    if( !psz_set[i_type] )
+                        goto quit_buildupdate;
+                }
+            }
+            else if( selected_type == ML_MEDIA )
+            {
+                if( !psz_album )
+                    psz_album = find->value.str;
+            }
+            else
+                assert( 0 );
+            break;
+        case ML_ALBUM_ID:
+            assert( selected_type != ML_ALBUM );
+            if( selected_type == ML_MEDIA )
+            {
+                if( i_album_id <= 0 )
+                {
+                    i_album_id = find->value.i;
+                    if( !psz_set[i_type] )
+                    {
+                        psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
+                                                      "album_id = '%d'",
+                                                      find->value.i );
+                        if( !psz_set[i_type] )
+                            goto quit_buildupdate;
+                    }
+                }
+            }
+            break;
+        case ML_PEOPLE:
+            if( selected_type == ML_MEDIA )
+            {
+                pi_padd_ids = (int*) realloc( pi_padd_ids , ( ++i_people_add * sizeof(int) ) );
+                pi_padd_ids[ i_people_add - 1 ] = ml_GetInt( p_ml, ML_PEOPLE_ID,
+                                            find->lvalue.str, ML_PEOPLE, find->lvalue.str,
+                                            find->value.str );
+                if( pi_padd_ids[ i_people_add - 1 ] <= 0 )
+                {
+                    AddPeople( p_ml, find->value.str, find->lvalue.str );
+                    pi_padd_ids[ i_people_add - 1 ] = ml_GetInt( p_ml, ML_PEOPLE_ID,
+                                            find->lvalue.str, ML_PEOPLE, find->lvalue.str,
+                                            find->value.str );
+                }
+            }
+            else if( strcmp( psz_lvalue, find->lvalue.str ) )
+            {
+                msg_Err( p_ml, "Trying to update a different person type" );
+                return VLC_EGENERIC;
+            }
+            else
+            {
+                if( !psz_set[i_type] ) psz_set[i_type] =
+                    sql_Printf( p_ml->p_sys->p_sql, "name = %Q", find->value.str );
+            }
+            break;
+        case ML_PEOPLE_ID:
+            /* TODO: Implement smarter updates for this case? */
+            assert( selected_type == ML_MEDIA );
+            if( selected_type == ML_MEDIA )
+            {
+                bool b_update = true;
+                for( int i = 0; i < i_people_add; i++ )
+                {
+                   if( pi_padd_ids[ i ] == find->value.i )
+                   {
+                      b_update = false;
+                      break;
+                   }
+                }
+                if( b_update )
+                {
+                    pi_padd_ids = (int *)realloc( pi_padd_ids, ( ++i_people_add * sizeof(int) ) );
+                    pi_padd_ids[ i_people_add - 1 ] = find->value.i;
+                }
+            }
+            break;
+        case ML_PEOPLE_ROLE:
+            msg_Dbg( p_ml, "Can't update role" );
+            break;
+        case ML_COMMENT:
+            assert( selected_type == ML_MEDIA );
+            SET_STR( "comment = %Q" );
+        case ML_COVER:
+            assert( selected_type == ML_ALBUM || selected_type == ML_MEDIA );
+            psz_cover = find->value.str;
+            SET_STR( "cover = %Q" );
+        case ML_DISC_NUMBER:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "disc = '%d'" );
+        case ML_DURATION:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "duration = '%d'" );
+        case ML_EXTRA:
+            assert( selected_type == ML_MEDIA );
+            SET_STR( "extra = %Q" );
+        case ML_FIRST_PLAYED:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "first_played =='%d'" );
+        case ML_GENRE:
+            assert( selected_type == ML_MEDIA );
+            SET_STR( "genre = %Q" );
+        /* ID cannot be updated */
+        /* Import time can't be updated */
+        case ML_LAST_PLAYED:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "last_played = '%d'" );
+        case ML_ORIGINAL_TITLE:
+            assert( selected_type == ML_MEDIA );
+            SET_STR( "original_title = %Q" );
+        case ML_PLAYED_COUNT:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "played_count = '%d'" );
+        case ML_PREVIEW:
+            assert( selected_type == ML_MEDIA );
+            SET_STR( "preview = %Q" );
+        case ML_SKIPPED_COUNT:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "skipped_count = '%d'" );
+        case ML_SCORE:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "score = '%d'" );
+        case ML_TITLE:
+            assert( selected_type == ML_MEDIA );
+            SET_STR( "title = %Q" );
+        case ML_TRACK_NUMBER:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "track = '%d'" );
+        case ML_TYPE:
+            assert( selected_type == ML_MEDIA );
+            if( !psz_set[i_type] ) psz_set[i_type] =
+                sql_Printf( p_ml->p_sys->p_sql, "type = '%d'", find->value.i );
+            break;
+        case ML_URI:
+            assert( selected_type == ML_MEDIA );
+            if( !psz_set[i_type] )
+            {
+                psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
+                                              "uri = %Q",
+                                              find->value.str );
+            }
+            break;
+        case ML_VOTE:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "vote = '%d'" );
+        case ML_YEAR:
+            assert( selected_type == ML_MEDIA );
+            SET_INT( "year = '%d'" );
+        case ML_END:
+            goto exitfor;
+        default:
+            msg_Warn( p_ml, "Invalid type for update : %d", i_type );
+        }
+    }
+exitfor:
+
+    /* TODO: Album artist. Verify albumart */
+    if( i_album_id <= 0 && psz_album && *psz_album )
+    {
+        i_ret = AddAlbum( p_ml, psz_album, psz_cover, 0 );
+        if( i_ret != VLC_SUCCESS )
+            goto quit_buildupdate;
+        i_album_id = ml_GetAlbumId( p_ml, psz_album );
+        if( i_album_id <= 0 )
+            goto quit_buildupdate;
+        psz_set[ML_ALBUM_ID] = sql_Printf( p_ml->p_sys->p_sql,
+                                      "album_id = '%d'", i_album_id );
+        if( !psz_set[i_type] )
+            goto quit_buildupdate;
+    }
+
+    for( unsigned i = 0; i <= ML_DIRECTORY; i++ )
+    {
+        if( psz_set[i] )
+        {
+            if( i == ML_EXTRA || i == ML_LANGUAGE )
+            {
+                free( psz_tmp );
+                if( asprintf( &psz_tmp, "%s%s%s", psz_extra ? psz_extra : "",
+                               psz_extra ? ", ": "",  psz_set[i] ) == -1 )
+                    goto quit_buildupdate;
+                free( psz_extra );
+                psz_extra = strdup( psz_tmp );
+            }
+            else
+            {
+                free( psz_tmp );
+                if( asprintf( &psz_tmp, "%s%s%s", psz_fullset ? psz_fullset : "",
+                               psz_fullset ? ", ": "",  psz_set[i] ) == -1 )
+                    goto quit_buildupdate;
+                free( psz_fullset );
+                psz_fullset = strdup( psz_tmp );
+            }
+        }
+    }
+    i_ret = VLC_SUCCESS;
+
+    /** Now build the right WHERE condition */
+    assert( psz_where && *psz_where );
+
+    /** Finally build the full query */
+    /** Pass if we have some people to add - Indirect update*/
+    if( !psz_fullset && i_people_add == 0 )
+    {
+        i_ret = VLC_EGENERIC;
+        goto quit_buildupdate;
+    }
+
+    if( psz_fullset ){
+        if( asprintf( ppsz_query, "UPDATE %s SET %s WHERE %s", psz_table,
+                      psz_fullset, psz_where ) == -1 )
+        {
+            goto quit_buildupdate;
+        }
+    }
+
+    if( selected_type == ML_MEDIA )
+    {
+        if( psz_extra )
+        {
+            if( asprintf( &psz_tmp, "%s; UPDATE extra SET %s WHERE %s",
+                        *ppsz_query, psz_extra, psz_where ) == -1 )
+                goto quit_buildupdate;
+            free( *ppsz_query );
+            *ppsz_query = psz_tmp;
+            psz_tmp = NULL;
+        }
+        char* psz_idstring = NULL;
+        if( i_people_add > 0 )
+        {
+            for( int i = 0; i < i_people_add; i++ )
+            {
+                if( asprintf( &psz_tmp, "%s%s%d", psz_idstring == NULL? "" : psz_idstring,
+                            psz_idstring == NULL ? "" : ",", pi_padd_ids[i] ) == -1 )
+                {
+                    free( psz_tmp );
+                    free( psz_idstring );
+                    goto quit_buildupdate;
+                }
+                free( psz_idstring );
+                psz_idstring = psz_tmp;
+                psz_tmp = NULL;
+            }
+            /* Delete all connections with people whom we will update now! */
+            if( asprintf( &psz_tmp, "%s;DELETE FROM media_to_people WHERE EXISTS "
+                    "(SELECT media.id, people.id FROM media JOIN media_to_people "
+                    "AS temp ON media.id = temp.media_id "
+                    "JOIN people ON temp.people_id = people.id "
+                    "WHERE %s AND people.role IN "
+                    "(SELECT people.role FROM people WHERE people.id IN (%s)) "
+                    "AND people.id NOT IN (%s) "
+                    "AND temp.media_id = media_to_people.media_id AND "
+                    "temp.people_id = media_to_people.people_id )",
+                    *ppsz_query == NULL ? "": *ppsz_query, psz_where,
+                    psz_idstring, psz_idstring ) == -1 )
+            {
+                free( psz_idstring );
+                goto quit_buildupdate;
+            }
+            free( *ppsz_query );
+            *ppsz_query = psz_tmp;
+            psz_tmp = NULL;
+            free( psz_idstring );
+        }
+        for( int i = 0; i < i_people_add ; i++ )
+        {
+            if( pi_padd_ids[i] > 0 )
+            {
+                /* OR IGNORE will avoid errors from collisions from old media
+                 * Perhaps this hack can be fixed...FIXME */
+                if( asprintf( &psz_tmp, "%s;INSERT OR IGNORE into media_to_people "
+                "(media_id,people_id) SELECT media.id, %d FROM media WHERE %s",
+                *ppsz_query == NULL ? "" : *ppsz_query, pi_padd_ids[i],
+                psz_where ) == -1 )
+                   goto quit_buildupdate;
+                FREENULL( *ppsz_query );
+                *ppsz_query = psz_tmp;
+                psz_tmp = NULL;
+            }
+        }
+    }
+
+    if( asprintf( ppsz_id_query, "SELECT id AS %s_id FROM %s WHERE %s",
+                psz_table, psz_table, psz_where ) == -1 )
+    {
+        goto quit_buildupdate;
+    }
+#ifndef NDEBUG
+    msg_Dbg( p_ml, "updated media where %s", psz_where );
+#endif
+
+quit_buildupdate:
+    free( psz_tmp );
+    free( psz_table );
+    free( psz_fullset );
+    free( psz_extra );
+    free( pi_padd_ids );
+    for( int i = 0; i <= ML_DIRECTORY; i++ )
+        free( psz_set[ i ] );
+
+    return i_ret;
+}
+
+#undef SET_STR
+#undef SET_INT
+
+/**
+ * @brief Update a ml_media_t
+ *
+ * @param p_ml the media library object
+ * @param p_media media to synchronise in the database
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ * @note: the media id may be 0, in this case, the update is based
+ *        on the url (less powerful). This function is threadsafe
+ *
+ * This synchronises all non NULL and non zero fields of p_media
+ * Synchronization of album and people is TODO
+ */
+int UpdateMedia( media_library_t *p_ml, ml_media_t *p_media )
+{
+    assert( p_media->i_id || ( p_media->psz_uri && *p_media->psz_uri ) );
+    vlc_array_t *changes = vlc_array_new();
+    ml_element_t *find = NULL;
+    int i_ret = VLC_EGENERIC;
+
+    ml_LockMedia( p_media );
+    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 );
+    }
+    if( p_media->psz_title )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_TITLE;
+        find->value.str = p_media->psz_title;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_type )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_TYPE;
+        find->value.i = p_media->i_type;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_duration )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_DURATION;
+        find->value.i = p_media->i_duration;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->psz_preview )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_PREVIEW;
+        find->value.str = p_media->psz_preview;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->psz_cover )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_COVER;
+        find->value.str = p_media->psz_cover;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_disc_number )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_DISC_NUMBER;
+        find->value.i = p_media->i_disc_number;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_track_number )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_TRACK_NUMBER;
+        find->value.i = p_media->i_track_number;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_year )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_YEAR;
+        find->value.i = p_media->i_year;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->psz_genre )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_GENRE;
+        find->value.str = p_media->psz_genre;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_album_id )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_ALBUM_ID;
+        find->value.i = p_media->i_album_id;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->psz_album )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_ALBUM;
+        find->value.str = p_media->psz_album;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_skipped_count )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_SKIPPED_COUNT;
+        find->value.i = p_media->i_skipped_count;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_last_skipped )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_LAST_SKIPPED;
+        find->value.i = p_media->i_last_skipped;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_played_count )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_PLAYED_COUNT;
+        find->value.i = p_media->i_played_count;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_last_played )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_LAST_PLAYED;
+        find->value.i = p_media->i_last_played;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_first_played )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_FIRST_PLAYED;
+        find->value.i = p_media->i_first_played;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_vote )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_VOTE;
+        find->value.i = p_media->i_vote;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->i_score )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_SCORE;
+        find->value.i = p_media->i_score;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->psz_comment )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_COMMENT;
+        find->value.str = p_media->psz_comment;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->psz_extra )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_EXTRA;
+        find->value.str = p_media->psz_extra;
+        vlc_array_append( changes, find );
+    }
+    if( p_media->psz_language )
+    {
+        find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        find->criteria = ML_LANGUAGE;
+        find->value.str = p_media->psz_language;
+        vlc_array_append( changes, find );
+    }
+    /*TODO: implement extended meta */
+    /* We're not taking import time! Good */
+
+    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
+ * TODO: Push this into UpdateMedia
+ **/
+int SetAlbumCover( 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;
+}
-- 
1.6.0.4

