[vlc-devel] [RFC PATCH 8/8] Add a new medialibrary module

Hugo Beauzée-Luyssen hugo at beauzee.fr
Mon Jun 25 10:10:06 CEST 2018


---
 configure.ac                                  |   1 +
 modules/misc/Makefile.am                      |   8 +
 .../misc/medialibrary/MetadataExtractor.cpp   | 219 +++++
 modules/misc/medialibrary/entities.cpp        | 339 ++++++++
 modules/misc/medialibrary/medialib.cpp        | 758 ++++++++++++++++++
 modules/misc/medialibrary/medialibrary.h      | 267 ++++++
 6 files changed, 1592 insertions(+)
 create mode 100644 modules/misc/medialibrary/MetadataExtractor.cpp
 create mode 100644 modules/misc/medialibrary/entities.cpp
 create mode 100644 modules/misc/medialibrary/medialib.cpp
 create mode 100644 modules/misc/medialibrary/medialibrary.h

diff --git a/configure.ac b/configure.ac
index 2eccb69be7..bd53e3865f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4134,6 +4134,7 @@ AS_IF([test "$enable_libplacebo" != "no"], [
 ])
 AM_CONDITIONAL(HAVE_LIBPLACEBO, [test "$enable_libplacebo" != "no"])
 
+PKG_ENABLE_MODULES_VLC([MEDIALIBRARY], [medialibrary], [medialibrary], (medialibrary support), [auto])
 
 dnl
 dnl  Endianness check
diff --git a/modules/misc/Makefile.am b/modules/misc/Makefile.am
index 5806f62aa9..c72f2b1efe 100644
--- a/modules/misc/Makefile.am
+++ b/modules/misc/Makefile.am
@@ -101,3 +101,11 @@ libxml_plugin_la_LIBADD = $(LIBXML2_LIBS)
 libxml_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(miscdir)' $(LDFLAGS_xml)
 EXTRA_LTLIBRARIES += libxml_plugin.la
 misc_LTLIBRARIES += $(LTLIBxml)
