From 49fd520ae96e62938038a750c06c784e5eb3d22e Mon Sep 17 00:00:00 2001 From: Srikanth Raju Date: Mon, 21 Jun 2010 22:24:53 +0530 Subject: [PATCH] ML: Include Media Library header --- include/vlc_media_library.h | 1281 +++++++++++++++++++++++++++++++++++ 1 file changed, 1281 insertions(+) create mode 100644 include/vlc_media_library.h diff --git a/include/vlc_media_library.h b/include/vlc_media_library.h new file mode 100644 index 0000000000..15ef0251b4 --- /dev/null +++ b/include/vlc_media_library.h @@ -0,0 +1,1281 @@ +/***************************************************************************** + * vlc_media_library.h: SQL-based media library + ***************************************************************************** + * Copyright (C) 2008-2010 the VideoLAN Team and AUTHORS + * $Id$ + * + * Authors: Antoine Lejeune + * Jean-Philippe André + * Rémi Duraffort + * Adrien Maglo + * Srikanth Raju + * + * 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 VLC_MEDIA_LIBRARY_H +# define VLC_MEDIA_LIBRARY_H + +# ifdef __cplusplus +extern "C" { +# endif + +#include +#include +#include + +/***************************************************************************** + * ML Enums + *****************************************************************************/ + +#define ML_PERSON_ARTIST "Artist" +#define ML_PERSON_ALBUM_ARTIST "Album Artist" +#define ML_PERSON_ENCODER "Encoder" +#define ML_PERSON_PUBLISHER "Publisher" + + +#define ml_priv( gc, t ) ((t *)(((char *)(gc)) - offsetof(t, ml_gc_data))) + +/** 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 */ + /* FIXME: 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 media_library_t media_library_t; +typedef struct media_library_sys_t media_library_sys_t; + +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; + + +typedef struct ml_gc_object_t +{ + vlc_spinlock_t spin; + bool pool; + uintptr_t refs; + void (*pf_destructor) (struct ml_gc_object_t *); +} ml_gc_object_t; + +#define ML_GC_MEMBERS ml_gc_object_t ml_gc_data; + +/** 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 + * + * This is the main structure holding the meta data in ML. + * @see b_sparse indicates whether the media struct has valid values + * in its Extra fields. Otherwise, it must be loaded with the API + * function. + * @see i_id indicates whether this struct is saved in the ML if i_id > 0 + * Otherwise, it can be added to the database + */ +struct ml_media_t +{ + ML_GC_MEMBERS + vlc_mutex_t lock; /**< Mutex for multithreaded access */ + bool b_sparse; /**< Specifies if media is loaded fully */ + ml_type_e i_type; /**< Type of the media (ml_type_e) */ + int8_t i_vote; /**< User vote */ + int16_t i_disc_number; /**< Disc number of media */ + int16_t i_track_number; /**< Track number */ + int16_t i_year; /**< Year of release */ + int32_t i_id; /**< Media ID in the database */ + int32_t i_score; /**< Score computed about the media */ + int32_t i_album_id; /**< Album id */ + int32_t i_played_count; /**< How many time the media was played */ + int32_t i_skipped_count; /**< No. of times file was skipped */ + int32_t i_bitrate; /**< Extra: Bitrate of the media */ + int32_t i_samplerate; /**< Extra: Samplerate of the media */ + int32_t i_bpm; /**< Extra: Beats per minute */ + char *psz_uri; /**< URI to find the media */ + char *psz_title; /**< Title of the media */ + char *psz_orig_title; /**< Original title (mainly for movies) */ + char *psz_album; /**< Name of the album */ + char *psz_cover; /**< URI of the cover */ + char *psz_genre; /**< Genre of the media */ + char *psz_preview; /**< Preview thumbnail for video, if any */ + char *psz_comment; /**< Comment or description about media */ + char *psz_language; /**< Extra: Language */ + char *psz_extra; /**< Extra: Some extra datas like lyrics */ + ml_person_t *p_people; /**< Extra: People associated with this + media This meta holds only one + artist if b_sparse = true */ + int64_t i_filesize; /**< Size of the file */ + mtime_t i_duration; /**< Duration in microseconds */ + mtime_t i_last_played; /**< Time when the media was last played */ + 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 */ + +}; + + +/** + * @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 + * + * Let [expr] indicate a valid expression + * [expr] = [expr] AND [expr], where the left and right are respective + * [expr] = [expr] OR [expr] + * [expr] = [expr] NOT [NULL] + * [expr] = [expr] SPEC [spec_expr] + * [expr] = [criteria=val] + * [spec_expr] = [DISTINCT/LIMIT/ASC/DESC = val ] + */ +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_Destroy, ( 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 + */ +static inline void ml_gc_incref( ml_media_t* p_media ) +{ + unsigned refs; + ml_gc_object_t* p_gc = &p_media->ml_gc_data; + assert( p_gc ); + + vlc_spin_lock (&p_gc->spin); + refs = ++p_gc->refs; + vlc_spin_unlock (&p_gc->spin); + assert (refs != 1); /* there had to be a reference already */ +} + +/** + * @brief Decrease reference count of media + * @param p_media The media object + */ +static inline void ml_gc_decref( ml_media_t* p_media ) +{ + /* The below code is from vlc_release(). */ + unsigned refs; + bool pool; + ml_gc_object_t* p_gc = &p_media->ml_gc_data; + assert( p_gc ); + + vlc_spin_lock (&p_gc->spin); + assert( p_gc->refs != 0 ); + refs = --p_gc->refs; + pool = p_gc->pool; + assert( ( refs != 0 && p_gc->pool == true ) || ( refs == 0 && p_gc->pool == false ) ); + vlc_spin_unlock (&p_gc->spin); + + if( refs == 0 && pool == false ) + { + vlc_spin_destroy (&p_gc->spin); + p_gc->pf_destructor (p_gc); + } +} + +/***************************************************************************** + * 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 ) ); + } +} + + + +/***************************************************************************** + * 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 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 ) ); + + +/** + * Convenience Macros + */ + +/** + * 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 ) + +#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 ) + + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* VLC_MEDIA_LIBRARY_H */ -- 2.39.2