From 8afa1b77b28d6972f28cbb850905f2d199e1ce57 Mon Sep 17 00:00:00 2001
From: Srikanth Raju <srikiraju@gmail.com>
Date: Sat, 29 Aug 2009 00:46:23 +0530
Subject: [PATCH] Media library core

This includes support for a media library module in core VLC.
---
 include/vlc_common.h        |    4 +
 include/vlc_media_library.h | 1234 +++++++++++++++++++++++++++++++++++++++++++
 src/Makefile.am             |    2 +
 src/libvlc-module.c         |    8 +
 src/libvlc.c                |   29 +
 src/libvlc.h                |    3 +-
 src/libvlccore.sym          |   14 +
 src/misc/media_library.c    |  486 +++++++++++++++++
 8 files changed, 1779 insertions(+), 1 deletions(-)
 create mode 100644 include/vlc_media_library.h
 create mode 100644 src/misc/media_library.c

diff --git a/include/vlc_common.h b/include/vlc_common.h
index 8675ff8..bbd29bd 100644
--- a/include/vlc_common.h
+++ b/include/vlc_common.h
@@ -355,6 +355,10 @@ typedef struct input_stats_t input_stats_t;
 typedef struct update_t update_t;
 typedef struct update_iterator_t update_iterator_t;
 
+/* SQL-based Media Library */
+typedef struct media_library_t media_library_t;
+typedef struct media_library_sys_t media_library_sys_t;
+
 /* Meta engine */
 typedef struct meta_engine_t meta_engine_t;
 
