[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