+
+libmedialibrary_plugin_la_SOURCES = misc/medialibrary/medialib.cpp misc/medialibrary/MetadataExtractor.cpp misc/medialibrary/entities.cpp
+libmedialibrary_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(MEDIALIBRARY_CPPFLAGS)
+libmedialibrary_plugin_la_LIBADD = $(MEDIALIBRARY_LIBS)
+libmedialibrary_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(miscdir)'
+EXTRA_LTLIBRARIES += libmedialibrary_plugin.la
+misc_LTLIBRARIES += $(LTLIBmedialibrary)
+
diff --git a/modules/misc/medialibrary/MetadataExtractor.cpp b/modules/misc/medialibrary/MetadataExtractor.cpp
new file mode 100644
index 0000000000..b3e2874a19
--- /dev/null
+++ b/modules/misc/medialibrary/MetadataExtractor.cpp
@@ -0,0 +1,219 @@
+/*****************************************************************************
+ * medialib.cpp: IParserService implementation using libvlccore
+ *****************************************************************************
+ * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "medialibrary.h"
+
+MetadataExtractor::MetadataExtractor( vlc_object_t* parent )
+    : m_obj( parent )
+{
+}
+
+void MetadataExtractor::onInputEvent( vlc_value_t intf_event, ParseContext& ctx )
+{
+    if ( intf_event.i_int != INPUT_EVENT_DEAD )
+        return;
+
+    {
+        vlc_mutex_locker lock( &ctx.m_mutex );
+        // We need to probe the item now, but not from VLC's thread
+        ctx.needsProbing = true;
+    }
+    vlc_cond_signal( &ctx.m_cond );
+}
+
+void MetadataExtractor::populateItem( medialibrary::parser::IItem& item, input_item_t* inputItem )
+{
+    vlc_mutex_locker lock( &inputItem->lock );
+
+    constexpr auto emptyStringWrapper = []( const char* psz ) {
+        return psz != nullptr ? std::string{ psz } : std::string{};
+    };
+
+    if ( inputItem->p_meta != nullptr )
+    {
+        item.setMeta( medialibrary::parser::IItem::Metadata::Title,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_Title ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::ArtworkUrl,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_ArtworkURL) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::ShowName,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_ShowName ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::Episode,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_Episode) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::Album,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_Album) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::Genre,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_Genre ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::Date,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_Date ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::AlbumArtist,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_AlbumArtist ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::Artist,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_Artist ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::TrackNumber,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_TrackNumber ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::DiscNumber,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_DiscNumber ) ) );
+        item.setMeta( medialibrary::parser::IItem::Metadata::DiscTotal,
+                      emptyStringWrapper( vlc_meta_Get( inputItem->p_meta, vlc_meta_DiscTotal ) ) );
+    }
+
+    item.setDuration( inputItem->i_duration );
+
+    for ( auto i = 0; i < inputItem->i_es; ++i )
+    {
+        medialibrary::parser::IItem::Track t;
+        const es_format_t *p_es = inputItem->es[i];
+
+        switch ( p_es->i_cat )
+        {
+            case AUDIO_ES:
+                t.type = medialibrary::parser::IItem::Track::Type::Audio;
+                t.a.nbChannels = p_es->audio.i_channels;
+                t.a.rate = p_es->audio.i_rate;
+                break;
+            case VIDEO_ES:
+                t.type = medialibrary::parser::IItem::Track::Type::Video;
+                t.v.fpsNum = p_es->video.i_frame_rate;
+                t.v.fpsDen = p_es->video.i_frame_rate_base;
+                t.v.width = p_es->video.i_width;
+                t.v.height = p_es->video.i_height;
+                t.v.sarNum = p_es->video.i_sar_num;
+                t.v.sarDen = p_es->video.i_sar_den;
+                break;
+            default:
+                continue;
+        }
+
+        char fourcc[4];
+        vlc_fourcc_to_char( p_es->i_codec, fourcc );
+        t.codec = std::string{ fourcc, 4 };
+
+        t.bitrate = p_es->i_bitrate;
+        t.language = emptyStringWrapper( p_es->psz_language );
+        t.description = emptyStringWrapper( p_es->psz_description );
+
+        item.addTrack( std::move( t ) );
+    }
+}
+
+int MetadataExtractor::onInputEvent( vlc_object_t*, const char*, vlc_value_t,
+                                     vlc_value_t cur, void* data )
+{
+    auto* ctx = reinterpret_cast<ParseContext*>( data );
+    ctx->mde->onInputEvent( cur, *ctx );
+    return VLC_SUCCESS;
+}
+
+void MetadataExtractor::onSubItemAdded( const vlc_event_t* event, ParseContext& ctx )
+{
+    auto root = event->u.input_item_subitem_tree_added.p_root;
+    for ( auto i = 0; i < root->i_children; ++i )
+    {
+        auto it = root->pp_children[i]->p_item;
+        auto& subItem = ctx.item.createSubItem( it->psz_uri, i );
+        populateItem( subItem, it );
+    }
+}
+
+void MetadataExtractor::onSubItemAdded( const vlc_event_t* event, void* data )
+{
+    auto* ctx = reinterpret_cast<ParseContext*>( data );
+    ctx->mde->onSubItemAdded( event, *ctx );
+}
+
+medialibrary::parser::Status MetadataExtractor::run( medialibrary::parser::IItem& item )
+{
+    const std::unique_ptr<ParseContext> ctx( new ParseContext{ this, item } );
+    ctx->inputItem = {
+        input_item_New( item.mrl().c_str(), NULL ),
+        &input_item_Release
+    };
+    if ( ctx->inputItem == nullptr )
+        return medialibrary::parser::Status::Fatal;
+
+    ctx->inputItem->i_preparse_depth = 1;
+    ctx->input = {
+        input_CreatePreparser( m_obj, ctx->inputItem.get() ),
+        &input_Close
+    };
+    if ( ctx->input == nullptr )
+        return medialibrary::parser::Status::Fatal;
+
+    var_AddCallback( ctx->input.get(), "intf-event", &MetadataExtractor::onInputEvent, ctx.get() );
+
+    vlc_event_attach( &ctx->inputItem->event_manager, vlc_InputItemSubItemTreeAdded,
+                      &MetadataExtractor::onSubItemAdded, ctx.get() );
+
+    input_Start( ctx->input.get() );
+
+    input_state_e state = INIT_S;
+    {
+        vlc_mutex_locker lock( &ctx->m_mutex );
+        while ( ctx->needsProbing == false )
+        {
+            vlc_cond_wait( &ctx->m_cond, &ctx->m_mutex );
+            if ( ctx->needsProbing == true )
+            {
+                state = input_GetState( ctx->input.get() );
+                if ( state == END_S || state == ERROR_S )
+                    break;
+            }
+        }
+    }
+
+    if ( state == ERROR_S )
+        return medialibrary::parser::Status::Fatal;
+
+    populateItem( item, ctx->inputItem.get() );
+
+    return medialibrary::parser::Status::Success;
+}
+
+const char* MetadataExtractor::name() const
+{
+    return "libvlccore extraction";
+}
+
+uint8_t MetadataExtractor::nbThreads() const
+{
+    return 1;
+}
+
+medialibrary::parser::Step MetadataExtractor::targetedStep() const
+{
+    return medialibrary::parser::Step::MetadataExtraction;
+}
+
+bool MetadataExtractor::initialize( medialibrary::IMediaLibrary* )
+{
+    return true;
+}
+
+void MetadataExtractor::onFlushing()
+{
+}
+
+void MetadataExtractor::onRestarted()
+{
+}
diff --git a/modules/misc/medialibrary/entities.cpp b/modules/misc/medialibrary/entities.cpp
new file mode 100644
index 0000000000..5f75dbe52c
--- /dev/null
+++ b/modules/misc/medialibrary/entities.cpp
@@ -0,0 +1,339 @@
+/*****************************************************************************
+ * entities.cpp: medialibrary C++ -> C entities conversion & management
+ *****************************************************************************
+ * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "medialibrary.h"
+
+#include <medialibrary/IMedia.h>
+#include <medialibrary/IFile.h>
+#include <medialibrary/IMovie.h>
+#include <medialibrary/IShow.h>
+#include <medialibrary/IShowEpisode.h>
+#include <medialibrary/IArtist.h>
+#include <medialibrary/IAlbum.h>
+#include <medialibrary/IAlbumTrack.h>
+#include <medialibrary/IGenre.h>
+#include <medialibrary/ILabel.h>
+
+bool Convert( const medialibrary::IAlbumTrack* input, ml_album_track_t& output )
+{
+    output.i_artist_id = input->artistId();
+    output.i_album_id = input->albumId();
+    output.i_disc_nb = input->discNumber();
+    output.i_genre_id = input->genreId();
+    output.i_track_nb = input->trackNumber();
+    return true;
+}
+
+bool Convert( const medialibrary::IShowEpisode* input, ml_show_episode_t& output )
+{
+    output.i_episode_nb = input->episodeNumber();
+    output.i_season_number = input->seasonNumber();
+    if ( input->shortSummary().empty() == false )
+    {
+        output.psz_summary = strdup( input->shortSummary().c_str() );
+        if ( unlikely( output.psz_summary == nullptr ) )
+            return false;
+    }
+    if ( input->tvdbId().empty() == false )
+    {
+        output.psz_tvdb_id = strdup( input->tvdbId().c_str() );
+        if ( unlikely( output.psz_tvdb_id == nullptr ) )
+            return false;
+    }
+    return true;
+}
+
+bool Convert( const medialibrary::IMovie* input, ml_movie_t& output )
+{
+    if ( input->imdbId().empty() == false )
+    {
+        output.psz_imdb_id = strdup( input->imdbId().c_str() );
+        if ( unlikely( output.psz_imdb_id == nullptr ) )
+            return false;
+    }
+    if ( input->shortSummary().empty() == false )
+    {
+        output.psz_summary = strdup( input->shortSummary().c_str() );
+        if ( unlikely( output.psz_summary == nullptr ) )
+            return false;
+    }
+    return true;
+}
+
+bool Convert( const medialibrary::IMedia* input, ml_media_t& output )
+{
+    output.i_id = input->id();
+
+    switch ( input->type() )
+    {
+        case medialibrary::IMedia::Type::Audio:
+            output.i_type = ML_MEDIA_TYPE_AUDIO;
+            switch( input->subType() )
+            {
+                case medialibrary::IMedia::SubType::AlbumTrack:
+                {
+                    auto albumTrack = input->albumTrack();
+                    if ( albumTrack == nullptr )
+                        return false;
+                    if ( Convert( albumTrack.get(), output.album_track ) == false )
+                        return false;
+                    break;
+                }
+                default:
+                    vlc_assert_unreachable();
+            }
+            break;
+        case medialibrary::IMedia::Type::Video:
+        {
+            output.i_type = ML_MEDIA_TYPE_VIDEO;
+            switch( input->subType() )
+            {
+                case medialibrary::IMedia::SubType::Movie:
+                {
+                    auto movie = input->movie();
+                    if ( movie == nullptr )
+                        return false;
+                    if ( Convert( movie.get(), output.movie ) == false )
+                        return false;
+                    break;
+                }
+                case medialibrary::IMedia::SubType::ShowEpisode:
+                {
+                    auto episode = input->showEpisode();
+                    if ( episode == nullptr )
+                        return false;
+                    if ( Convert( episode.get(), output.show_episode ) == false )
+                        return false;
+                    break;
+                }
+                case medialibrary::IMedia::SubType::AlbumTrack:
+                case medialibrary::IMedia::SubType::Unknown:
+                    vlc_assert_unreachable();
+            }
+            break;
+        }
+        case medialibrary::IMedia::Type::External:
+            output.i_type = ML_MEDIA_TYPE_EXTERNAL;
+            break;
+        case medialibrary::IMedia::Type::Unknown:
+            vlc_assert_unreachable();
+    }
+    output.i_year = input->releaseDate();
+    output.i_duration = input->duration();
+    output.b_is_favorite = input->isFavorite();
+    output.i_playcount = input->playCount();
+
+    output.psz_title = strdup( input->title().c_str() );
+    if ( unlikely( output.psz_title == nullptr ) )
+        return false;
+
+    auto files = input->files();
+    output.p_files = ml_convert_list<ml_file_list_t>( files );
+    if ( output.p_files == nullptr )
+        return false;
+
+    if ( input->isThumbnailGenerated() == true )
+    {
+        output.psz_artwork_mrl = strdup( input->thumbnail().c_str() );
+        if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+            return false;
+    }
+
+    return true;
+}
+
+bool Convert( const medialibrary::IFile* input, ml_file_t& output )
+{
+    switch ( input->type() )
+    {
+        case medialibrary::IFile::Type::Main:
+            output.i_type = ML_FILE_TYPE_MAIN;
+            break;
+        case medialibrary::IFile::Type::Part:
+            output.i_type = ML_FILE_TYPE_PART;
+            break;
+        case medialibrary::IFile::Type::Soundtrack:
+            output.i_type = ML_FILE_TYPE_SOUNDTRACK;
+            break;
+        case medialibrary::IFile::Type::Subtitles:
+            output.i_type = ML_FILE_TYPE_SUBTITLE;
+            break;
+        case medialibrary::IFile::Type::Playlist:
+            output.i_type = ML_FILE_TYPE_PLAYLIST;
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+    output.psz_mrl = strdup( input->mrl().c_str() );
+    if ( unlikely( output.psz_mrl == nullptr ) )
+        return false;
+    output.b_external = input->isExternal();
+    return true;
+}
+
+bool Convert( const medialibrary::IAlbum* input, ml_album_t& output )
+{
+    output.i_id = input->id();
+    auto featuring = input->artists( false )->all();
+    output.p_featuring = ml_convert_list<ml_artist_list_t>( featuring );
+    output.i_nb_tracks = input->nbTracks();
+    output.i_duration = input->duration();
+    output.i_year = input->releaseYear();
+    if ( input->title().empty() == false )
+    {
+        output.psz_title = strdup( input->title().c_str() );
+        if ( unlikely( output.psz_title == nullptr ) )
+            return false;
+    }
+    if ( input->shortSummary().empty() == false )
+    {
+        output.psz_summary = strdup( input->shortSummary().c_str() );
+        if ( unlikely( output.psz_summary == nullptr ) )
+            return false;
+    }
+    if ( input->artworkMrl().empty() == false )
+    {
+        output.psz_artwork_mrl = strdup( input->artworkMrl().c_str() );
+        if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+            return false;
+    }
+    auto artist = input->albumArtist();
+    if ( artist != nullptr )
+    {
+        output.i_artist_id = artist->id();
+        switch ( artist->id() )
+        {
+            case medialibrary::UnknownArtistID:
+                output.psz_artist = strdup( _( "Unknown Artist" ) );
+                break;
+            case medialibrary::VariousArtistID:
+                output.psz_artist = strdup( _( "Various Artist" ) );
+                break;
+            default:
+                output.psz_artist = strdup( artist->name().c_str() );
+                break;
+        }
+        if ( unlikely( output.psz_artist == nullptr ) )
+            return false;
+    }
+    return true;
+}
+
+bool Convert( const medialibrary::IArtist* input, ml_artist_t& output )
+{
+    output.i_id = input->id();
+    output.i_nb_album = input->nbAlbums();
+    output.i_nb_tracks = input->nbTracks();
+    switch ( input->id() )
+    {
+        case medialibrary::UnknownArtistID:
+            output.psz_name = strdup( _( "Unknown Artist" ) );
+            break;
+        case medialibrary::VariousArtistID:
+            output.psz_name = strdup( _( "Various Artist" ) );
+            break;
+        default:
+            output.psz_name = strdup( input->name().c_str() );
+            break;
+    }
+    if ( unlikely( output.psz_name == nullptr ) )
+        return false;
+    if ( input->shortBio().empty() == false )
+    {
+        output.psz_shortbio = strdup( input->shortBio().c_str() );
+        if ( unlikely( output.psz_shortbio == nullptr ) )
+            return false;
+    }
+    if ( input->artworkMrl().empty() == false )
+    {
+        output.psz_artwork_mrl = strdup( input->artworkMrl().c_str() );
+        if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+            return false;
+    }
+    if ( input->musicBrainzId().empty() == false )
+    {
+        output.psz_mb_id = strdup( input->musicBrainzId().c_str() );
+        if ( unlikely( output.psz_mb_id == nullptr ) )
+            return false;
+    }
+    return true;
+}
+
+void Release( ml_genre_t& genre )
+{
+    free( genre.psz_name );
+}
+
+bool Convert( const medialibrary::IGenre* input, ml_genre_t& output )
+{
+    output.i_id = input->id();
+    output.i_nb_tracks = input->nbTracks();
+    assert( input->name().empty() == false );
+    output.psz_name = strdup( input->name().c_str() );
+    if ( unlikely( output.psz_name == nullptr ) )
+        return false;
+    return true;
+}
+
+bool Convert( const medialibrary::IShow* input, ml_show_t& output )
+{
+    output.i_id = input->id();
+    output.i_release_year = input->releaseDate();
+    output.i_nb_episodes = input->nbEpisodes();
+    output.i_nb_seasons = input->nbSeasons();
+    if ( input->name().empty() == false )
+    {
+        output.psz_name = strdup( input->name().c_str() );
+        if ( output.psz_name == nullptr )
+            return false;
+    }
+    if ( input->artworkMrl().empty() == false )
+    {
+        output.psz_artwork_mrl = strdup( input->artworkMrl().c_str() );
+        if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+            return false;
+    }
+    if ( input->tvdbId().empty() == false )
+    {
+        output.psz_tvdb_id = strdup( input->tvdbId().c_str() );
+        if ( unlikely( output.psz_tvdb_id == nullptr ) )
+            return false;
+    }
+    if ( input->shortSummary().empty() == false )
+    {
+        output.psz_summary = strdup( input->shortSummary().c_str() );
+        if ( unlikely( output.psz_summary == nullptr ) )
+            return false;
+    }
+    return true;
+}
+
+bool Convert( const medialibrary::ILabel* input, ml_label_t& output )
+{
+    assert( input->name().empty() == false );
+    output.psz_name = strdup( input->name().c_str() );
+    if ( unlikely( output.psz_name == nullptr ) )
+        return false;
+    return true;
+}
diff --git a/modules/misc/medialibrary/medialib.cpp b/modules/misc/medialibrary/medialib.cpp
new file mode 100644
index 0000000000..1c7f404aa9
--- /dev/null
+++ b/modules/misc/medialibrary/medialib.cpp
@@ -0,0 +1,758 @@
+/*****************************************************************************
+ * medialib.cpp: medialibrary module
+ *****************************************************************************
+ * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_media_library.h>
+#include "medialibrary.h"
+
+#include <medialibrary/IFolder.h>
+#include <medialibrary/IMedia.h>
+#include <medialibrary/IAlbumTrack.h>
+#include <medialibrary/IAlbum.h>
+#include <medialibrary/IArtist.h>
+#include <medialibrary/IGenre.h>
+#include <medialibrary/IMetadata.h>
+
+class Logger : public medialibrary::ILogger
+{
+public:
+    Logger( vlc_object_t* obj ) : m_obj( obj ) {}
+
+private:
+    virtual void Error( const std::string& msg ) override
+    {
+        msg_Err( m_obj, "%s", msg.c_str() );
+    }
+    virtual void Warning( const std::string& msg ) override
+    {
+        msg_Warn( m_obj, "%s", msg.c_str() );
+    }
+    virtual void Info( const std::string& msg ) override
+    {
+        msg_Dbg( m_obj, "%s", msg.c_str() );
+    }
+    virtual void Debug( const std::string& msg ) override
+    {
+        msg_Dbg( m_obj, "%s", msg.c_str() );
+    }
+
+private:
+    vlc_object_t* m_obj;
+};
+
+void MediaLibrary::onMediaAdded( std::vector<medialibrary::MediaPtr> )
+{
+}
+
+void MediaLibrary::onMediaUpdated( std::vector<medialibrary::MediaPtr> )
+{
+}
+
+void MediaLibrary::onMediaDeleted( std::vector<int64_t> )
+{
+}
+
+void MediaLibrary::onArtistsAdded( std::vector<medialibrary::ArtistPtr> )
+{
+}
+
+void MediaLibrary::onArtistsModified( std::vector<medialibrary::ArtistPtr> )
+{
+}
+
+void MediaLibrary::onArtistsDeleted( std::vector<int64_t> )
+{
+}
+
+void MediaLibrary::onAlbumsAdded( std::vector<medialibrary::AlbumPtr> )
+{
+}
+
+void MediaLibrary::onAlbumsModified( std::vector<medialibrary::AlbumPtr> )
+{
+}
+
+void MediaLibrary::onAlbumsDeleted( std::vector<int64_t> )
+{
+}
+
+void MediaLibrary::onTracksAdded( std::vector<medialibrary::AlbumTrackPtr> )
+{
+}
+
+void MediaLibrary::onTracksDeleted( std::vector<int64_t> )
+{
+}
+
+void MediaLibrary::onPlaylistsAdded( std::vector<medialibrary::PlaylistPtr> )
+{
+}
+
+void MediaLibrary::onPlaylistsModified( std::vector<medialibrary::PlaylistPtr> )
+{
+}
+
+void MediaLibrary::onPlaylistsDeleted( std::vector<int64_t> )
+{
+}
+
+void MediaLibrary::onDiscoveryStarted( const std::string& )
+{
+}
+
+void MediaLibrary::onDiscoveryProgress( const std::string& )
+{
+}
+
+void MediaLibrary::onDiscoveryCompleted( const std::string& )
+{
+}
+
+void MediaLibrary::onReloadStarted( const std::string& )
+{
+}
+
+void MediaLibrary::onReloadCompleted( const std::string& )
+{
+}
+
+void MediaLibrary::onEntryPointRemoved( const std::string&, bool )
+{
+}
+
+void MediaLibrary::onEntryPointBanned( const std::string&, bool )
+{
+}
+
+void MediaLibrary::onEntryPointUnbanned( const std::string&, bool )
+{
+}
+
+void MediaLibrary::onParsingStatsUpdated( uint32_t )
+{
+}
+
+void MediaLibrary::onBackgroundTasksIdleChanged( bool )
+{
+}
+
+void MediaLibrary::onMediaThumbnailReady( medialibrary::MediaPtr, bool )
+{
+}
+
+MediaLibrary::MediaLibrary( vlc_object_t* obj )
+    : m_logger( new Logger( obj ) )
+    , m_ml( NewMediaLibrary() )
+{
+    m_ml->setVerbosity( medialibrary::LogLevel::Info );
+    m_ml->setLogger( m_logger.get() );
+    auto userDir = wrapCPtr( config_GetUserDir( VLC_USERDATA_DIR ) );
+    std::string mlDir = std::string{ userDir.get() } + "/ml/";
+
+    //tmp:
+    unlink( (mlDir + "ml.db").c_str() );
+
+    auto initStatus = m_ml->initialize( mlDir + "ml.db", mlDir + "thumbnails/", this );
+    switch ( initStatus )
+    {
+        case medialibrary::InitializeResult::AlreadyInitialized:
+            throw std::runtime_error( "Unexpected double medialibrary intialization" );
+        case medialibrary::InitializeResult::Failed:
+            throw std::runtime_error( "Medialibrary failed to initialize" );
+        case medialibrary::InitializeResult::DbReset:
+            msg_Info( obj, "FIXME: Handle database reset" );
+            break;
+        case medialibrary::InitializeResult::Success:
+            msg_Dbg( obj, "MediaLibrary successfully initialized" );
+            break;
+    }
+
+    m_ml->addParserService( std::make_shared<MetadataExtractor>( obj ) );
+    auto res = m_ml->start();
+    if ( res == false )
+        throw std::runtime_error( "Failed to start medialibrary" );
+    m_ml->discover( "file:///home/chouquette/dev/medialibrary/test/samples/samples/playlist/same_folder/" );
+    m_ml->reload();
+}
+
+int MediaLibrary::Control( int query, va_list args )
+{
+    switch ( query )
+    {
+        case ML_ADD_FOLDER:
+        case ML_REMOVE_FOLDER:
+        case ML_BAN_FOLDER:
+        case ML_UNBAN_FOLDER:
+        {
+            const char* mrl = va_arg( args, const char* );
+            switch( query )
+            {
+                case ML_ADD_FOLDER:
+                    m_ml->discover( mrl );
+                    break;
+                case ML_REMOVE_FOLDER:
+                    m_ml->removeEntryPoint( mrl );
+                    break;
+                case ML_BAN_FOLDER:
+                    m_ml->banFolder( mrl );
+                    break;
+                case ML_UNBAN_FOLDER:
+                    m_ml->unbanFolder( mrl );
+                    break;
+            }
+            break;
+        }
+        case ML_LIST_FOLDERS:
+        {
+            auto entryPoints = m_ml->entryPoints()->all();
+            auto nbItem = entryPoints.size();
+            auto list = wrapCArray( reinterpret_cast<ml_entrypoint_t*>(
+                    calloc( entryPoints.size(), sizeof( ml_entrypoint_t ) ) ),
+                    [nbItem]( ml_entrypoint_t* ptr ) {
+                        vlc_ml_entrypoints_release( ptr, nbItem );
+                    });
+            if ( unlikely( list == nullptr ) )
+                return VLC_ENOMEM;
+            for ( auto i = 0u; i < entryPoints.size(); ++i )
+            {
+                const auto ep = entryPoints[i].get();
+                if ( ep->isPresent() == true )
+                {
+                    list[i].psz_mrl = strdup( ep->mrl().c_str() );
+                    if ( unlikely( list[i].psz_mrl == nullptr ) )
+                        return VLC_ENOMEM;
+                    list[i].b_present = true;
+                }
+                else
+                {
+                    list[i].psz_mrl = nullptr;
+                    list[i].b_present = false;
+                }
+                list[i].b_banned = ep->isBanned();
+            }
+            *(va_arg( args, ml_entrypoint_t**) ) = list.release();
+            *(va_arg( args, size_t*) ) = entryPoints.size();
+            break;
+        }
+        case ML_PAUSE_BACKGROUND:
+            m_ml->pauseBackgroundOperations();
+            break;
+        case ML_RESUME_BACKGROUND:
+            m_ml->resumeBackgroundOperations();
+            break;
+        case ML_CLEAR_HISTORY:
+            m_ml->clearHistory();
+            break;
+        case ML_MEDIA_INCREASE_PLAY_COUNT:
+        {
+            auto mediaId = va_arg( args, int64_t );
+            auto m = m_ml->media( mediaId );
+            if ( m == nullptr )
+                return VLC_EGENERIC;
+            if ( m->increasePlayCount() == false )
+                return VLC_EGENERIC;
+            break;
+        }
+        case ML_MEDIA_GET_MEDIA_PLAYBACK_PREF:
+        {
+            auto mediaId = va_arg( args, int64_t );
+            auto meta = va_arg( args, int );
+            auto res = va_arg( args, char** );
+            return getMeta( mediaId, meta, res );
+        }
+        case ML_MEDIA_SET_MEDIA_PLAYBACK_PREF:
+        {
+            auto mediaId = va_arg( args, int64_t );
+            auto meta = va_arg( args, int );
+            auto value = va_arg( args, const char* );
+            return setMeta( mediaId, meta, value );
+        }
+        default:
+            return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+int MediaLibrary::List( int listQuery, const ml_query_params_t* params, va_list args )
+{
+    medialibrary::QueryParameters p{};
+    medialibrary::QueryParameters* paramsPtr = nullptr;
+    uint32_t nbItems = 0;
+    uint32_t offset = 0;
+    if ( params )
+    {
+        p.desc = params->b_desc;
+        switch ( params->i_sort )
+        {
+            case ML_SORTING_DEFAULT:
+                p.sort = medialibrary::SortingCriteria::Default;
+                break;
+            case ML_SORTING_ALPHA:
+                p.sort = medialibrary::SortingCriteria::Alpha;
+                break;
+            case ML_SORTING_DURATION:
+                p.sort = medialibrary::SortingCriteria::Duration;
+                break;
+            case ML_SORTING_INSERTIONDATE:
+                p.sort = medialibrary::SortingCriteria::InsertionDate;
+                break;
+            case ML_SORTING_LASTMODIFICATIONDATE:
+                p.sort = medialibrary::SortingCriteria::LastModificationDate;
+                break;
+            case ML_SORTING_RELEASEDATE:
+                p.sort = medialibrary::SortingCriteria::ReleaseDate;
+                break;
+            case ML_SORTING_FILESIZE:
+                p.sort = medialibrary::SortingCriteria::FileSize;
+                break;
+            case ML_SORTING_ARTIST:
+                p.sort = medialibrary::SortingCriteria::Artist;
+                break;
+            case ML_SORTING_PLAYCOUNT:
+                p.sort = medialibrary::SortingCriteria::PlayCount;
+                break;
+            case ML_SORTING_ALBUM:
+                p.sort = medialibrary::SortingCriteria::Album;
+                break;
+            case ML_SORTING_FILENAME:
+                p.sort = medialibrary::SortingCriteria::Filename;
+                break;
+            case ML_SORTING_TRACKNUMBER:
+                p.sort = medialibrary::SortingCriteria::TrackNumber;
+                break;
+        }
+        nbItems = params->i_nbResults;
+        offset = params->i_offset;
+        paramsPtr = &p;
+    }
+    switch( listQuery )
+    {
+        case ML_LIST_ALBUM_TRACKS:
+        case ML_COUNT_ALBUM_TRACKS:
+        {
+            auto album = m_ml->album( va_arg( args, int64_t ) );
+            if ( album == nullptr )
+                return VLC_EGENERIC;
+            auto query = album->tracks( paramsPtr );
+            switch ( listQuery )
+            {
+                case ML_LIST_ALBUM_TRACKS:
+                    *va_arg( args, ml_media_list_t**) = ml_convert_list<ml_media_list_t>(
+                                query->items( nbItems, offset ) );
+                    break;
+                case ML_COUNT_ALBUM_TRACKS:
+                    *va_arg( args, size_t* ) = query->count();
+                    break;
+                default:
+                    vlc_assert_unreachable();
+            }
+            break;
+        }
+        case ML_LIST_ARTIST_ALBUMS:
+        case ML_COUNT_ARTIST_ALBUMS:
+        case ML_LIST_ARTIST_TRACKS:
+        case ML_COUNT_ARTIST_TRACKS:
+        {
+            auto artist = m_ml->artist( va_arg( args, int64_t ) );
+            if ( artist == nullptr )
+                return VLC_EGENERIC;
+            switch( listQuery )
+            {
+                case ML_LIST_ARTIST_ALBUMS:
+                case ML_COUNT_ARTIST_ALBUMS:
+                {
+                    auto query = artist->albums( paramsPtr );
+                    switch ( listQuery )
+                    {
+                        case ML_LIST_ARTIST_ALBUMS:
+                            *va_arg( args, ml_album_list_t**) = ml_convert_list<ml_album_list_t>(
+                                        query->items( nbItems, offset ) );
+                            break;
+                        case ML_COUNT_ARTIST_ALBUMS:
+                            *va_arg( args, size_t* ) = query->count();
+                            break;
+                        default:
+                            vlc_assert_unreachable();
+                    }
+                    break;
+                }
+                case ML_LIST_ARTIST_TRACKS:
+                case ML_COUNT_ARTIST_TRACKS:
+                {
+                    auto query = artist->media( paramsPtr );
+                    switch ( listQuery )
+                    {
+                        case ML_LIST_ARTIST_TRACKS:
+                            *va_arg( args, ml_media_list_t**) = ml_convert_list<ml_media_list_t>(
+                                        query->items( nbItems, offset ) );
+                            break;
+                        case ML_COUNT_ARTIST_TRACKS:
+                            *va_arg( args, size_t* ) = query->count();
+                            break;
+                        default:
+                            vlc_assert_unreachable();
+                    }
+                    break;
+                }
+                default:
+                    vlc_assert_unreachable();
+            }
+            break;
+        }
+        case ML_LIST_VIDEOS:
+        {
+            auto query = m_ml->videoFiles( paramsPtr );
+            auto res = ml_convert_list<ml_media_list_t>( query->items( nbItems, offset ) );
+            *va_arg( args, ml_media_list_t**) = res;
+            break;
+        }
+        case ML_COUNT_VIDEOS:
+        {
+            auto query = m_ml->videoFiles( paramsPtr );
+            *va_arg( args, size_t* ) = query->count();
+            break;
+        }
+        case ML_LIST_AUDIOS:
+        {
+            auto query = m_ml->audioFiles( paramsPtr );
+            auto res = ml_convert_list<ml_media_list_t>( query->items( nbItems, offset ) );
+            *va_arg( args, ml_media_list_t**) = res;
+            break;
+        }
+        case ML_COUNT_AUDIOS:
+        {
+            auto query = m_ml->audioFiles( paramsPtr );
+            *va_arg( args, size_t* ) = query->count();
+            break;
+        }
+        case ML_LIST_ALBUMS:
+        {
+            auto query = m_ml->albums( paramsPtr );
+            auto res = ml_convert_list<ml_album_list_t>( query->items( nbItems, offset ) );
+            *va_arg( args, ml_album_list_t**) = res;
+            break;
+        }
+        case ML_COUNT_ALBUMS:
+        {
+            auto query = m_ml->albums( paramsPtr );
+            *va_arg( args, size_t* ) = query->count();
+            break;
+        }
+        case ML_LIST_GENRES:
+        {
+            auto query = m_ml->genres( paramsPtr );
+            auto res = ml_convert_list<ml_genre_list_t>( query->items( nbItems, offset ) );
+            *va_arg( args, ml_genre_list_t**) = res;
+            break;
+        }
+        case ML_COUNT_GENRES:
+        {
+            auto query = m_ml->genres( paramsPtr );
+            *va_arg( args, size_t* ) = query->count();
+            break;
+        }
+        case ML_LIST_ARTISTS:
+        {
+            auto query = m_ml->artists( paramsPtr );
+            auto res = ml_convert_list<ml_artist_list_t>( query->items( nbItems, offset ) );
+            *va_arg( args, ml_artist_list_t**) = res;
+            break;
+        }
+        case ML_COUNT_ARTISTS:
+        {
+            auto query = m_ml->artists( paramsPtr );
+            *va_arg( args, size_t* ) = query->count();
+            break;
+        }
+        case ML_LIST_GENRE_ARTISTS:
+        case ML_COUNT_GENRE_ARTISTS:
+        case ML_LIST_GENRE_TRACKS:
+        case ML_COUNT_GENRE_TRACKS:
+        case ML_LIST_GENRE_ALBUMS:
+        case ML_COUNT_GENRE_ALBUMS:
+        {
+            auto genre = m_ml->genre( va_arg( args, int64_t ) );
+            if ( genre == nullptr )
+                return VLC_EGENERIC;
+            switch( listQuery )
+            {
+                case ML_LIST_GENRE_ARTISTS:
+                case ML_COUNT_GENRE_ARTISTS:
+                {
+                    auto query = genre->artists( paramsPtr );
+                    switch ( listQuery )
+                    {
+                        case ML_LIST_GENRE_ARTISTS:
+                            *va_arg( args, ml_artist_list_t**) = ml_convert_list<ml_artist_list_t>(
+                                            query->items( nbItems, offset ) );
+                            break;
+                        case ML_COUNT_GENRE_ARTISTS:
+                            *va_arg( args, size_t* ) = query->count();
+                            break;
+                        default:
+                            vlc_assert_unreachable();
+                    }
+                    break;
+                }
+                case ML_LIST_GENRE_TRACKS:
+                case ML_COUNT_GENRE_TRACKS:
+                {
+                    auto query = genre->tracks( paramsPtr );
+                    switch ( listQuery )
+                    {
+                        case ML_LIST_GENRE_TRACKS:
+                            *va_arg( args, ml_media_list_t**) = ml_convert_list<ml_media_list_t>( query->items( nbItems, offset ) );
+                            break;
+                        case ML_COUNT_GENRE_TRACKS:
+                            *va_arg( args, size_t*) = query->count();
+                            break;
+                        default:
+                            vlc_assert_unreachable();
+                    }
+                    break;
+                }
+                case ML_LIST_GENRE_ALBUMS:
+                case ML_COUNT_GENRE_ALBUMS:
+                {
+                    auto query = genre->albums( paramsPtr );
+                    switch ( listQuery )
+                    {
+                        case ML_LIST_GENRE_ALBUMS:
+                            *va_arg( args, ml_album_list_t**) = ml_convert_list<ml_album_list_t>(
+                                        query->items( nbItems, offset ) );
+                            break;
+                        case ML_COUNT_GENRE_ALBUMS:
+                            *va_arg( args, size_t* ) = query->count();
+                            break;
+                        default:
+                            vlc_assert_unreachable();
+                    }
+                    break;
+                }
+                default:
+                    vlc_assert_unreachable();
+            }
+            break;
+        }
+        case ML_LIST_MEDIA_LABELS:
+        case ML_COUNT_MEDIA_LABELS:
+        {
+            auto media = m_ml->media( va_arg( args, int64_t ) );
+            if ( media == nullptr )
+                return VLC_EGENERIC;
+            auto query = media->labels();
+            switch ( listQuery )
+            {
+                case ML_LIST_MEDIA_LABELS:
+                    *va_arg( args, ml_label_list_t**) = ml_convert_list<ml_label_list_t>(
+                                query->items( nbItems, offset ) );
+                    break;
+                case ML_COUNT_MEDIA_LABELS:
+                    *va_arg( args, size_t* ) = query->count();
+                    break;
+                default:
+                    vlc_assert_unreachable();
+            }
+            break;
+        }
+    }
+    return VLC_SUCCESS;
+}
+
+void* MediaLibrary::Get( int query, int64_t id )
+{
+    switch ( query )
+    {
+        case ML_GET_MEDIA:
+        {
+            auto media = m_ml->media( id );
+            return CreateAndConvert<ml_media_t>( media.get() );
+        }
+        case ML_GET_ALBUM:
+        {
+            auto album = m_ml->album( id );
+            return CreateAndConvert<ml_album_t>( album.get() );
+        }
+        case ML_GET_ARTIST:
+        {
+            auto artist = m_ml->artist( id );
+            return CreateAndConvert<ml_artist_t>( artist.get() );
+        }
+        case ML_GET_GENRE:
+        {
+            auto genre = m_ml->genre( id );
+            return CreateAndConvert<ml_genre_t>( genre.get() );
+        }
+        case ML_GET_SHOW:
+        {
+            auto show = m_ml->show( id );
+            return CreateAndConvert<ml_show_t>( show.get() );
+        }
+        default:
+            vlc_assert_unreachable();
+
+    }
+    return nullptr;
+}
+
+medialibrary::IMedia::MetadataType MediaLibrary::metadataType( int meta )
+{
+    switch ( meta )
+    {
+        case ML_PLAYBACK_PREF_RATING:
+            return medialibrary::IMedia::MetadataType::Rating;
+        case ML_PLAYBACK_PREF_PROGRESS:
+            return medialibrary::IMedia::MetadataType::Progress;
+        case ML_PLAYBACK_PREF_SPEED:
+            return medialibrary::IMedia::MetadataType::Speed;
+        case ML_PLAYBACK_PREF_TITLE:
+            return medialibrary::IMedia::MetadataType::Title;
+        case ML_PLAYBACK_PREF_CHAPTER:
+            return medialibrary::IMedia::MetadataType::Chapter;
+        case ML_PLAYBACK_PREF_PROGRAM:
+            return medialibrary::IMedia::MetadataType::Program;
+        case ML_PLAYBACK_PREF_SEEN:
+            return medialibrary::IMedia::MetadataType::Seen;
+        case ML_PLAYBACK_PREF_VIDEO_TRACK:
+            return medialibrary::IMedia::MetadataType::VideoTrack;
+        case ML_PLAYBACK_PREF_ASPECT_RATIO:
+            return medialibrary::IMedia::MetadataType::AspectRatio;
+        case ML_PLAYBACK_PREF_ZOOM:
+            return medialibrary::IMedia::MetadataType::Zoom;
+        case ML_PLAYBACK_PREF_CROP:
+            return medialibrary::IMedia::MetadataType::Crop;
+        case ML_PLAYBACK_PREF_DEINTERLACE:
+            return medialibrary::IMedia::MetadataType::Deinterlace;
+        case ML_PLAYBACK_PREF_VIDEO_FILTER:
+            return medialibrary::IMedia::MetadataType::VideoFilter;
+        case ML_PLAYBACK_PREF_AUDIO_TRACK:
+            return medialibrary::IMedia::MetadataType::AudioTrack;
+        case ML_PLAYBACK_PREF_GAIN:
+            return medialibrary::IMedia::MetadataType::Gain;
+        case ML_PLAYBACK_PREF_AUDIO_DELAY:
+            return medialibrary::IMedia::MetadataType::AudioDelay;
+        case ML_PLAYBACK_PREF_SUBTITLE_TRACK:
+            return medialibrary::IMedia::MetadataType::SubtitleTrack;
+        case ML_PLAYBACK_PREF_SUBTITLE_DELAY:
+            return medialibrary::IMedia::MetadataType::SubtitleDelay;
+        case ML_PLAYBACK_PREF_APP_SPECIFIC:
+            return medialibrary::IMedia::MetadataType::ApplicationSpecific;
+        default:
+            vlc_assert_unreachable();
+    }
+}
+
+int MediaLibrary::getMeta( int64_t mediaId, int meta, char** result )
+{
+    auto media = m_ml->media( mediaId );
+    if ( media == nullptr )
+        return VLC_EGENERIC;
+    auto& md = media->metadata( metadataType( meta ) );
+    if ( md.isSet() == false )
+    {
+        *result = nullptr;
+        return VLC_SUCCESS;
+    }
+    *result = strdup( md.str().c_str() );
+    if ( *result == nullptr )
+        return VLC_ENOMEM;
+    return VLC_SUCCESS;
+}
+
+int MediaLibrary::setMeta( int64_t mediaId, int meta, const char* value )
+{
+    auto media = m_ml->media( mediaId );
+    if ( media == nullptr )
+        return VLC_EGENERIC;
+    bool res;
+    if ( value == nullptr )
+        res = media->unsetMetadata( metadataType( meta ) );
+    else
+        res = media->setMetadata( metadataType( meta ), value );
+    if ( res == false )
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+static void* Get( vlc_medialibrary_t* module, int query, int64_t id )
+{
+    auto ml = reinterpret_cast<MediaLibrary*>( module->p_sys );
+    return ml->Get( query, id );
+}
+
+static int List( vlc_medialibrary_t* module, int query,
+                   const ml_query_params_t* params, ... )
+{
+    va_list args;
+    va_start( args, params );
+    auto ml = reinterpret_cast<MediaLibrary*>( module->p_sys );
+    auto res = ml->List( query, params, args );
+    va_end( args );
+    return res;
+}
+
+static int Control( vlc_medialibrary_t* module, int query, ... )
+{
+    va_list args;
+    va_start( args, query );
+    auto ml = reinterpret_cast<MediaLibrary*>( module->p_sys );
+    int res = ml->Control( query, args );
+    va_end( args );
+    return res;
+}
+
+static int Open( vlc_object_t* obj )
+{
+    vlc_medialibrary_t* p_module = reinterpret_cast<vlc_medialibrary_t*>( obj );
+
+    try
+    {
+        p_module->p_sys = new MediaLibrary( obj );
+    }
+    catch ( const std::exception& ex )
+    {
+        msg_Err( obj, "Failed to instantiate/initialize medialibrary: %s", ex.what() );
+        return VLC_EGENERIC;
+    }
+    p_module->pf_control = Control;
+    p_module->pf_get = Get;
+    p_module->pf_list = List;
+    return VLC_SUCCESS;
+}
+
+static void Close( vlc_medialibrary_t* module )
+{
+    MediaLibrary* p_ml = reinterpret_cast<MediaLibrary*>( module->p_sys );
+    delete p_ml;
+}
+
+vlc_module_begin()
+    set_shortname(N_("media library"))
+    set_description(N_( "Organize your media" ))
+    set_category(CAT_ADVANCED)
+    set_subcategory(SUBCAT_ADVANCED_MISC)
+    set_capability("medialibrary", 100)
+    set_callbacks(Open, Close)
+vlc_module_end()
diff --git a/modules/misc/medialibrary/medialibrary.h b/modules/misc/medialibrary/medialibrary.h
new file mode 100644
index 0000000000..d12983736e
--- /dev/null
+++ b/modules/misc/medialibrary/medialibrary.h
@@ -0,0 +1,267 @@
+/*****************************************************************************
+ * medialibrary.h: medialibrary module common declarations
+ *****************************************************************************
+ * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 MEDIALIBRARY_H
+#define MEDIALIBRARY_H
+
+#include <medialibrary/IMediaLibrary.h>
+#include <medialibrary/parser/IParserService.h>
+#include <medialibrary/parser/IItem.h>
+#include <medialibrary/parser/Parser.h>
+#include <medialibrary/IMedia.h>
+
+#include <vlc_common.h>
+#include <vlc_threads.h>
+#include <vlc_input_item.h>
+#include <vlc_input.h>
+#include <vlc_media_library.h>
+
+#include <cstdarg>
+
+struct vlc_event_t;
+struct vlc_object_t;
+
+class Logger;
+
+template <typename T>
+inline std::unique_ptr<T, void (*)(void*)> wrapCPtr( T* ptr )
+{
+    static_assert( std::is_pointer<T>::value == false, "T must be a non pointer type" );
+    return std::unique_ptr<T, decltype( &free )>( ptr, &free );
+}
+
+template <typename T, typename Releaser>
+inline auto wrapCPtr( T* ptr, Releaser&& r )
+    -> std::unique_ptr<T, typename std::decay<decltype( r )>::type>
+{
+    static_assert( std::is_pointer<T>::value == false, "T must be a non pointer type" );
+    return std::unique_ptr<T, typename std::decay<decltype( r )>::type>( ptr, std::forward<Releaser>( r ) );
+}
+
+template <typename T, typename Releaser>
+inline auto wrapCArray( T* ptr, Releaser&& r )
+    -> std::unique_ptr<T[], typename std::decay<decltype( r )>::type>
+{
+    static_assert( std::is_pointer<T>::value == false, "T must be a non pointer type" );
+    return std::unique_ptr<T[], typename std::decay<decltype( r )>::type>( ptr, std::forward<Releaser>( r ) );
+}
+
+class MetadataExtractor : public medialibrary::parser::IParserService
+{
+private:
+    struct ParseContext
+    {
+        ParseContext( MetadataExtractor* mde, medialibrary::parser::IItem& item )
+            : inputItem( nullptr, &input_item_Release )
+            , input( nullptr, &input_Close )
+            , needsProbing( false )
+            , mde( mde )
+            , item( item )
+        {
+            vlc_mutex_init( &m_mutex );
+            vlc_cond_init( &m_cond );
+        }
+        ~ParseContext()
+        {
+            vlc_cond_destroy( &m_cond );
+            vlc_mutex_destroy( &m_mutex );
+        }
+
+        std::unique_ptr<input_item_t, decltype(&input_item_Release)> inputItem;
+        std::unique_ptr<input_thread_t, decltype(&input_Close)> input;
+        vlc_cond_t m_cond;
+        vlc_mutex_t m_mutex;
+        bool needsProbing;
+        MetadataExtractor* mde;
+        medialibrary::parser::IItem& item;
+    };
+
+public:
+    MetadataExtractor( vlc_object_t* parent );
+    virtual ~MetadataExtractor() = default;
+
+    // All methods are meant to be accessed through IParserService, not directly
+    // hence they are all private
+private:
+    virtual medialibrary::parser::Status run( medialibrary::parser::IItem& item ) override;
+    virtual const char*name() const override;
+    virtual uint8_t nbThreads() const override;
+    virtual medialibrary::parser::Step targetedStep() const override;
+    virtual bool initialize( medialibrary::IMediaLibrary* ml ) override;
+    virtual void onFlushing() override;
+    virtual void onRestarted() override;
+
+    void onInputEvent( vlc_value_t event, ParseContext& ctx );
+    void onSubItemAdded( const vlc_event_t* event, ParseContext& ctx );
+    void populateItem( medialibrary::parser::IItem& item, input_item_t* inputItem );
+
+    static int onInputEvent( vlc_object_t*, const char*, vlc_value_t,
+                             vlc_value_t cur, void* data );
+    static void onSubItemAdded( const vlc_event_t* event, void* data );
+
+private:
+    vlc_object_t* m_obj;
+};
+
+class MediaLibrary : public medialibrary::IMediaLibraryCb
+{
+public:
+    MediaLibrary( vlc_object_t* obj );
+    int Control( int query, va_list args );
+    int List( int query, const ml_query_params_t* params, va_list args );
+    void* Get( int query, int64_t id );
+
+private:
+    static medialibrary::IMedia::MetadataType metadataType( int meta );
+    int getMeta( int64_t mediaId, int meta, char** result );
+    int setMeta(int64_t mediaId, int meta, const char* value );
+
+private:
+    std::unique_ptr<Logger> m_logger;
+    std::unique_ptr<medialibrary::IMediaLibrary> m_ml;
+
+    // IMediaLibraryCb interface
+public:
+    virtual void onMediaAdded(std::vector<medialibrary::MediaPtr> media) override;
+    virtual void onMediaUpdated(std::vector<medialibrary::MediaPtr> media) override;
+    virtual void onMediaDeleted(std::vector<int64_t> mediaIds) override;
+    virtual void onArtistsAdded(std::vector<medialibrary::ArtistPtr> artists) override;
+    virtual void onArtistsModified(std::vector<medialibrary::ArtistPtr> artists) override;
+    virtual void onArtistsDeleted(std::vector<int64_t> artistsIds) override;
+    virtual void onAlbumsAdded(std::vector<medialibrary::AlbumPtr> albums) override;
+    virtual void onAlbumsModified(std::vector<medialibrary::AlbumPtr> albums) override;
+    virtual void onAlbumsDeleted(std::vector<int64_t> albumsIds) override;
+    virtual void onTracksAdded(std::vector<medialibrary::AlbumTrackPtr> tracks) override;
+    virtual void onTracksDeleted(std::vector<int64_t> trackIds) override;
+    virtual void onPlaylistsAdded(std::vector<medialibrary::PlaylistPtr> playlists) override;
+    virtual void onPlaylistsModified(std::vector<medialibrary::PlaylistPtr> playlists) override;
+    virtual void onPlaylistsDeleted(std::vector<int64_t> playlistIds) override;
+    virtual void onDiscoveryStarted(const std::string& entryPoint) override;
+    virtual void onDiscoveryProgress(const std::string& entryPoint) override;
+    virtual void onDiscoveryCompleted(const std::string& entryPoint) override;
+    virtual void onReloadStarted(const std::string& entryPoint) override;
+    virtual void onReloadCompleted(const std::string& entryPoint) override;
+    virtual void onEntryPointRemoved(const std::string& entryPoint, bool success) override;
+    virtual void onEntryPointBanned(const std::string& entryPoint, bool success) override;
+    virtual void onEntryPointUnbanned(const std::string& entryPoint, bool success) override;
+    virtual void onParsingStatsUpdated(uint32_t percent) override;
+    virtual void onBackgroundTasksIdleChanged(bool isIdle) override;
+    virtual void onMediaThumbnailReady(medialibrary::MediaPtr media, bool success) override;
+};
+
+bool Convert( const medialibrary::IMedia* input, ml_media_t& output );
+
+bool Convert( const medialibrary::IFile* input, ml_file_t& output );
+
+bool Convert( const medialibrary::IMovie* input, ml_movie_t& output );
+
+bool Convert( const medialibrary::IShowEpisode* input, ml_show_episode_t& output );
+
+bool Convert( const medialibrary::IAlbumTrack* input, ml_album_track_t& output );
+
+bool Convert( const medialibrary::IAlbum* input, ml_album_t& output );
+
+bool Convert( const medialibrary::IArtist* input, ml_artist_t& output );
+
+void Release( ml_genre_t& genre );
+bool Convert( const medialibrary::IGenre* input, ml_genre_t& output );
+
+bool Convert( const medialibrary::IShow* input, ml_show_t& output );
+
+bool Convert( const medialibrary::ILabel* input, ml_label_t& output );
+
+/**
+ * Release a heap allocated instance of T.
+ */
+template <typename T>
+void Release( T* entity )
+{
+    if ( entity == nullptr )
+        return;
+    Release( *entity );
+    free( entity );
+}
+
+// Dispatcher to libvlccore's ml release functions
+static inline void Release( ml_show_t* show ) { vlc_ml_show_release( show ); }
+static inline void Release( ml_artist_t* artist ) { vlc_ml_artist_release( artist ); }
+static inline void Release( ml_album_t* album ) { vlc_ml_album_release( album ); }
+static inline void Release( ml_genre_t* genre ) { vlc_ml_genre_release( genre ); }
+static inline void Release( ml_media_t* media ) { vlc_ml_media_release( media ); }
+static inline void Release( ml_label_list_t* list ) { vlc_ml_label_list_release( list ); }
+static inline void Release( ml_file_list_t* list ) { vlc_ml_file_list_release( list ); }
+static inline void Release( ml_artist_list_t* list ) { vlc_ml_artist_list_release( list ); }
+static inline void Release( ml_media_list_t* list ) { vlc_ml_media_list_release( list ); }
+static inline void Release( ml_album_list_t* list ) { vlc_ml_album_list_release( list ); }
+static inline void Release( ml_show_list_t* list ) { vlc_ml_show_list_release( list ); }
+static inline void Release( ml_genre_list_t* list ) { vlc_ml_genre_list_release( list ); }
+
+template <typename To, typename From>
+To* ml_convert_list( const std::vector<std::shared_ptr<From>>& input )
+{
+    // This function uses duck typing and assumes all lists have a p_items member
+    static_assert( std::is_pointer<To>::value == false,
+                   "Destination type must not be a pointer" );
+    static_assert( std::is_pointer<decltype(To::p_items)>::value == true,
+                   "Missing or invalid p_items member" );
+
+    // Allocate the ml_*_list_t
+    auto list = wrapCPtr<To>(
+        reinterpret_cast<To*>( malloc( sizeof( To ) ) ),
+        static_cast<void(*)(To*)>( &Release ) );
+    if ( unlikely( list == nullptr ) )
+        return nullptr;
+    using ItemType = typename std::remove_pointer<decltype(To::p_items)>::type;
+
+    // And allocate it's p_items pointer
+    list->p_items = reinterpret_cast<ItemType*>( malloc( input.size() * sizeof( ItemType ) ) );
+    if ( unlikely( list->p_items == nullptr ) )
+        return nullptr;
+
+    for ( auto i = 0u; i < input.size(); ++i )
+    {
+         if ( Convert( input[i].get(), list->p_items[i] ) == false )
+             return nullptr;
+         list->i_nb_items++;
+    }
+    return list.release();
+}
+
+template <typename T, typename Input>
+T* CreateAndConvert( const Input* input )
+{
+    auto res = wrapCPtr<T>(
+                reinterpret_cast<T*>( malloc( sizeof( T ) ) ),
+                static_cast<void(*)(T*)>( &Release ) );
+    if ( unlikely( res == nullptr ) )
+        return nullptr;
+    if ( Convert( input, *res ) == false )
+        return nullptr;
+    // Override the pf_relase that each Convert<T> helper will assign.
+    // The Convert function will use the ReleaseRef variant of the release function,
+    // as it converts in place, and doesn't have to free the allocated pointer.
+    // When CreateAndConvert is used, we heap-allocate an instance of T, and therefor
+    // we also need to release it.
+    return res.release();
+}
+
+
+
+#endif // MEDIALIBRARY_H
-- 
2.18.0



More information about the vlc-devel mailing list