diff --git a/include/vlc_media_library.h b/include/vlc_media_library.h
new file mode 100644
index 0000000..22fd4c0
--- /dev/null
+++ b/include/vlc_media_library.h
@@ -0,0 +1,1234 @@
+/*****************************************************************************
+ * vlc_media_library.h: 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>
+ *
+ * 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.
+ *****************************************************************************/
+
+#if !defined( __LIBVLC__ )
+# error You are not libvlc or one of its plugins. You cannot include this file
+#endif
+
+#ifndef VLC_MEDIA_LIBRARY_H
+# define VLC_MEDIA_LIBRARY_H
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+#include <vlc_common.h>
+#include <assert.h>
+#include <vlc_playlist.h>
+
+/*****************************************************************************
+ * ML Enums
+ *****************************************************************************/
+
+#define ML_PERSON_ARTIST        "Artist"
+#define ML_PERSON_ALBUM_ARTIST  "Album Artist"
+#define ML_PERSON_ENCODER       "Encoder"
+#define ML_PERSON_PUBLISHER     "Publisher"
+
+/** List of Query select types.
+ * In a query array or variable argument list, each select type is followed
+ * by an argument (X) of variable type (char* or int, @see ml_element_t).
+ * These types can be used either in the query list or in the result array.
+ * Some types are reserved for the result array:
+ */
+typedef enum
+{
+    ML_ALBUM = 1,              /**< Album Title */
+    ML_ALBUM_ID,               /**< Album ID */
+    ML_ALBUM_COVER,            /**< Album Cover art url */
+    /* TODO: Remove ML_ARTIST */
+    ML_ARTIST,                 /**< Artist, interpreted as ML_PEOPLE
+                                    && ML_PEOPLE_ROLE = ML_PERSON_ARTIST */
+    ML_ARTIST_ID,              /**< Artist ID, interpreted as ML_PEOPLE_ID
+                                    && ML_PEOPLE_ROLE = ML_PERSON_ARTIST */
+    ML_COMMENT,                /**< Comment about media */
+    ML_COUNT_MEDIA,            /**< Number of medias */
+    ML_COUNT_ALBUM,            /**< Number of albums */
+    ML_COUNT_PEOPLE,           /**< Number of people */
+    ML_COVER,                  /**< Cover art url */
+    ML_DURATION,               /**< Duration in ms */
+    ML_DISC_NUMBER,            /**< Disc number of the track */
+    ML_EXTRA,                  /**< Extra/comment (string) on the media */
+    ML_FIRST_PLAYED,           /**< First time media was played */
+    ML_FILESIZE,               /**< Size of the media file */
+    ML_GENRE,                  /**< Genre of the media (if any) */
+    ML_ID,                     /**< Media ID */
+    ML_IMPORT_TIME,            /**< Date when media was imported */
+    ML_LANGUAGE,               /**< Language */
+    ML_LAST_PLAYED,            /**< Last play UNIX timestamp */
+    ML_LAST_SKIPPED,           /**< Time when media was last skipped */
+    ML_ORIGINAL_TITLE,         /**< Media original title (if any) */
+    ML_PEOPLE,                 /**< Any People associated with this media */
+    ML_PEOPLE_ID,              /**< Id of a person */
+    ML_PEOPLE_ROLE,            /**< Person role */
+    ML_PLAYED_COUNT,           /**< Media play count */
+    ML_PREVIEW,                /**< Url of the video preview */
+    ML_SKIPPED_COUNT,          /**< Number of times skipped */
+    ML_SCORE,                  /**< Computed media score */
+    ML_TITLE,                  /**< Media title */
+    ML_TRACK_NUMBER,           /**< Media track number (if any) */
+    ML_TYPE,                   /**< Media type. @see ml_type_e */
+    ML_URI,                    /**< Media full URI. */
+    ML_VOTE,                   /**< Media user vote value */
+    ML_YEAR,                   /**< Media publishing year */
+    ML_DIRECTORY,              /**< Monitored directory */
+    ML_MEDIA,                  /**< Full media descriptor. @see ml_media_t */
+    ML_MEDIA_SPARSE,           /**< Sparse media. @see ml_media_t */
+    ML_MEDIA_EXTRA,            /**< Sparse + Extra = Full media */
+
+    /* Some special elements */
+    ML_LIMIT     = -1,         /**< Limit a query to X results */
+    ML_SORT_DESC = -2,         /**< Sort a query descending on argument X */
+    ML_SORT_ASC  = -3,         /**< Sort a query ascending on argument X */
+    ML_DISTINCT  = -4,         /**< Add DISTINCT to SELECT statements. */
+    ML_END       = -42         /**< End of argument list */
+} ml_select_e;
+
+/** Media types (audio, video, etc...) */
+typedef enum
+{
+    ML_UNKNOWN   = 0,       /**< Unknown media type */
+    ML_AUDIO     = 1 << 0,  /**< Audio only media */
+    ML_VIDEO     = 1 << 1,  /**< Video media. May contain audio channels */
+    ML_STREAM    = 1 << 2,  /**< Streamed media = not a local file */
+    ML_NODE      = 1 << 3,  /**< Nodes like simple nodes, directories, playlists, etc */
+    ML_REMOVABLE = 1 << 4,  /**< Removable media: CD/DVD/Card/... */
+} ml_type_e;
+
+/** Query result item/list type: integers, strings, medias, timestamps */
+typedef enum {
+    ML_TYPE_INT,        /**< Object is an int */
+    ML_TYPE_PSZ,        /**< A string char* */
+    ML_TYPE_TIME,       /**< A timestamp mtime_t */
+    ML_TYPE_MEDIA,      /**< A pointer to a media ml_media_t* */
+} ml_result_type_e;
+
+/** Arguments for VLC Control for the media library */
+typedef enum
+{
+    ML_SET_DATABASE,      /**< arg1 = char *psz_host
+                               arg2 = int i_port
+                               arg3 = char *psz_user
+                               arg4 = char *psz_pass */
+    ML_INIT_DATABASE,     /**< No arg */
+    ML_ADD_INPUT_ITEM,    /**< arg1 = input_item_t* */
+    ML_ADD_PLAYLIST_ITEM, /**< arg1 = playlist_item_t * */
+    ML_ADD_MONITORED,     /**< arg1 = char* */
+    ML_DEL_MONITORED,     /**< arg1 = char* */
+    ML_GET_MONITORED,     /**< arg1 = vlc_array_t* */
+} ml_control_e;
+
+/* Operations that can be specified between find conditions */
+typedef enum
+{
+    ML_OP_NONE = 0,       /**< This is to specify an actual condition */
+    ML_OP_AND,            /**< AND condition */
+    ML_OP_OR,             /**< OR condition */
+    ML_OP_NOT,            /**< NOT condition */
+    ML_OP_SPECIAL         /**< This is for inclusion of
+                            *  special stuffs like LIMIT */
+} ml_op_e;
+
+/* Comparison operators used in a single find condition */
+typedef enum
+{
+    ML_COMP_NONE = 0,
+    ML_COMP_LESSER,              ///< <
+    ML_COMP_LESSER_OR_EQUAL,     ///< <=
+    ML_COMP_EQUAL,               ///< ==
+    ML_COMP_GREATER_OR_EQUAL,    ///< >=
+    ML_COMP_GREATER,             ///< >
+    ML_COMP_HAS,                 ///< "Contains", equivalent to SQL "LIKE %x%"
+    ML_COMP_STARTS_WITH,         ///< Equivalent to SQL "LIKE %x"
+    ML_COMP_ENDS_WITH,           ///< Equivalent to SQL "LIKE x%"
+} ml_comp_e;
+
+/*****************************************************************************
+ * ML Structures and types
+ *****************************************************************************/
+
+typedef struct ml_media_t      ml_media_t;
+typedef struct ml_result_t     ml_result_t;
+typedef struct ml_element_t    ml_element_t;
+typedef struct ml_person_t     ml_person_t;
+typedef struct ml_ftree_t      ml_ftree_t;
+
+/** Main structure of the media library. VLC object. */
+struct media_library_t
+{
+    VLC_COMMON_MEMBERS
+
+    module_t             *p_module;  /**< the media library module */
+    media_library_sys_t  *p_sys;     /**< internal struture */
+
+    /** Member functions */
+    struct
+    {
+        /**< Search in the database */
+        int ( * pf_Find )            ( media_library_t *p_media_library,
+                                       vlc_array_t *p_result_array,
+                                       va_list args );
+
+        /**< Search in the database using an array of arguments */
+        int ( * pf_FindAdv )         ( media_library_t *p_media_library,
+                                       vlc_array_t *p_result_array,
+                                       ml_select_e selected_type,
+                                       const char *psz_lvalue,
+                                       ml_ftree_t *tree );
+
+        /**< Update the database using an array of arguments */
+        int ( * pf_Update )          ( media_library_t *p_media_library,
+                                       ml_select_e selected_type,
+                                       const char *psz_lvalue,
+                                       ml_ftree_t *where,
+                                       vlc_array_t *changes );
+
+        /**< Delete many medias in the database */
+        int ( * pf_Delete )    ( media_library_t *p_media_library,
+                                       vlc_array_t *p_array );
+
+        /**< Control the media library */
+        int ( * pf_Control ) ( media_library_t *p_media_library,
+                               int i_query, va_list args );
+
+        /**< Create associated input item */
+        input_item_t* ( * pf_InputItemFromMedia ) (
+                    media_library_t *p_media_library, int i_media );
+
+        /**< Get a media */
+        ml_media_t* ( * pf_GetMedia ) (
+                    media_library_t *p_media_library, int i_media,
+                    ml_select_e select, bool reload );
+    } functions;
+};
+
+
+/**
+ * @brief Structure to describe a media
+ */
+struct ml_media_t
+{
+    VLC_GC_MEMBERS
+    vlc_mutex_t     lock;               /**< Mutex for multithreaded access */
+
+    bool            b_pool;             /**< Private: Specifies whether media exists in pool */
+
+    bool            b_sparse;           /**< Specifies if media is loaded fully */
+    int32_t         i_id;               /**< Media ID in the database */
+    char            *psz_uri;           /**< URI to find the media */
+    ml_type_e       i_type;             /**< Type of the media (ml_type_e) */
+    char            *psz_title;         /**< Title of the media */
+    char            *psz_orig_title;    /**< Original title (mainly for movies) */
+    char            *psz_album;         /**< Name of the album */
+    int32_t         i_album_id;         /**< Album id */
+    char            *psz_cover;         /**< URI of the cover */
+    char            *psz_preview;       /**< Preview thumbnail for video, if any */
+    int16_t         i_disc_number;      /**< Disc number of media */
+    int16_t         i_track_number;     /**< Track number */
+    int16_t         i_year;             /**< Year of release */
+    char            *psz_genre;         /**< Genre of the media */
+    int8_t          i_vote;             /**< User vote */
+    int32_t         i_score;            /**< Score computed about the media */
+    char            *psz_comment;       /**< Comment or description about media */
+    int64_t         i_filesize;         /**< Size of the file */
+
+    /* Counts and Times */
+    mtime_t         i_duration;         /**< Duration in microseconds */
+    int32_t         i_played_count;     /**< How many time the media was played */
+    mtime_t         i_last_played;      /**< Time when the media was last played */
+    int32_t         i_skipped_count;    /**< No. of times file was skipped */
+    mtime_t         i_last_skipped;     /**< Time when the media was last skipped */
+    mtime_t         i_first_played;     /**< First played */
+    mtime_t         i_import_time;      /**< Time when media was added */
+
+    /* Extra/Advanced meta
+     * This meta is not loaded by default */
+    ml_person_t     *p_people;          /**< People associated with this media
+                                             This meta holds only one artist
+                                             if b_sparse = true */
+    char            *psz_language;      /**< Language */
+    char            *psz_extra;         /**< Some extra datas like lyrics */
+
+    int32_t         i_bitrate;          /**< Bitrate of the media */
+    int32_t         i_samplerate;       /**< Samplerate of the media */
+    int32_t         i_bpm;              /**< Beats per minute */
+};
+
+
+/**
+ * @brief Main communication struct between GUI and sql_media_library.
+ * Generic representation of an ML/SQL query result.
+ */
+struct ml_result_t
+{
+    int32_t          id;        /**< Media/Album/Artist... ID (if any) */
+    ml_result_type_e type;      /**< Type of value */
+    union
+    {
+        /* Classical results */
+        int             i;
+        char           *psz;
+        mtime_t         time;
+
+        /* Complex result: media descriptor */
+        ml_media_t     *p_media;
+    } value;                    /**< Value of the result obtained */
+};
+
+
+/**
+ * @brief Element of a query: criteria type/value pair
+ * Used for update and delete queries
+ */
+struct ml_element_t
+{
+    ml_select_e    criteria;    /**< SELECT criteria type. @see ml_select_e */
+    union
+    {
+        int     i;
+        char*   str;
+    } value;                    /**< SELECT criteria value (string or int) */
+    union
+    {
+        int     i;
+        char*   str;
+    } lvalue;                   /**< Refer to @see ml_ftree_t lvalue docs */
+};
+
+/**
+ * Binary tree used to parse the WHERE condition for a search
+ */
+struct ml_ftree_t
+{
+    ml_op_e         op;         /**< Operator. ML_OP_NONE means this is a leaf
+                                  *  node. Criteria and value gives its data.
+                                  *  ML_OP_SPECIAL specifies a special node
+                                  *  that does not form a part of the WHERE.
+                                  *  The right node consists of the data
+                                  *  with its criteria set to the special val
+                                  *  and the left node is the corresponding
+                                  *  subtree of the parent node.
+                                  *  ML_OP_NOT only left sub tree is considered
+                                  *  ML_OP_AND and ML_OP_OR consider both
+                                  *  left and right subtrees */
+    ml_ftree_t      *left;      /**< Left child of Bin tree */
+    ml_ftree_t      *right;     /**< Right child of Bin tree */
+    ml_select_e     criteria;   /**< SELECT criteria type @see ml_select_e
+                                  *  The criteria value is considered only when
+                                  *  op = ML_OP_NONE i.e. in leaf nodes */
+    ml_comp_e       comp;       /**< Condition between type and value */
+    union
+    {
+        int     i;
+        char    *str;
+    } value;                    /**< SELECT criteria value ( string or int ) */
+    union
+    {
+        int     i;
+        char    *str;
+    } lvalue;                   /**< Used as key value for people types/roles.
+                                     An empty string "" denotes ANY person role.
+                                     NULL is used for all other criterias */
+};
+
+
+/**
+ * Person class. Implemented as a linked list
+ */
+struct ml_person_t
+{
+    char               *psz_role;   /**< Type of person */
+    char               *psz_name;   /**< Name of the person */
+    int                 i_id;       /**< ID in the database */
+    ml_person_t        *p_next;     /**< Next person in list */
+};
+
+
+/*****************************************************************************
+ * ML Function headers
+ *****************************************************************************/
+
+/**
+ * @brief Acquire a reference to the media library singleton
+ * @param p_this The object holding the media library
+ * @return The media library object. NULL if the media library
+ * object could not be loaded
+ */
+VLC_EXPORT( media_library_t*, __ml_Hold, ( vlc_object_t* p_this ) );
+#define ml_Hold( a ) __ml_Hold( VLC_OBJECT(a) )
+
+/**
+ * @brief Discard your ref to media library
+ * @param p_this The object holding the media library
+ */
+VLC_EXPORT( void, __ml_Release, ( vlc_object_t* p_this ) );
+#define ml_Release(a) __ml_Release( VLC_OBJECT(a))
+
+/**
+ * @brief Create a Media Library VLC object.
+ * @param p_this Parent to attach the ML object to.
+ * @param psz_name Name for the module
+ * @return The ML object.
+ */
+VLC_EXPORT( media_library_t*, __ml_Create, ( vlc_object_t *p_this, char* psz_name ) );
+
+/**
+ * @brief Destructor for the Media library singleton
+ * @param p_this Parent the ML object is attached to
+ */
+VLC_EXPORT( void, __ml_Destructor, ( vlc_object_t* p_this ) );
+
+/**
+ * @brief Control the Media Library
+ * @param p_media_library the media library object
+ * @param i_type one of ml_control_e values @see ml_control_e.
+ * @param ... optional arguments.
+ * @return VLC_SUCCESS or an error
+ */
+static inline int ml_ControlVa( media_library_t *p_media_library,
+                                ml_control_e i_type, va_list args )
+{
+    return p_media_library->functions.pf_Control( p_media_library,
+                                                  i_type,
+                                                  args );
+}
+
+/**
+ * @brief Control the Media Library
+ * @param i_type one of ml_control_e values @see ml_control_e.
+ * Variable arguments list equivalent
+ */
+#define ml_Control( a, b, args... )     __ml_Control( a, b, ## args )
+static inline int __ml_Control( media_library_t *p_media_library,
+                                ml_control_e i_type, ... )
+{
+    va_list args;
+    int returned;
+
+    va_start( args, i_type );
+    returned = ml_ControlVa( p_media_library, i_type, args );
+    va_end( args );
+
+    return returned;
+}
+
+/**
+ * @brief Determine an attribute's type (int or string)
+ * @param meta Attribute to test @see ml_select_e
+ * @return -1 if invalid, 0 if this is an integer, 1 if this is a string
+ */
+static inline int ml_AttributeIsString( ml_select_e meta )
+{
+    switch( meta )
+    {
+    /* Strings */
+    case ML_ALBUM:
+    case ML_ARTIST:
+    case ML_COMMENT:
+    case ML_COVER:
+    case ML_EXTRA:
+    case ML_GENRE:
+    case ML_LANGUAGE:
+    case ML_PREVIEW:
+    case ML_PEOPLE:
+    case ML_PEOPLE_ROLE:
+    case ML_ORIGINAL_TITLE:
+    case ML_TITLE:
+    case ML_URI:
+        return 1;
+
+    /* Integers */
+    case ML_ALBUM_ID:
+    case ML_ARTIST_ID:
+    case ML_DURATION:
+    case ML_DISC_NUMBER:
+    case ML_COUNT_MEDIA:
+    case ML_COUNT_ALBUM:
+    case ML_COUNT_PEOPLE:
+    case ML_FILESIZE:
+    case ML_FIRST_PLAYED:
+    case ML_ID:
+    case ML_IMPORT_TIME:
+    case ML_LAST_PLAYED:
+    case ML_LIMIT:
+    case ML_PLAYED_COUNT:
+    case ML_PEOPLE_ID:
+    case ML_SCORE:
+    case ML_SKIPPED_COUNT:
+    case ML_TRACK_NUMBER:
+    case ML_TYPE:
+    case ML_VOTE:
+    case ML_YEAR:
+        return 0;
+
+    /* Invalid or no following value (in a SELECT statement) */
+    default:
+        return -1;
+    }
+}
+
+/* Reference Counting Functions */
+/**
+ * @brief Increment reference count of media
+ * @param p_media The media object
+ */
+VLC_EXPORT( void, ml_gc_incref, ( ml_media_t* p_media ) );
+
+/**
+ * @brief Decrease reference count of media
+ * @param p_media The media object
+ */
+VLC_EXPORT( void, ml_gc_decref, ( ml_media_t* p_media ) );
+
+/*****************************************************************************
+ * ML Free Functions
+ *****************************************************************************/
+
+/**
+ * @brief Free a person object
+ * @param p_media Person object to free
+ * @note This function is NOT threadsafe
+ */
+static inline void ml_FreePeople( ml_person_t *p_person )
+{
+    if( p_person == NULL )
+        return;
+    ml_FreePeople( p_person->p_next );
+    free( p_person->psz_name );
+    free( p_person->psz_role );
+    free( p_person );
+}
+
+/**
+ * @brief Free only the content of a media. @see ml_media_t
+ * @param p_media Media object
+ * @note This function is NOT threadsafe.
+ */
+static inline void ml_FreeMediaContent( ml_media_t *p_media )
+{
+    free( p_media->psz_uri );
+    free( p_media->psz_title );
+    free( p_media->psz_orig_title );
+    free( p_media->psz_cover );
+    free( p_media->psz_comment );
+    free( p_media->psz_extra );
+    free( p_media->psz_genre );
+    free( p_media->psz_album );
+    free( p_media->psz_preview );
+    free( p_media->psz_language );
+    ml_FreePeople( p_media->p_people );
+    p_media->b_sparse = true;
+    p_media->i_id = 0;
+    p_media->i_type = ML_UNKNOWN;
+    p_media->i_album_id = 0;
+    p_media->i_disc_number = 0;
+    p_media->i_track_number = 0;
+    p_media->i_year = 0;
+    p_media->i_vote = 0;
+    p_media->i_score = 0;
+    p_media->i_filesize = 0;
+    p_media->i_duration = 0;
+    p_media->i_played_count = 0;
+    p_media->i_last_played = 0;
+    p_media->i_skipped_count = 0;
+    p_media->i_last_skipped = 0;
+    p_media->i_first_played = 0;
+    p_media->i_import_time = 0;
+    p_media->i_bitrate = 0;
+    p_media->i_samplerate = 0;
+    p_media->i_bpm = 0;
+}
+
+/**
+ * @brief Free a result item. @see ml_result_t
+ * @param p_result Result item to free
+ * @note This will free any strings and decref medias.
+ */
+static inline void ml_FreeResult( ml_result_t *p_result )
+{
+    if( p_result )
+    {
+        switch( p_result->type )
+        {
+            case ML_TYPE_PSZ:
+                free( p_result->value.psz );
+                break;
+            case ML_TYPE_MEDIA:
+                ml_gc_decref( p_result->value.p_media );
+                break;
+            default:
+                break;
+        }
+        free( p_result );
+    }
+}
+
+
+/**
+ * @brief Free a ml_element_t item.
+ * @param p_find Find object to free
+ * @see ml_element_t */
+static inline void ml_FreeElement( ml_element_t *p_elt )
+{
+    if( p_elt )
+    {
+        if( ml_AttributeIsString( p_elt->criteria ) )
+        {
+            free( p_elt->value.str );
+        }
+        if( p_elt->criteria == ML_PEOPLE )
+        {
+            free( p_elt->lvalue.str );
+        }
+        free( p_elt );
+    }
+}
+
+
+/**
+ * @brief Destroy a vlc_array_t of ml_result_t
+ * @param ml_result_array The result array to free
+ * @note Frees all results and contents of the results
+ */
+static inline void ml_DestroyResultArray( vlc_array_t *p_result_array )
+{
+    for( int i = 0; i < vlc_array_count( p_result_array ); i++ )
+    {
+        ml_FreeResult( ( ml_result_t* ) vlc_array_item_at_index(
+                p_result_array, i ) );
+    }
+}
+
+
+/** Get information using the *media* ID. This returns only 1 information.
+ * @note You have to free the string returned (if that's a string!).
+ **/
+#define ml_GetAlbumById( a, id )            ml_GetPsz( a, ML_ALBUM, ML_ID, id )
+#define ml_GetArtistById( a, id )           ml_GetPsz( a, ML_PEOPLE, ML_PERSON_ARTIST, ML_ID, id )
+#define ml_GetCoverUriById( a, id )         ml_GetPsz( a, ML_COVER, ML_ID, id )
+#define ml_GetEncoderById( a, id )          ml_GetPsz( a, ML_PEOPLE, ML_PERSON_ENCODER, ML_ID, id )
+#define ml_GetExtraById( a, id )            ml_GetPsz( a, ML_EXTRA, ML_ID, id )
+#define ml_GetGenreById( a, id )            ml_GetPsz( a, ML_GENRE, ML_ID, id )
+#define ml_GetOriginalTitleById( a, id )    ml_GetPsz( a, ML_ORIGINAL_TITLE, ML_ID, id )
+#define ml_GetPublisherById( a, id )        ml_GetPsz( a, ML_PEOPLE, ML_PERSON_PUBLISHER, ML_ID, id )
+#define ml_GetTitleById( a, id )            ml_GetPsz( a, ML_TITLE, ML_ID, id )
+#define ml_GetUriById( a, id )              ml_GetPsz( a, ML_URI, ML_ID, id )
+
+#define ml_GetAlbumIdById( a, id )          ml_GetInt( a, ML_ALBUM_ID, ML_ID, id )
+#define ml_GetArtistIdById( a, id )         ml_GetInt( a, ML_PEOPLE_ID, ML_PERSON_ARTIST, ML_ID, id )
+#define ml_GetDurationById( a, id )         ml_GetInt( a, ML_DURATION, ML_ID, id )
+#define ml_GetEncoderIdById( a, id )        ml_GetInt( a, ML_PEOPLE_ID, ML_PERSON_ENCODER, ML_ID, id )
+#define ml_GetLastPlayedById( a, id )       ml_GetInt( a, ML_LAST_PLAYED, ML_ID, id )
+#define ml_GetPlayedCountById( a, id )      ml_GetInt( a, ML_PLAYED_COUNT, ML_ID, id )
+#define ml_GetPublisherIdById( a, id )      ml_GetInt( a, ML_PEOPLE_ID, ML_PERSON_PUBLISHER, ML_ID, id )
+#define ml_GetScoreById( a, id )            ml_GetInt( a, ML_SCORE, ML_ID, id )
+#define ml_GetTrackNumberById( a, id )      ml_GetInt( a, ML_TRACK_NUMBER, ML_ID, id )
+#define ml_GetTypeById( a, id )             ml_GetInt( a, ML_TYPE, ML_ID, id )
+#define ml_GetYearById( a, id )             ml_GetInt( a, ML_YEAR, ML_ID, id )
+#define ml_GetVoteById( a, id )             ml_GetInt( a, ML_VOTE, ML_ID, id )
+
+/** Albums handling */
+#define ml_GetAlbumId( a, b )               ml_GetInt( a, ML_ALBUM_ID, ML_ALBUM, b )
+
+/** People handling */
+#define ml_GetArtistId( a, b )              ml_GetInt( a, ML_PERSON_ID, ML_PERSON_ARTIST, ML_PERSON, ML_PERSON_ARTIST, b )
+#define ml_GetEncoderId( a, b )             ml_GetInt( a, ML_PERSON_ID, ML_PERSON_ENCODER, ML_PERSON, ML_PERSON_ENCODER, b )
+#define ml_GetPublisherId( a, b )           ml_GetInt( a, ML_PERSON_ID, ML_PERSON_PUBLISHER, ML_PERSON, ML_PERSON_PUBLISHER, b )
+
+/** Counts handling */
+#define ml_GetMediaCount( a, ... )          __ml_GetInt( a, ML_COUNT_MEDIA,      __VA_ARGS__, ML_END )
+#define ml_GetAlbumCount( a, ... )          __ml_GetInt( a, ML_COUNT_ALBUM,      __VA_ARGS__, ML_END )
+#define ml_GetPeopleCount( a, ... )         __ml_GetInt( a, ML_COUNT_PEOPLE,     __VA_ARGS__, ML_END )
+
+
+/** Get all elements as a vlc_array_t of ml_result_t.
+ * Returns an array of data
+ **/
+static inline int __ml_Find( media_library_t *p_media_library,
+                             vlc_array_t *p_result_array, ... );
+#define ml_Find( a, b, ... )                __ml_Find( a, b, __VA_ARGS__, ML_END )
+
+#define ml_FindAlbum( a, b, ... )           __ml_Find( a, b, ML_ALBUM,           __VA_ARGS__, ML_END )
+#define ml_FindArtist( a, b, ... )          __ml_Find( a, b, ML_PERSON, ML_PERSON_ARTIST, __VA_ARGS__, ML_END )
+#define ml_FindEncoder( a, b, ... )         __ml_Find( a, b, ML_PERSON, ML_PERSON_ENCODER, __VA_ARGS__, ML_END )
+#define ml_FindGenre( a, b, ... )           __ml_Find( a, b, ML_GENRE,           __VA_ARGS__, ML_END )
+#define ml_FindMedia( a, b, ... )           __ml_Find( a, b, ML_MEDIA,           __VA_ARGS__, ML_END )
+#define ml_FindOriginalTitle( a, b, ... )   __ml_Find( a, b, ML_ORIGINAL_TITLE,  __VA_ARGS__, ML_END )
+#define ml_FindPublisher( a, b, ... )       __ml_Find( a, b, ML_PERSON, ML_PERSON_PUBLISHER, __VA_ARGS__, ML_END )
+#define ml_FindTitle( a, b, ... )           __ml_Find( a, b, ML_TITLE,           __VA_ARGS__, ML_END )
+#define ml_FindType( a, b, ... )            __ml_Find( a, b, ML_TYPE,            __VA_ARGS__, ML_END )
+#define ml_FindUri( a, b, ... )             __ml_Find( a, b, ML_URI,             __VA_ARGS__, ML_END )
+#define ml_FindYear( a, b, ... )            __ml_Find( a, b, ML_YEAR,            __VA_ARGS__, ML_END )
+
+#define ml_FindAllAlbums( a, b )            ml_FindAlbum( a, b,         ML_DISTINCT )
+#define ml_FindAllArtists( a, b )           ml_FindArtist( a, b,        ML_DISTINCT )
+#define ml_FindAllGenres( a, b )            ml_FindGenre( a, b,         ML_DISTINCT )
+#define ml_FindAllMedias( a, b )            ml_FindMedia( a, b,         ML_DISTINCT )
+#define ml_FindAllOriginalTitles( a, b )    ml_FindOriginalTitle( a, b, ML_DISTINCT )
+#define ml_FindAllPublishers( a, b, ... )   ml_FindPublisher( a, b,     ML_DISTINCT )
+#define ml_FindAllTitles( a, b )            ml_FindTitle( a, b,         ML_DISTINCT )
+#define ml_FindAllTypes( a, b )             ml_FindType( a, b,          ML_DISTINCT )
+#define ml_FindAllUris( a, b )              ml_FindUri( a, b,           ML_DISTINCT )
+#define ml_FindAllYears( a, b )             ml_FindYear( a, b,          ML_DISTINCT )
+
+#define ml_FindAlbumAdv( a, b, c )          ml_FindAdv( a, b, ML_ALBUM,         NULL, c )
+#define ml_FindArtistAdv( a, b, c )         ml_FindAdv( a, b, ML_PERSON,        ML_PERSON_ARTIST, c )
+#define ml_FindEncoderAdv( a, b, c )        ml_FindAdv( a, b, ML_PERSON,        ML_PERSON_ENCODER, c )
+#define ml_FindGenreAdv( a, b, c )          ml_FindAdv( a, b, ML_GENRE,         NULL, c )
+#define ml_FindMediaAdv( a, b, c )          ml_FindAdv( a, b, ML_MEDIA,         NULL, c )
+#define ml_FindOriginalTitleAdv( a, b, c )  ml_FindAdv( a, b, ML_ORIGINAL_TITLE,NULL, c )
+#define ml_FindPublisherAdv( a, b, c )      ml_FindAdv( a, b, ML_PUBLISHER,     ML_PERSON_PUBLISHER, c )
+#define ml_FindTitleAdv( a, b, c )          ml_FindAdv( a, b, ML_TITLE,         NULL, c )
+#define ml_FindTypeAdv( a, b, c )           ml_FindAdv( a, b, ML_TYPE,          NULL, c )
+#define ml_FindUriAdv( a, b, c )            ml_FindAdv( a, b, ML_URI,           NULL, c )
+#define ml_FindYearAdv( a, b, c )           ml_FindAdv( a, b, ML_YEAR,          NULL, c )
+
+
+/*****************************************************************************
+ * ML Object Management Functions
+ *****************************************************************************/
+
+/** Helpers for locking and unlocking */
+#define ml_LockMedia( a )      vlc_mutex_lock( &a->lock )
+#define ml_UnlockMedia( a )    vlc_mutex_unlock( &a->lock )
+
+/**
+ * @brief Destructor for ml_media_t
+ */
+VLC_EXPORT( void, media_Destroy, ( gc_object_t *p_gc ) );
+
+/**
+ * @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
+ */
+VLC_EXPORT( ml_media_t*, media_New, ( media_library_t* p_ml, int id,
+        ml_select_e select, bool reload ) );
+
+
+/* Forward declaration */
+static inline int ml_CopyPersons( ml_person_t** a, ml_person_t* b );
+
+/**
+ * @brief Copy all members of a ml_media_t to another.
+ * @param b Destination media, already allocated
+ * @param a Source media, cannot be NULL, const
+ * @note This does not check memory allocation (for strdup). It is threadsafe
+ * @todo Free b content, before inserting a?
+ */
+static inline int ml_CopyMedia( ml_media_t *b, ml_media_t *a )
+{
+    if( !a || !b ) return VLC_EGENERIC;
+    assert( a != b );
+    ml_LockMedia( a );
+    ml_LockMedia( b );
+    b->b_sparse = a->b_sparse;
+    b->i_id = a->i_id;
+    b->i_type = a->i_type;
+    b->i_album_id = a->i_album_id;
+    b->i_disc_number = a->i_disc_number;
+    b->i_track_number = a->i_track_number;
+    b->i_year = a->i_year;
+    b->i_vote = a->i_vote;
+    b->i_score = a->i_score;
+    b->i_filesize = a->i_filesize;
+    b->i_duration = a->i_duration;
+    b->i_played_count = a->i_played_count;
+    b->i_last_played = a->i_last_played;
+    b->i_skipped_count = a->i_skipped_count;
+    b->i_last_skipped = a->i_last_skipped;
+    b->i_first_played = a->i_first_played;
+    b->i_import_time = a->i_import_time;
+    b->i_bitrate = a->i_bitrate;
+    b->i_samplerate = a->i_samplerate;
+    b->i_bpm = a->i_bpm;
+    free( b->psz_uri );
+    if( a->psz_uri )
+        b->psz_uri = strdup( a->psz_uri );
+    free( b->psz_title );
+    if( a->psz_title )
+        b->psz_title = strdup( a->psz_title );
+    free( b->psz_orig_title );
+    if( a->psz_orig_title )
+        b->psz_orig_title = strdup( a->psz_orig_title );
+    free( b->psz_album );
+    if( a->psz_album )
+        b->psz_album = strdup( a->psz_album );
+    free( b->psz_cover );
+    if( a->psz_cover )
+        b->psz_cover = strdup( a->psz_cover );
+    free( b->psz_genre );
+    if( a->psz_genre )
+        b->psz_genre = strdup( a->psz_genre );
+    free( b->psz_comment );
+    if( a->psz_comment )
+        b->psz_comment = strdup( a->psz_comment );
+    free( b->psz_extra );
+    if( a->psz_extra )
+        b->psz_extra = strdup( a->psz_extra );
+    free( b->psz_preview );
+    if( a->psz_preview )
+        b->psz_preview = strdup( a->psz_preview );
+    free( b->psz_language );
+    if( a->psz_language )
+        b->psz_language = strdup( a->psz_language );
+    ml_FreePeople( b->p_people );
+    if( a->p_people )        ml_CopyPersons( &( b->p_people ), a->p_people );
+    ml_UnlockMedia( b );
+    ml_UnlockMedia( a );
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * ML Find Tree Related Functions
+ *****************************************************************************/
+#define ml_FreeFindTree( tree )          ml_GenericFreeFindTree( tree, true )
+#define ml_ShallowFreeFindTree( tree )   ml_GenericFreeFindTree( tree, false )
+/**
+ * @brief Free a find tree
+ * @param Find tree to free
+ * @param true to free any associated strings, false to not free them
+ */
+static inline void ml_GenericFreeFindTree( ml_ftree_t* tree, bool freestrings )
+{
+    if( tree == NULL )
+        return;
+    if( tree->left )
+    {
+        ml_GenericFreeFindTree( tree->left, freestrings );
+        free( tree->left );
+    }
+    if( tree->right )
+    {
+        ml_GenericFreeFindTree( tree->right, freestrings );
+        free( tree->right );
+    }
+    if( tree->op == ML_OP_NONE && ml_AttributeIsString( tree->criteria )
+            && freestrings == true)
+    {
+        free( tree->value.str );
+        if( tree->criteria == ML_PEOPLE )
+            free( tree->lvalue.str );
+    }
+}
+
+/**
+ * @brief Checks if a given find tree has leaf nodes
+ * @param Find tree
+ * @return Number of leaf nodes
+ */
+static inline int ml_FtreeHasOp( ml_ftree_t* tree )
+{
+    if( tree == NULL )
+        return 0;
+    if( tree->criteria > 0 && tree->op == ML_OP_NONE )
+        return 1;
+    else
+        return ml_FtreeHasOp( tree->left ) + ml_FtreeHasOp( tree->right );
+}
+
+
+/**
+ * @brief Connect up a find tree
+ * @param op operator to connect with
+ * If op = ML_OP_NONE, then you are connecting to a tree consisting of
+ * only SPECIAL nodes.
+ * If op = ML_OP_NOT, then right MUST be NULL
+ * op must not be ML_OP_SPECIAL, @see __ml_FtreeSpec
+ * @param left part of the tree
+ * @param right part of the tree
+ * @return Pointer to new tree
+ * @note Use the helpers!
+ */
+VLC_EXPORT( ml_ftree_t*, ml_OpConnectChilds, ( ml_op_e op, ml_ftree_t* left,
+        ml_ftree_t* right ) );
+
+/**
+ * @brief Attaches a special node to a tree
+ * @param tree Tree to attach special node to
+ * @param crit Criteria may be SORT_ASC, SORT_DESC, LIMIT or DISTINCT
+ * @param limit Limit used if LIMIT criteria used
+ * @param Sort string used if SORT criteria is used
+ * @return Pointer to new tree
+ * @note Use the helpers
+ */
+VLC_EXPORT( ml_ftree_t*, __ml_FtreeSpec, ( ml_ftree_t* tree,
+                                          ml_select_e crit,
+                                          int limit,
+                                          char* sort ) );
+
+/**
+ * @brief This function gives quick sequential adding capability
+ * @param left Tree to add to. This may be NULL
+ * @param right Tree to append. May not be NULL
+ * @return Pointer to new tree.*/
+static inline ml_ftree_t* ml_FtreeFastAnd( ml_ftree_t* left,
+                                           ml_ftree_t* right )
+{
+    if( ml_FtreeHasOp( left ) == 0 )
+    {
+        return ml_OpConnectChilds( ML_OP_NONE, left, right );
+    }
+    else
+    {
+        return ml_OpConnectChilds( ML_OP_AND, left, right );
+    }
+}
+#define ml_FtreeAnd( left, right ) ml_OpConnectChilds( ML_OP_AND, left, right )
+#define ml_FtreeOr( left, right )  ml_OpConnectChilds( ML_OP_OR, left, right )
+#define ml_FtreeNot( left )        ml_OpConnectChilds( ML_OP_NOT, left, NULL )
+
+#define ml_FtreeSpecAsc( tree, str )        __ml_FtreeSpec( tree, ML_SORT_ASC, 0, str )
+#define ml_FtreeSpecDesc( tree, str )       __ml_FtreeSpec( tree, ML_SORT_DESC, 0, str )
+#define ml_FtreeSpecLimit( tree, limit )    __ml_FtreeSpec( tree, ML_LIMIT, limit, NULL )
+#define ml_FtreeSpecDistinct( tree )        __ml_FtreeSpec( tree, ML_DISTINCT, 0, NULL )
+
+
+/*****************************************************************************
+ * ML Core Functions
+ *****************************************************************************/
+
+/**
+ * @brief Create input item from media
+ * @param p_media_library This ML instance.
+ * @param i_media_id ID of the media to use to create an input_item.
+ * @return The media item.
+ */
+static inline input_item_t* ml_CreateInputItem(
+        media_library_t *p_media_library, int i_media_id )
+{
+    return p_media_library->functions.pf_InputItemFromMedia( p_media_library,
+                                                             i_media_id );
+}
+
+/**
+ * @brief Search in the database according some criterias
+ *
+ * @param p_media_library the media library object
+ * @param result a pointer to a result array
+ * @param ... parameters to select the data
+ * @return VLC_SUCCESS or an error
+ */
+static inline int __ml_Find( media_library_t *p_media_library,
+                             vlc_array_t *p_result_array, ... )
+{
+    va_list args;
+    int returned;
+
+    va_start( args, p_result_array );
+    returned = p_media_library->functions.pf_Find( p_media_library,
+                                                   p_result_array, args );
+    va_end( args );
+
+    return returned;
+}
+
+
+/**
+ * @brief Search in the database according some criterias (threaded)
+ * @param p_media_library the media library object
+ * @param result_array a pointer to a result array
+ * @param result_type type of data to retrieve
+ * @param psz_lvalue This should contain any necessary lvalue/key
+ * for the given result_type. Used for ML_PEOPLE. Otherwise NULL
+ * @param args parameters to select the data
+ * @return VLC_SUCCESS or an error
+ */
+static inline int ml_FindAdv( media_library_t *p_media_library,
+                              vlc_array_t *p_result_array,
+                              ml_select_e result_type,
+                              char* psz_lvalue,
+                              ml_ftree_t *tree )
+{
+    return p_media_library->functions.pf_FindAdv( p_media_library,
+                                                  p_result_array,
+                                                  result_type,
+                                                  psz_lvalue,
+                                                  tree );
+}
+
+
+/**
+ * @brief Find a value in the ML database, fill p_result with it.
+ * @param p_media_library Media library object
+ * @param p_result Object to put result into
+ * @param Args [ SelectType [ PersonType ] Value ] ... ML_END
+ * @note Do not use this function directly.
+ */
+static inline int __ml_GetValue( media_library_t *p_media_library,
+                                  ml_result_t *p_result,
+                                  va_list args )
+{
+    vlc_array_t *p_result_array = vlc_array_new();
+    int i_ret = p_media_library->functions.pf_Find( p_media_library,
+                                                    p_result_array,
+                                                    args );
+    if( i_ret != VLC_SUCCESS )
+        goto exit;
+    if( vlc_array_count( p_result_array ) > 0 )
+        memcpy( p_result,
+                ( ml_result_t* ) vlc_array_item_at_index( p_result_array, 0 ),
+                sizeof( ml_result_t) );
+    else
+        i_ret = VLC_EGENERIC;
+
+exit:
+    /* Note: Do not free the results, because of memcpy */
+    vlc_array_destroy( p_result_array );
+    return i_ret;
+}
+
+/**
+ * @brief Search an INTEGER in the database
+ * This uses a Query but returns only one integer (>0), or an error code.
+ *
+ * @param p_media_library the media library object
+ * @param va_args parameters to select the data
+ * @return Found INTEGER >= 0 or an error
+ */
+#define ml_GetInt( ml, ... ) __ml_GetInt( ml, __VA_ARGS__, ML_LIMIT, 1, ML_END )
+static inline int __ml_GetInt( media_library_t *p_media_library, ... )
+{
+    va_list args;
+    va_start( args, p_media_library );
+    ml_result_t result;
+    int i_ret = __ml_GetValue( p_media_library, &result, args );
+    va_end( args );
+    if( i_ret != VLC_SUCCESS )
+        return i_ret;
+    else
+        return result.value.i;
+}
+
+
+/**
+ * @brief Search a string (VARCHAR) in the database
+ * This uses a Query but returns only one integer (>0), or an error code.
+ *
+ * @param p_media_library the media library object
+ * @param va_args parameters to select the data
+ * @return Found string, or NULL if not found or in case of error
+ */
+#define ml_FindPsz( ml, ... ) __ml_GetPsz( ml, __VA_ARGS__, ML_LIMIT, 1, ML_END )
+static inline char* __ml_GetPsz( media_library_t *p_media_library, ... )
+{
+    va_list args;
+    va_start( args, p_media_library );
+    ml_result_t result;
+    int i_ret = __ml_GetValue( p_media_library, &result, args );
+    va_end( args );
+    if( i_ret != VLC_SUCCESS )
+        return NULL;
+    else
+        return result.value.psz; // no need to duplicate
+}
+
+/**
+ * @brief Generic update in Media Library database
+ *
+ * @param p_media_library the media library object
+ * @param selected_type the type of the element we're selecting
+ * @param where list of ids/uris to be changed
+ * @param changes list of changes to make in the entries
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+static inline int ml_Update( media_library_t *p_media_library,
+                             ml_select_e selected_type,
+                             const char* psz_lvalue,
+                             ml_ftree_t *where,
+                             vlc_array_t *changes )
+{
+    return p_media_library->functions.pf_Update( p_media_library,
+                                                 selected_type, psz_lvalue,
+                                                 where, changes );
+}
+
+/**
+ * @brief Update a given table
+ * @param p_media_library The media library object
+ * @param selected_type The table to update
+ * @param psz_lvalue The role of the person if selected_type = ML_PEOPLE
+ * @param id The id of the row to update
+ * @param ... The update data. [SelectType [RoleType] Value]
+ */
+#define ml_UpdateSimple( ml, sel, lval, id, ... ) \
+        __ml_UpdateSimple( ml, sel, lval, id, __VA_ARGS__, ML_END )
+VLC_EXPORT( int, __ml_UpdateSimple, ( media_library_t *p_media_library,
+                                     ml_select_e selected_type,
+                                     const char* psz_lvalue,
+                                     int id, ... ) );
+
+/**
+ * @brief Generic DELETE function
+ * Delete a media and all its references which don't point
+ * to anything else.
+ *
+ * @param p_media_library This media_library_t object
+ * @param id the id of the media to delete
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+static inline int
+ml_DeleteSimple( media_library_t *p_media_library, int id )
+{
+    vlc_array_t* p_where = vlc_array_new();
+    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 );
+    int i_return =  p_media_library->functions.pf_Delete( p_media_library,
+            p_where );
+    free( p_find );
+    vlc_array_destroy( p_where );
+    return i_return;
+}
+
+/**
+ * @brief Delete many medias in the media library
+ * @param p_media_library Media library object
+ * @param p_array Array of ids to delete
+ * @return VLC_SUCCESS or VLC_EGENERIC
+ */
+static inline int
+ml_Delete( media_library_t *p_media_library, vlc_array_t* p_array )
+{
+    return p_media_library->functions.pf_Delete( p_media_library,
+                                                        p_array );
+}
+
+
+/*****************************************************************************
+ * ML Person Related Functions
+ *****************************************************************************/
+
+/**
+ * @brief Create and append a person object to the given list
+ * @param pp_person pointer to person list. Set the address to null to create new list
+ * @param i_role The role of the person
+ * @param psz_name The name string. Will be strdup'd
+ * @param i_id The id in the database
+ * @note This function is NOT thread safe. Please lock any associated media
+ */
+static inline int ml_CreateAppendPersonAdv( ml_person_t **pp_person,
+        const char* psz_role, const char* psz_name, int i_id )
+{
+    assert( i_id || ( psz_name && *psz_name && psz_role && *psz_role ) );
+    if( !pp_person )
+        return VLC_EGENERIC;
+    if( *pp_person != NULL )
+        return ml_CreateAppendPersonAdv( &((**pp_person).p_next),
+                                         psz_role, psz_name, i_id);
+    *pp_person = ( ml_person_t * ) calloc( 1, sizeof( ml_person_t ) );
+    (*pp_person)->psz_name = (psz_name && *psz_name) ? strdup( psz_name ): NULL;
+    (*pp_person)->psz_role = (psz_role && *psz_role) ? strdup( psz_role ): NULL;
+    (*pp_person)->i_id = i_id;
+    (*pp_person)->p_next = NULL;
+    return VLC_SUCCESS;
+}
+
+/**
+ * @brief Create and append a person object to the given list
+ * @param pp_person pointer to person list.
+ * Set the address to NULL to create a new list
+ * @param personfrom Person object to copy from
+ * @note Ignores the next variable and copies only the variables.
+ * Uses ml_CreateAppendPersonAdv
+ * @note This function is NOT threadsafe
+ */
+static inline int ml_CreateAppendPerson( ml_person_t **pp_person,
+                                         ml_person_t *p_personfrom )
+{
+    return ml_CreateAppendPersonAdv( pp_person,
+                                     p_personfrom->psz_role,
+                                     p_personfrom->psz_name,
+                                     p_personfrom->i_id );
+}
+
+/**
+ * @brief Copy one person list into another
+ * @param a To list
+ * @param b From list
+ * @note On errors, you have to free any allocated persons yourself
+ * @note This function is NOT threadsafe. Please ensure your medias are locked
+ */
+static inline int ml_CopyPersons( ml_person_t** a, ml_person_t* b )
+{
+    int i_ret;
+    while( b )
+    {
+        i_ret = ml_CreateAppendPerson( a, b );
+        if( i_ret != VLC_SUCCESS )
+            return i_ret;
+        b = b->p_next;
+    }
+    return VLC_SUCCESS;
+}
+
+
+/**
+ * @brief Returns a person list of given type
+ * @param p_ml The ML object
+ * @param p_media The Media object
+ * @param i_type The person type
+ * @note This function is thread safe
+ */
+VLC_EXPORT( ml_person_t*, ml_GetPersonsFromMedia, ( media_library_t* p_ml,
+                                                    ml_media_t* p_media,
+                                                    const char *psz_role ) );
+
+
+#define ml_GetAlbumArtistsFromMedia( a, b ) ml_GetPersonsFromMedia( a, b, ML_PERSON_ALBUM_ARTIST );
+#define ml_GetArtistsFromMedia( a, b )      ml_GetPersonsFromMedia( a, b, ML_PERSON_ARTIST );
+#define ml_GetEncodersFromMedia( a, b )     ml_GetPersonsFromMedia( a, b, ML_PERSON_ENCODER );
+#define ml_GetPublishersFromMedia( a, b )   ml_GetPersonsFromMedia( a, b, ML_PERSON_PUBLISHER );
+
+/**
+ * @brief Delete a certain type of people from a media
+ * @param p_media Media to delete from
+ * @param i_type Type of person to delete
+ * @note This function is threadsafe
+ */
+VLC_EXPORT( void, ml_DeletePersonTypeFromMedia, ( ml_media_t* p_media,
+                                                 const char *psz_role ) );
+
+
+/**
+ * @brief Creates and adds the playlist based on a given find tree
+ * @param p_ml Media library object
+ * @param p_tree Find tree to create SELECT
+ */
+
+VLC_EXPORT( void, ml_PlaySmartPlaylistBasedOn, ( media_library_t* p_ml,
+                                                ml_ftree_t* p_tree ) );
+
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* VLC_MEDIA_LIBRARY_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index e3198a4..8470e8d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -76,6 +76,7 @@ pluginsinclude_HEADERS = \
 	../include/vlc_md5.h \
 	../include/vlc_messages.h \
 	../include/vlc_meta.h \
+	../include/vlc_media_library.h \
 	../include/vlc_modules.h \
 	../include/vlc_mtime.h \
 	../include/vlc_objects.h \
@@ -425,6 +426,7 @@ SOURCES_libvlc_common = \
 	misc/update_crypto.c \
 	misc/xml.c \
 	misc/devices.c \
+	misc/media_library.c \
 	extras/libc.c \
 	misc/filter.c \
 	misc/filter_chain.c \
diff --git a/src/libvlc-module.c b/src/libvlc-module.c
index ca3ba77..6694cb6 100644
--- a/src/libvlc-module.c
+++ b/src/libvlc-module.c
@@ -1192,6 +1192,10 @@ static const char *const ppsz_albumart_descriptions[] =
     "The media library is automatically saved and reloaded each time you " \
     "start VLC." )
 
+#define LOAD_ML_TEXT N_( "Load Media Library" )
+#define LOAD_ML_LONGTEXT N_( \
+    "Enable this option to load the SQL-based Media Library at VLC startup" )
+
 #define PLTREE_TEXT N_("Display playlist tree")
 #define PLTREE_LONGTEXT N_( \
     "The playlist can use a tree to categorize some items, like the " \
@@ -2007,6 +2011,10 @@ vlc_module_begin ()
     add_bool( "play-and-stop", 0, NULL, PAS_TEXT, PAS_LONGTEXT, false )
         change_safe()
     add_bool( "media-library", 1, NULL, ML_TEXT, ML_LONGTEXT, false )
+#if defined( MEDIA_LIBRARY )
+    add_bool( "load-media-library-on-startup", 1, NULL, LOAD_ML_TEXT,
+            LOAD_ML_LONGTEXT, false )
+#endif
     add_bool( "playlist-tree", 0, NULL, PLTREE_TEXT, PLTREE_LONGTEXT, false )
 
     add_string( "open", "", NULL, OPEN_TEXT, OPEN_LONGTEXT, false )
diff --git a/src/libvlc.c b/src/libvlc.c
index 9860d54..6a03c67 100644
--- a/src/libvlc.c
+++ b/src/libvlc.c
@@ -79,6 +79,7 @@
 #   include <hal/libhal.h>
 #endif
 
+#include <vlc_media_library.h>
 #include <vlc_playlist.h>
 #include <vlc_interface.h>
 
@@ -261,6 +262,7 @@ libvlc_int_t * libvlc_InternalCreate( void )
 
     priv = libvlc_priv (p_libvlc);
     priv->p_playlist = NULL;
+    priv->p_ml = NULL;
     priv->p_dialog_provider = NULL;
     priv->p_vlm = NULL;
 
@@ -829,6 +831,23 @@ int libvlc_InternalInit( libvlc_int_t *p_libvlc, int i_argc,
     playlist_Activate( p_playlist );
     vlc_object_attach( p_playlist, p_libvlc );
 
+#if defined(MEDIA_LIBRARY)
+    /* Get the ML */
+    if( var_GetBool( p_libvlc, "load-media-library-on-startup" ) == true )
+    {
+        priv->p_ml = __ml_Create( VLC_OBJECT( p_libvlc ), NULL );
+        if( !priv->p_ml )
+        {
+            msg_Err( p_libvlc, "ML initialization failed" );
+            return VLC_EGENERIC;
+        }
+    }
+    else
+    {
+        priv->p_ml = NULL;
+    }
+#endif
+
     /* Add service discovery modules */
     psz_modules = config_GetPsz( p_playlist, "services-discovery" );
     if( psz_modules && *psz_modules )
@@ -1039,6 +1058,16 @@ void libvlc_InternalCleanup( libvlc_int_t *p_libvlc )
     }
 #endif
 
+#if defined(MEDIA_LIBRARY)
+    media_library_t* p_ml = priv->p_ml;
+    if( p_ml )
+    {
+        __ml_Destructor( VLC_OBJECT( p_ml ) );
+        vlc_object_release( p_ml );
+        libvlc_priv(p_playlist->p_libvlc)->p_ml = NULL;
+    }
+#endif
+
     /* Free playlist */
     /* Any thread still running must not assume pl_Hold() succeeds. */
     msg_Dbg( p_libvlc, "removing playlist" );
diff --git a/src/libvlc.h b/src/libvlc.h
index 0889083..798d768 100644
--- a/src/libvlc.h
+++ b/src/libvlc.h
@@ -230,7 +230,8 @@ typedef struct libvlc_priv_t
 
     /* Singleton objects */
     module_t          *p_memcpy_module;  ///< Fast memcpy plugin used
-    playlist_t        *p_playlist; //< the playlist singleton
+    playlist_t        *p_playlist; ///< the playlist singleton
+    media_library_t   *p_ml;    ///< the ML singleton
     vlm_t             *p_vlm;  ///< the VLM singleton (or NULL)
     vlc_object_t      *p_dialog_provider; ///< dialog provider
     httpd_t           *p_httpd; ///< HTTP daemon (src/network/httpd.c)
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 1af3f51..d17cf97 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -224,6 +224,20 @@ libvlc_Quit
 LocaleFree
 make_URI
 mdate
+__ml_Create
+__ml_Destructor
+__ml_Hold
+__ml_Release
+media_Destroy
+media_New
+ml_gc_incref
+ml_gc_decref
+ml_OpConnectChilds
+__ml_FtreeSpec
+__ml_UpdateSimple
+ml_GetPersonsFromMedia
+ml_DeletePersonTypeFromMedia
+ml_PlaySmartPlaylistBasedOn
 module_config_free
 module_config_get
 module_exists
diff --git a/src/misc/media_library.c b/src/misc/media_library.c
new file mode 100644
index 0000000..a9544ca
--- /dev/null
+++ b/src/misc/media_library.c
@@ -0,0 +1,486 @@
+/*****************************************************************************
+ * media_library.c: SQL-based media library: ML creators and destructors
+ *****************************************************************************
+ * Copyright (C) 2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Srikanth Raju <srikiraju at gmail dot com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#if !defined( __LIBVLC__ )
+  #error You are not libvlc or one of its plugins. You cannot include this file
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <vlc_media_library.h>
+#include "../libvlc.h"
+
+/**
+ * @brief Destroy the medialibrary object
+ * @param Parent object that holds the media library object
+ */
+void __ml_Destructor( vlc_object_t * p_this )
+{
+    media_library_t* p_ml = ( media_library_t* )p_this;
+    module_unneed( p_ml, p_ml->p_module );
+}
+
+/**
+ * @brief Create an instance of the media library
+ * @param p_this Parent object
+ * @param psz_name Name which is passed to module_need (not needed)
+ * @return p_ml created and attached, module loaded. NULL if
+ * not able to load
+ */
+media_library_t *__ml_Create( vlc_object_t *p_this, char *psz_name )
+{
+#if !defined(MEDIA_LIBRARY)
+    return NULL;
+#else
+    media_library_t *p_ml = NULL;
+
+    p_ml = ( media_library_t * ) vlc_custom_create(
+                                p_this, sizeof( media_library_t ),
+                                VLC_OBJECT_GENERIC, "media-library" );
+    if( !p_ml )
+    {
+        msg_Err( p_this, "unable to create media library object" );
+        return NULL;
+    }
+    vlc_object_attach( p_ml, p_this );
+
+    p_ml->p_module = module_need( p_ml, "media-library", psz_name, false );
+    if( !p_ml->p_module )
+    {
+        vlc_object_release( p_ml );
+        msg_Err( p_this, "Media Library provider not found" );
+        return NULL;
+    }
+
+    return p_ml;
+#endif
+}
+
+/**
+ * @brief Acquire a reference to the media library singleton
+ * @param p_this Object that holds the reference
+ * @return media_library_t The ml object. NULL if not compiled with
+ * media library or if unable to load
+ */
+media_library_t* __ml_Hold( vlc_object_t* p_this )
+{
+#if !defined(MEDIA_LIBRARY)
+    return NULL;
+#else
+    media_library_t* p_ml = NULL;
+    p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
+    assert( VLC_OBJECT( p_ml ) != p_this );
+    if( p_ml == NULL &&
+            var_GetBool( p_this->p_libvlc, "load-media-library-on-startup" ) == false )
+    {
+        libvlc_priv (p_this->p_libvlc)->p_ml
+            = __ml_Create( VLC_OBJECT( p_this->p_libvlc ), NULL );
+        p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
+    }
+    if( p_ml )
+        vlc_object_hold( p_ml );
+    return p_ml;
+#endif
+}
+
+/**
+ * @brief Release a reference to the media library singleton
+ * @param p_this Object that holds the reference
+ */
+void __ml_Release( vlc_object_t* p_this )
+{
+#if defined(MEDIA_LIBRARY)
+    media_library_t* p_ml;
+    p_ml = libvlc_priv (p_this->p_libvlc)->p_ml;
+    if( p_ml == NULL )
+    {
+        msg_Warn( p_this->p_libvlc , "Spurious release ML called");
+        return;
+    }
+    assert( VLC_OBJECT( p_ml ) != p_this );
+    vlc_object_release( p_ml );
+#endif
+}
+
+/**
+ * @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* media_New( media_library_t* p_ml, int id,
+        ml_select_e select, bool reload )
+{
+    return p_ml->functions.pf_GetMedia( p_ml, id, select, reload );
+}
+
+/**
+ * @brief Destructor for ml_media_t
+ */
+void media_Destroy( gc_object_t *p_gc )
+{
+    ml_media_t* p_media = vlc_priv( p_gc, ml_media_t );
+    vlc_mutex_destroy( &p_media->lock );
+    ml_FreeMediaContent( p_media );
+    free( p_media );
+}
+
+/**
+ * @brief Increment reference count of media
+ * @param p_media The media object
+ */
+void ml_gc_incref( ml_media_t* p_media )
+{
+    vlc_gc_incref( p_media );
+}
+
+/**
+ * @brief Decrease reference count of media
+ * @param p_media The media object
+ */
+void ml_gc_decref( ml_media_t* p_media )
+{
+    if( p_media->b_pool )
+    {
+        /* The below code is from vlc_release(). */
+        unsigned refs;
+        gc_object_t* p_gc = &p_media->vlc_gc_data;
+        assert( p_gc );
+        assert ((((uintptr_t)&p_gc->refs) & (sizeof (void *) - 1)) == 0); /* alignment */
+
+#if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+        refs = __sync_sub_and_fetch (&p_gc->refs, 1);
+#elif defined (WIN64)
+        refs = InterlockedDecrement64 (&p_gc->refs);
+#elif defined (WIN32)
+        refs = InterlockedDecrement (&p_gc->refs);
+#elif defined(__APPLE__)
+        refs = OSAtomicDecrement32Barrier((int*)&p_gc->refs);
+#else
+        vlc_spin_lock (&p_gc->spin);
+        refs = --p_gc->refs;
+        vlc_spin_unlock (&p_gc->spin);
+#endif
+        /* Rely on mlcache garbage collection from watch system */
+        assert (refs > 0);
+    }
+    else
+        vlc_gc_decref( p_media );
+}
+
+
+/**
+ * @brief Update a given table
+ * @param p_media_library The media library object
+ * @param selected_type The table to update
+ * @param psz_lvalue The role of the person if selected_type = ML_PEOPLE
+ * @param id The id of the row to update
+ * @param ... The update data. [SelectType [RoleType] Value] ... ML_END
+ */
+int __ml_UpdateSimple( media_library_t *p_media_library,
+                                     ml_select_e selected_type,
+                                     const char* psz_lvalue,
+                                     int id, ... )
+{
+    ml_element_t *update = NULL;
+    vlc_array_t *array = vlc_array_new();
+    int i_ret = VLC_SUCCESS;
+
+    va_list args;
+    va_start( args, id );
+
+    ml_select_e sel;
+    do {
+        update = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
+        sel = ( ml_select_e ) va_arg( args, int );
+        update->criteria = sel;
+        if( sel == ML_PEOPLE )
+        {
+            update->lvalue.str = va_arg( args, char* );
+            update->value.str = va_arg( args, char* );
+            vlc_array_append( array, update );
+        }
+        else if( sel == ML_PEOPLE_ID )
+        {
+           update->lvalue.str = va_arg( args, char* );
+           update->value.i = va_arg( args, int );
+           vlc_array_append( array, update );
+        }
+        else if( sel == ML_PEOPLE_ROLE )
+        {
+#ifndef NDEBUG
+            msg_Dbg( p_media_library,
+                     "this argument is invalid for Update: %d",
+                     (int)sel );
+#endif
+        }
+        else
+        {
+            switch( ml_AttributeIsString( sel ) )
+            {
+                case -1:
+                    if( sel != ML_END )
+                    {
+#ifndef NDEBUG
+                        msg_Dbg( p_media_library,
+                                 "this argument is invalid for Update: %d",
+                                 (int)sel );
+#endif
+                        i_ret = VLC_EGENERIC;
+                    }
+                    else if( sel == ML_END )
+                        vlc_array_append( array, update );
+                    break;
+                case 0:
+                    update->value.str = va_arg( args, char* );
+                    vlc_array_append( array, update );
+                    break;
+                case 1:
+                    update->value.i = va_arg( args, int );
+                    vlc_array_append( array, update );
+                    break;
+            }
+        }
+    } while( sel != ML_END );
+
+    va_end( args );
+
+    ml_ftree_t* p_where = NULL;
+    ml_ftree_t* find = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
+    find->criteria = ML_ID;
+    find->value.i = id ;
+    find->comp = ML_COMP_EQUAL;
+    p_where = ml_FtreeFastAnd( p_where, find );
+
+    /* Let's update the database ! */
+    if( i_ret == VLC_SUCCESS )
+        i_ret = ml_Update( p_media_library, selected_type, psz_lvalue,
+                            p_where, array );
+
+    /* Destroying array */
+    for( int i = 0; i < vlc_array_count( array ); i++ )
+    {
+        free( vlc_array_item_at_index( array, i ) );
+    }
+    vlc_array_destroy( array );
+    ml_FreeFindTree( p_where );
+
+    return i_ret;
+}
+
+/**
+ * @brief Connect up a find tree
+ * @param op operator to connect with
+ * If op = ML_OP_NONE, then you are connecting to a tree consisting of
+ * only SPECIAL nodes.
+ * If op = ML_OP_NOT, then right MUST be NULL
+ * op must not be ML_OP_SPECIAL, @see __ml_FtreeSpec
+ * @param left part of the tree
+ * @param right part of the tree
+ * @return Pointer to new tree
+ * @note Use the helpers!
+ */
+ml_ftree_t* ml_OpConnectChilds( ml_op_e op, ml_ftree_t* left,
+        ml_ftree_t* right )
+{
+    /* Use this Op for fresh trees (with only special nodes/none at all!) */
+    if( op == ML_OP_NONE )
+    {
+        assert( ml_FtreeHasOp( left ) == 0 );
+        if( left == NULL )
+            return right;
+        /* Percolate down tree only for special nodes */
+        assert( left->op == ML_OP_SPECIAL );
+        if( left->left == NULL )
+        {
+            left->left = right;
+            return left;
+        }
+        else
+        {
+            return ml_OpConnectChilds( ML_OP_NONE, left->left, right );
+        }
+    }
+    else if( op == ML_OP_NOT )
+    {
+        assert( right == NULL && left != NULL );
+        assert( ml_FtreeHasOp( left ) > 0 );
+    }
+    else if( op == ML_OP_SPECIAL )
+    {
+        assert( 0 );
+    }
+    else
+    {
+        assert( right != NULL && left != NULL );
+        assert( ml_FtreeHasOp( left ) > 0 );
+        assert( ml_FtreeHasOp( right ) > 0 );
+    }
+    ml_ftree_t* p_parent = (ml_ftree_t *) calloc( 1, sizeof( ml_ftree_t ) );
+    p_parent->op = op;
+    p_parent->left = left;
+    p_parent->right = right;
+    return p_parent;
+}
+
+/**
+ * @brief Attaches a special node to a tree
+ * @param tree Tree to attach special node to
+ * @param crit Criteria may be SORT_ASC, SORT_DESC, LIMIT or DISTINCT
+ * @param limit Limit used if LIMIT criteria used
+ * @param Sort string used if SORT criteria is used
+ * @return Pointer to new tree
+ * @note Use the helpers
+ */
+ml_ftree_t* __ml_FtreeSpec( ml_ftree_t* tree,
+                                          ml_select_e crit,
+                                          int limit,
+                                          char* sort )
+{
+    assert( crit == ML_SORT_ASC || crit == ML_LIMIT || crit == ML_SORT_DESC ||
+            crit == ML_DISTINCT );
+    ml_ftree_t* right = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
+    right->criteria = crit;
+    if( crit == ML_LIMIT )
+        right->value.i = limit;
+    else if( crit == ML_SORT_ASC || crit == ML_SORT_DESC )
+        right->value.str = strdup( sort );
+    right->op = ML_OP_NONE;
+    ml_ftree_t* p_parent = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
+    p_parent->right = right;
+    p_parent->op = ML_OP_SPECIAL;
+    p_parent->left = tree;
+    return p_parent;
+}
+
+
+/**
+ * @brief Creates and adds the playlist based on a given find tree
+ * @param p_ml Media library object
+ * @param p_tree Find tree to create SELECT
+ */
+void ml_PlaySmartPlaylistBasedOn( media_library_t* p_ml,
+                                                ml_ftree_t* p_tree )
+{
+    assert( p_tree );
+    vlc_array_t* p_results = vlc_array_new();
+    ml_FindAdv( p_ml, p_results, ML_ID, NULL, p_tree );
+    playlist_t* p_pl = pl_Hold( p_ml );
+    playlist_Lock( p_pl );
+    playlist_Clear( p_pl, true );
+    for( int i = 0; i < vlc_array_count( p_results ); i++ )
+    {
+        ml_result_t* p_res = ( ml_result_t* ) vlc_array_item_at_index( p_results, i );
+        input_item_t* p_item;
+        if( p_res )
+        {
+            p_item = ml_CreateInputItem( p_ml, p_res->value.i );
+            playlist_AddInput( p_pl, p_item, PLAYLIST_APPEND,
+                           PLAYLIST_END, true, true );
+        }
+    }
+    playlist_Unlock( p_pl );
+    ml_DestroyResultArray( p_results );
+    vlc_array_destroy( p_results );
+}
+
+/**
+ * @brief Returns a person list of given type
+ * @param p_ml The ML object
+ * @param p_media The Media object
+ * @param i_type The person type
+ * @note This function is thread safe
+ */
+ml_person_t*  ml_GetPersonsFromMedia( media_library_t* p_ml,
+                                                    ml_media_t* p_media,
+                                                    const char *psz_role )
+{
+    VLC_UNUSED( p_ml );
+    assert( p_media != NULL );
+    ml_person_t* p_return = NULL;
+    ml_LockMedia( p_media );
+    ml_person_t* p_person = p_media->p_people;
+    while( p_person )
+    {
+        if( strcmp( p_person->psz_role, psz_role ) == 0 )
+        {
+            int i_ret = ml_CreateAppendPerson( &p_return, p_person );
+            if( i_ret != VLC_SUCCESS )
+            {
+                ml_UnlockMedia( p_media );
+                ml_FreePeople( p_return );
+                return NULL;
+            }
+        }
+        p_person = p_person->p_next;
+    }
+    ml_UnlockMedia( p_media );
+    //TODO: Fill up empty names + clean up list
+    return p_return;
+}
+
+/**
+ * @brief Delete a certain type of people from a media
+ * @param p_media Media to delete from
+ * @param i_type Type of person to delete
+ * @note This function is threadsafe
+ */
+void ml_DeletePersonTypeFromMedia( ml_media_t* p_media, const char *psz_role )
+{
+    assert( p_media );
+    ml_LockMedia( p_media );
+    ml_person_t* p_prev = NULL;
+    ml_person_t* p_person = p_media->p_people;
+
+    while( p_person )
+    {
+        if( strcmp( p_person->psz_role, psz_role ) == 0 )
+        {
+            if( p_prev == NULL )
+            {
+                p_media->p_people = p_person->p_next;
+                p_person->p_next = NULL;
+                ml_FreePeople( p_person );
+                p_person = p_media->p_people;
+            }
+            else
+            {
+                p_prev->p_next = p_person->p_next;
+                p_person->p_next = NULL;
+                ml_FreePeople( p_person );
+                p_person = p_prev->p_next;
+            }
+        }
+        else
+        {
+            p_prev = p_person;
+            p_person = p_person->p_next;
+        }
+    }
+    ml_UnlockMedia( p_media );
+}
+
-- 
1.6.0.4

