[vlc-devel] [PATCH 9/9] Add a new medialibrary module
Hugo Beauzée-Luyssen
hugo at beauzee.fr
Fri Jul 13 17:07:27 CEST 2018
---
configure.ac | 1 +
modules/misc/Makefile.am | 8 +
.../misc/medialibrary/MetadataExtractor.cpp | 221 +++
modules/misc/medialibrary/entities.cpp | 477 +++++++
modules/misc/medialibrary/medialib.cpp | 1190 +++++++++++++++++
modules/misc/medialibrary/medialibrary.h | 223 +++
6 files changed, 2120 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 ba76fc124f..243304fe78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4143,6 +4143,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 f2d13ed278..1b8089efdc 100644
--- a/modules/misc/Makefile.am
+++ b/modules/misc/Makefile.am
@@ -100,3 +100,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..3fcc863588
--- /dev/null
+++ b/modules/misc/medialibrary/MetadataExtractor.cpp
@@ -0,0 +1,221 @@
+/*****************************************************************************
+ * 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( const vlc_input_event* ev,
+ ParseContext& ctx )
+{
+ if ( ev->type != INPUT_EVENT_DEAD )
+ return;
+
+ {
+ vlc_mutex_locker lock( &ctx.m_mutex );
+ // We need to probe the item now, but not from the input 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 ) );
+ }
+}
+
+void MetadataExtractor::onInputEvent( input_thread_t*, void *data,
+ const struct vlc_input_event *event )
+{
+ auto* ctx = reinterpret_cast<ParseContext*>( data );
+ ctx->mde->onInputEvent( event, *ctx );
+}
+
+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, &MetadataExtractor::onInputEvent,
+ ctx.get(), ctx->inputItem.get() ),
+ &input_Close
+ };
+ if ( ctx->input == nullptr )
+ return medialibrary::parser::Status::Fatal;
+
+ vlc_event_attach( &ctx->inputItem->event_manager, vlc_InputItemSubItemTreeAdded,
+ &MetadataExtractor::onSubItemAdded, ctx.get() );
+
+ input_Start( ctx->input.get() );
+
+ int 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 = var_GetInteger( ctx->input.get(), "state" );
+ if ( state == END_S || state == ERROR_S )
+ break;
+ // Reset the probing flag for next event
+ ctx->needsProbing = false;
+ }
+ }
+ }
+
+ if ( state == ERROR_S )
+ return medialibrary::parser::Status::Fatal;
+ assert( state == END_S );
+
+ 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..3eb03b3984
--- /dev/null
+++ b/modules/misc/medialibrary/entities.cpp
@@ -0,0 +1,477 @@
+/*****************************************************************************
+ * 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>
+#include <medialibrary/IPlaylist.h>
+#include <medialibrary/IAudioTrack.h>
+#include <medialibrary/IVideoTrack.h>
+
+bool Convert( const medialibrary::IAlbumTrack* input, vlc_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, vlc_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;
+ }
+ else
+ output.psz_summary = nullptr;
+ if ( input->tvdbId().empty() == false )
+ {
+ output.psz_tvdb_id = strdup( input->tvdbId().c_str() );
+ if ( unlikely( output.psz_tvdb_id == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_tvdb_id = nullptr;
+ return true;
+}
+
+bool Convert( const medialibrary::IMovie* input, vlc_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;
+ }
+ else
+ output.psz_imdb_id = nullptr;
+ if ( input->shortSummary().empty() == false )
+ {
+ output.psz_summary = strdup( input->shortSummary().c_str() );
+ if ( unlikely( output.psz_summary == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_summary = nullptr;
+ return true;
+}
+
+static bool convertTracksCommon( vlc_ml_media_track_t* output, const std::string& codec,
+ const std::string& language, const std::string& desc )
+{
+ if ( codec.empty() == false )
+ {
+ output->psz_codec = strdup( codec.c_str() );
+ if ( unlikely( output->psz_codec == nullptr ) )
+ return false;
+ }
+ else
+ output->psz_codec = nullptr;
+
+ if ( language.empty() == false )
+ {
+ output->psz_language = strdup( language.c_str() );
+ if ( unlikely( output->psz_language == nullptr ) )
+ return false;
+ }
+ else
+ output->psz_language = nullptr;
+
+ if ( desc.empty() == false )
+ {
+ output->psz_description = strdup( desc.c_str() );
+ if ( unlikely( output->psz_description == nullptr ) )
+ return false;
+ }
+ else
+ output->psz_description = nullptr;
+ return true;
+}
+
+static bool convertTracks( const medialibrary::IMedia* inputMedia, vlc_ml_media_t& outputMedia )
+{
+ auto videoTracks = inputMedia->videoTracks()->all();
+ auto audioTracks = inputMedia->audioTracks()->all();
+ auto nbItems = videoTracks.size() + audioTracks.size();
+ outputMedia.p_tracks = static_cast<vlc_ml_media_track_list_t*>(
+ calloc( 1, sizeof( *outputMedia.p_tracks ) +
+ nbItems * sizeof( *outputMedia.p_tracks->p_items ) ) );
+ if ( unlikely( outputMedia.p_tracks == nullptr ) )
+ return false;
+ outputMedia.p_tracks->i_nb_items = 0;
+
+ vlc_ml_media_track_t* items = outputMedia.p_tracks->p_items;
+ for ( const auto& t : videoTracks )
+ {
+ vlc_ml_media_track_t* output = &items[outputMedia.p_tracks->i_nb_items++];
+
+ if ( convertTracksCommon( output, t->codec(), t->language(), t->description() ) == false )
+ return false;
+ output->i_type = VLC_ML_TRACK_TYPE_VIDEO;
+ output->i_bitrate = t->bitrate();
+ output->v.i_fpsNum = t->fpsNum();
+ output->v.i_fpsDen = t->fpsDen();
+ output->v.i_sarNum = t->sarNum();
+ output->v.i_sarDen = t->sarDen();
+ }
+ for ( const auto& t : audioTracks )
+ {
+ vlc_ml_media_track_t* output = &items[outputMedia.p_tracks->i_nb_items++];
+
+ if ( convertTracksCommon( output, t->codec(), t->language(), t->description() ) == false )
+ return false;
+ output->i_type = VLC_ML_TRACK_TYPE_AUDIO;
+ output->i_bitrate = t->bitrate();
+ output->a.i_nbChannels = t->nbChannels();
+ output->a.i_sampleRate = t->sampleRate();
+ }
+ return true;
+}
+
+bool Convert( const medialibrary::IMedia* input, vlc_ml_media_t& output )
+{
+ output.i_id = input->id();
+
+ switch ( input->type() )
+ {
+ case medialibrary::IMedia::Type::Audio:
+ output.i_type = VLC_ML_MEDIA_TYPE_AUDIO;
+ switch( input->subType() )
+ {
+ case medialibrary::IMedia::SubType::AlbumTrack:
+ {
+ output.i_subtype = VLC_ML_MEDIA_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 = VLC_ML_MEDIA_TYPE_VIDEO;
+ switch( input->subType() )
+ {
+ case medialibrary::IMedia::SubType::Movie:
+ {
+ output.i_subtype = VLC_ML_MEDIA_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:
+ {
+ output.i_subtype = VLC_ML_MEDIA_SUBTYPE_SHOW_EPISODE;
+ auto episode = input->showEpisode();
+ if ( episode == nullptr )
+ return false;
+ if ( Convert( episode.get(), output.show_episode ) == false )
+ return false;
+ break;
+ }
+ case medialibrary::IMedia::SubType::Unknown:
+ output.i_subtype = VLC_ML_MEDIA_SUBTYPE_UNKNOWN;
+ break;
+ case medialibrary::IMedia::SubType::AlbumTrack:
+ vlc_assert_unreachable();
+ }
+ break;
+ }
+ case medialibrary::IMedia::Type::External:
+ output.i_type = VLC_ML_MEDIA_TYPE_EXTERNAL;
+ break;
+ case medialibrary::IMedia::Type::Stream:
+ output.i_type = VLC_ML_MEDIA_TYPE_STREAM;
+ 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.i_last_played_date = input->lastPlayedDate();
+
+ 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<vlc_ml_file_list_t>( files );
+ if ( output.p_files == nullptr )
+ return false;
+
+ if ( convertTracks( input, output ) == false )
+ return false;
+
+ if ( input->isThumbnailGenerated() == true )
+ {
+ output.psz_artwork_mrl = strdup( input->thumbnail().c_str() );
+ if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_artwork_mrl = nullptr;
+
+ return true;
+}
+
+bool Convert( const medialibrary::IFile* input, vlc_ml_file_t& output )
+{
+ switch ( input->type() )
+ {
+ case medialibrary::IFile::Type::Main:
+ output.i_type = VLC_ML_FILE_TYPE_MAIN;
+ break;
+ case medialibrary::IFile::Type::Part:
+ output.i_type = VLC_ML_FILE_TYPE_PART;
+ break;
+ case medialibrary::IFile::Type::Soundtrack:
+ output.i_type = VLC_ML_FILE_TYPE_SOUNDTRACK;
+ break;
+ case medialibrary::IFile::Type::Subtitles:
+ output.i_type = VLC_ML_FILE_TYPE_SUBTITLE;
+ break;
+ case medialibrary::IFile::Type::Playlist:
+ output.i_type = VLC_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, vlc_ml_album_t& output )
+{
+ output.i_id = input->id();
+ 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;
+ }
+ else
+ output.psz_title = nullptr;
+ if ( input->shortSummary().empty() == false )
+ {
+ output.psz_summary = strdup( input->shortSummary().c_str() );
+ if ( unlikely( output.psz_summary == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_summary = nullptr;
+ if ( input->artworkMrl().empty() == false )
+ {
+ output.psz_artwork_mrl = strdup( input->artworkMrl().c_str() );
+ if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_artwork_mrl = nullptr;
+ 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, vlc_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;
+ }
+ else
+ output.psz_shortbio = nullptr;
+ if ( input->artworkMrl().empty() == false )
+ {
+ output.psz_artwork_mrl = strdup( input->artworkMrl().c_str() );
+ if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_artwork_mrl = nullptr;
+ if ( input->musicBrainzId().empty() == false )
+ {
+ output.psz_mb_id = strdup( input->musicBrainzId().c_str() );
+ if ( unlikely( output.psz_mb_id == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_mb_id = nullptr;
+ return true;
+}
+
+void Release( vlc_ml_genre_t& genre )
+{
+ free( genre.psz_name );
+}
+
+bool Convert( const medialibrary::IGenre* input, vlc_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, vlc_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->title().empty() == false )
+ {
+ output.psz_name = strdup( input->title().c_str() );
+ if ( output.psz_name == nullptr )
+ return false;
+ }
+ else
+ output.psz_name = nullptr;
+ if ( input->artworkMrl().empty() == false )
+ {
+ output.psz_artwork_mrl = strdup( input->artworkMrl().c_str() );
+ if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_artwork_mrl = nullptr;
+ if ( input->tvdbId().empty() == false )
+ {
+ output.psz_tvdb_id = strdup( input->tvdbId().c_str() );
+ if ( unlikely( output.psz_tvdb_id == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_tvdb_id = nullptr;
+ if ( input->shortSummary().empty() == false )
+ {
+ output.psz_summary = strdup( input->shortSummary().c_str() );
+ if ( unlikely( output.psz_summary == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_summary = nullptr;
+ return true;
+}
+
+bool Convert( const medialibrary::ILabel* input, vlc_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;
+}
+
+bool Convert( const medialibrary::IPlaylist* input, vlc_ml_playlist_t& output )
+{
+ output.i_id = input->id();
+ if ( input->name().empty() == false )
+ {
+ output.psz_name = strdup( input->name().c_str() );
+ if ( unlikely( output.psz_name == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_name = nullptr;
+ if ( input->artworkMrl().empty() != false )
+ {
+ output.psz_artwork_mrl = strdup( input->artworkMrl().c_str() );
+ if ( unlikely( output.psz_artwork_mrl == nullptr ) )
+ return false;
+ }
+ else
+ output.psz_artwork_mrl = nullptr;
+ output.i_creation_date = input->creationDate();
+ return true;
+}
diff --git a/modules/misc/medialibrary/medialib.cpp b/modules/misc/medialibrary/medialib.cpp
new file mode 100644
index 0000000000..0a60024582
--- /dev/null
+++ b/modules/misc/medialibrary/medialib.cpp
@@ -0,0 +1,1190 @@
+/*****************************************************************************
+ * 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>
+#include <medialibrary/IShow.h>
+#include <medialibrary/IPlaylist.h>
+
+#include <sstream>
+
+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_obj( obj )
+{
+}
+
+bool MediaLibrary::Start()
+{
+ if ( m_ml != nullptr )
+ return true;
+
+ std::unique_ptr<medialibrary::IMediaLibrary> ml( NewMediaLibrary() );
+
+ m_logger.reset( new Logger( m_obj ) );
+ ml->setVerbosity( medialibrary::LogLevel::Info );
+ ml->setLogger( m_logger.get() );
+
+ auto userDir = vlc::wrap_cptr( config_GetUserDir( VLC_USERDATA_DIR ) );
+ std::string mlDir = std::string{ userDir.get() } + "/ml/";
+
+ auto initStatus = ml->initialize( mlDir + "ml.db", mlDir + "thumbnails/", this );
+ switch ( initStatus )
+ {
+ case medialibrary::InitializeResult::AlreadyInitialized:
+ msg_Info( m_obj, "MediaLibrary was already initialized" );
+ return true;
+ case medialibrary::InitializeResult::Failed:
+ msg_Err( m_obj, "Medialibrary failed to initialize" );
+ return false;
+ case medialibrary::InitializeResult::DbReset:
+ msg_Info( m_obj, "Database was reset" );
+ break;
+ case medialibrary::InitializeResult::Success:
+ msg_Dbg( m_obj, "MediaLibrary successfully initialized" );
+ break;
+ }
+
+ ml->addParserService( std::make_shared<MetadataExtractor>( m_obj ) );
+ auto res = ml->start();
+ if ( res == false )
+ {
+ msg_Err( m_obj, "Failed to start the MediaLibrary" );
+ return false;
+ }
+ auto folders = vlc::wrap_cptr( var_InheritString( m_obj, "ml-folders" ) );
+ if ( folders != nullptr && strlen( folders.get() ) > 0 )
+ {
+ std::stringstream ss( folders.get() );
+ std::string folder;
+ while ( std::getline( ss, folder, ';' ) )
+ ml->discover( folder );
+ }
+ else
+ {
+ auto videoFolder = vlc::wrap_cptr( config_GetUserDir( VLC_VIDEOS_DIR ) );
+ std::string varValue;
+ if ( videoFolder != nullptr )
+ {
+ auto mrl = std::string{ "file://" } + videoFolder.get();
+ ml->discover( mrl );
+ varValue = mrl;
+ }
+ auto musicFolder = vlc::wrap_cptr( config_GetUserDir( VLC_MUSIC_DIR ) );
+ if ( musicFolder != nullptr )
+ {
+ auto mrl = std::string{ "file://" } + musicFolder.get();
+ ml->discover( mrl );
+ if ( varValue.empty() == false )
+ varValue += ";";
+ varValue += mrl;
+ }
+ if ( varValue.empty() == false )
+ config_PutPsz( "ml-folders", varValue.c_str() );
+ }
+ ml->reload();
+ m_ml = std::move( ml );
+ return true;
+}
+
+int MediaLibrary::Control( int query, va_list args )
+{
+ if ( Start() == false )
+ return VLC_EGENERIC;
+
+ switch ( query )
+ {
+ case VLC_ML_ADD_FOLDER:
+ case VLC_ML_REMOVE_FOLDER:
+ case VLC_ML_BAN_FOLDER:
+ case VLC_ML_UNBAN_FOLDER:
+ {
+ const char* mrl = va_arg( args, const char* );
+ switch( query )
+ {
+ case VLC_ML_ADD_FOLDER:
+ m_ml->discover( mrl );
+ break;
+ case VLC_ML_REMOVE_FOLDER:
+ m_ml->removeEntryPoint( mrl );
+ break;
+ case VLC_ML_BAN_FOLDER:
+ m_ml->banFolder( mrl );
+ break;
+ case VLC_ML_UNBAN_FOLDER:
+ m_ml->unbanFolder( mrl );
+ break;
+ }
+ break;
+ }
+ case VLC_ML_LIST_FOLDERS:
+ {
+ auto entryPoints = m_ml->entryPoints()->all();
+ auto nbItem = entryPoints.size();
+ auto list = vlc::wrap_carray( reinterpret_cast<vlc_ml_entrypoint_t*>(
+ calloc( entryPoints.size(), sizeof( vlc_ml_entrypoint_t ) ) ),
+ [nbItem]( vlc_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, vlc_ml_entrypoint_t**) ) = list.release();
+ *(va_arg( args, size_t*) ) = entryPoints.size();
+ break;
+ }
+ case VLC_ML_PAUSE_BACKGROUND:
+ m_ml->pauseBackgroundOperations();
+ break;
+ case VLC_ML_RESUME_BACKGROUND:
+ m_ml->resumeBackgroundOperations();
+ break;
+ case VLC_ML_CLEAR_HISTORY:
+ m_ml->clearHistory();
+ break;
+ case VLC_ML_NEW_EXTERNAL_MEDIA:
+ {
+ auto mrl = va_arg( args, const char* );
+ auto media = m_ml->addExternalMedia( mrl );
+ if ( media == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, vlc_ml_media_t**) = CreateAndConvert<vlc_ml_media_t>( media.get() );
+ return VLC_SUCCESS;
+ }
+ case VLC_ML_NEW_STREAM:
+ {
+ auto mrl = va_arg( args, const char* );
+ auto media = m_ml->addStream( mrl );
+ if ( media == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, vlc_ml_media_t**) = CreateAndConvert<vlc_ml_media_t>( media.get() );
+ return VLC_SUCCESS;
+ }
+ case VLC_ML_MEDIA_INCREASE_PLAY_COUNT:
+ case VLC_ML_MEDIA_GET_MEDIA_PLAYBACK_PREF:
+ case VLC_ML_MEDIA_SET_MEDIA_PLAYBACK_PREF:
+ case VLC_ML_MEDIA_SET_THUMBNAIL:
+ return controlMedia( query, args );
+ default:
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
+}
+
+int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_list args )
+{
+ if ( Start() == false )
+ return VLC_EGENERIC;
+
+ medialibrary::QueryParameters p{};
+ medialibrary::QueryParameters* paramsPtr = nullptr;
+ uint32_t nbItems = 0;
+ uint32_t offset = 0;
+ const char* psz_pattern = nullptr;
+ if ( params )
+ {
+ p.desc = params->b_desc;
+ p.sort = sortingCriteria( params->i_sort );
+ nbItems = params->i_nbResults;
+ offset = params->i_offset;
+ psz_pattern = params->psz_pattern;
+ paramsPtr = &p;
+ }
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_MEDIA_OF:
+ case VLC_ML_COUNT_MEDIA_OF:
+ case VLC_ML_LIST_ARTISTS_OF:
+ case VLC_ML_COUNT_ARTISTS_OF:
+ case VLC_ML_LIST_ALBUMS_OF:
+ case VLC_ML_COUNT_ALBUMS_OF:
+ {
+ auto parentType = va_arg( args, int );
+ listQuery = filterListChildrenQuery( listQuery, parentType );
+ }
+ default:
+ break;
+ }
+ switch( listQuery )
+ {
+ case VLC_ML_LIST_ALBUM_TRACKS:
+ case VLC_ML_COUNT_ALBUM_TRACKS:
+ case VLC_ML_LIST_ALBUM_ARTISTS:
+ case VLC_ML_COUNT_ALBUM_ARTISTS:
+ return listAlbums( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
+
+ case VLC_ML_LIST_ARTIST_ALBUMS:
+ case VLC_ML_COUNT_ARTIST_ALBUMS:
+ case VLC_ML_LIST_ARTIST_TRACKS:
+ case VLC_ML_COUNT_ARTIST_TRACKS:
+ return listArtists( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
+
+ case VLC_ML_LIST_VIDEOS:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchVideo( psz_pattern, paramsPtr );
+ else
+ query = m_ml->videoFiles( paramsPtr );
+ auto res = ml_convert_list<vlc_ml_media_list_t>( query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_media_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_VIDEOS:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchVideo( psz_pattern, paramsPtr );
+ else
+ query = m_ml->videoFiles( paramsPtr );
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_AUDIOS:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAudio( psz_pattern, paramsPtr );
+ else
+ query = m_ml->audioFiles( paramsPtr );
+ auto res = ml_convert_list<vlc_ml_media_list_t>( query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_media_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_AUDIOS:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAudio( psz_pattern, paramsPtr );
+ else
+ query = m_ml->audioFiles( paramsPtr );
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_ALBUMS:
+ {
+ medialibrary::Query<medialibrary::IAlbum> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAlbums( psz_pattern, paramsPtr );
+ else
+ query = m_ml->albums( paramsPtr );
+ auto res = ml_convert_list<vlc_ml_album_list_t>( query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_album_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_ALBUMS:
+ {
+ medialibrary::Query<medialibrary::IAlbum> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAlbums( psz_pattern, paramsPtr );
+ else
+ query = m_ml->albums( paramsPtr );
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_GENRES:
+ {
+ medialibrary::Query<medialibrary::IGenre> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchGenre( psz_pattern, paramsPtr );
+ else
+ query = m_ml->genres( paramsPtr );
+ auto res = ml_convert_list<vlc_ml_genre_list_t>( query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_genre_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_GENRES:
+ {
+ medialibrary::Query<medialibrary::IGenre> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchGenre( psz_pattern, paramsPtr );
+ else
+ query = m_ml->genres( paramsPtr );
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_ARTISTS:
+ {
+ medialibrary::Query<medialibrary::IArtist> query;
+ bool includeAll = va_arg( args, int ) != 0;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchArtists( psz_pattern, paramsPtr );
+ else
+ {
+ query = m_ml->artists( includeAll, paramsPtr );
+ }
+ auto res = ml_convert_list<vlc_ml_artist_list_t>( query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_artist_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_ARTISTS:
+ {
+ medialibrary::Query<medialibrary::IArtist> query;
+ bool includeAll = va_arg( args, int ) != 0;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchArtists( psz_pattern, paramsPtr );
+ else
+ query = m_ml->artists( includeAll, paramsPtr );
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_GENRE_ARTISTS:
+ case VLC_ML_COUNT_GENRE_ARTISTS:
+ case VLC_ML_LIST_GENRE_TRACKS:
+ case VLC_ML_COUNT_GENRE_TRACKS:
+ case VLC_ML_LIST_GENRE_ALBUMS:
+ case VLC_ML_COUNT_GENRE_ALBUMS:
+ return listGenre( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
+
+ case VLC_ML_LIST_MEDIA_LABELS:
+ case VLC_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 VLC_ML_LIST_MEDIA_LABELS:
+ *va_arg( args, vlc_ml_label_list_t**) = ml_convert_list<vlc_ml_label_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_MEDIA_LABELS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ case VLC_ML_LIST_SHOWS:
+ {
+ medialibrary::Query<medialibrary::IShow> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchShows( psz_pattern, paramsPtr );
+ else
+ query = m_ml->shows( paramsPtr );
+ *va_arg( args, vlc_ml_show_list_t** ) = ml_convert_list<vlc_ml_show_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ }
+ case VLC_ML_COUNT_SHOWS:
+ {
+ auto query = m_ml->shows( paramsPtr );
+ *va_arg( args, int64_t* ) = query->count();
+ return VLC_SUCCESS;
+ }
+ case VLC_ML_LIST_SHOW_EPISODES:
+ case VLC_ML_COUNT_SHOW_EPISODES:
+ {
+ auto show = m_ml->show( va_arg( args, int64_t ) );
+ if ( show == nullptr )
+ return VLC_EGENERIC;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = show->searchEpisodes( psz_pattern, paramsPtr );
+ else
+ query = show->episodes( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_SHOW_EPISODES:
+ *va_arg( args, vlc_ml_media_list_t**) = ml_convert_list<vlc_ml_media_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_SHOW_EPISODES:
+ *va_arg( args, int64_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ case VLC_ML_LIST_PLAYLIST_MEDIA:
+ case VLC_ML_COUNT_PLAYLIST_MEDIA:
+ case VLC_ML_LIST_PLAYLISTS:
+ case VLC_ML_COUNT_PLAYLISTS:
+ return listPlaylist( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
+ case VLC_ML_LIST_HISTORY:
+ {
+ auto query = m_ml->history();
+ *va_arg( args, vlc_ml_media_list_t**) = ml_convert_list<vlc_ml_media_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ }
+ case VLC_ML_LIST_STREAM_HISTORY:
+ {
+ auto query = m_ml->streamHistory();
+ *va_arg( args, vlc_ml_media_list_t**) = ml_convert_list<vlc_ml_media_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ }
+ }
+ return VLC_SUCCESS;
+}
+
+void* MediaLibrary::Get( int query, int64_t id )
+{
+ if ( Start() == false )
+ return nullptr;
+
+ switch ( query )
+ {
+ case VLC_ML_GET_MEDIA:
+ {
+ auto media = m_ml->media( id );
+ return CreateAndConvert<vlc_ml_media_t>( media.get() );
+ }
+ case VLC_ML_GET_ALBUM:
+ {
+ auto album = m_ml->album( id );
+ return CreateAndConvert<vlc_ml_album_t>( album.get() );
+ }
+ case VLC_ML_GET_ARTIST:
+ {
+ auto artist = m_ml->artist( id );
+ return CreateAndConvert<vlc_ml_artist_t>( artist.get() );
+ }
+ case VLC_ML_GET_GENRE:
+ {
+ auto genre = m_ml->genre( id );
+ return CreateAndConvert<vlc_ml_genre_t>( genre.get() );
+ }
+ case VLC_ML_GET_SHOW:
+ {
+ auto show = m_ml->show( id );
+ return CreateAndConvert<vlc_ml_show_t>( show.get() );
+ }
+ case VLC_ML_GET_PLAYLIST:
+ {
+ auto playlist = m_ml->playlist( id );
+ return CreateAndConvert<vlc_ml_playlist_t>( playlist.get() );
+ }
+ default:
+ vlc_assert_unreachable();
+
+ }
+ return nullptr;
+}
+
+medialibrary::IMedia::MetadataType MediaLibrary::metadataType( int meta )
+{
+ switch ( meta )
+ {
+ case VLC_ML_PLAYBACK_PREF_RATING:
+ return medialibrary::IMedia::MetadataType::Rating;
+ case VLC_ML_PLAYBACK_PREF_PROGRESS:
+ return medialibrary::IMedia::MetadataType::Progress;
+ case VLC_ML_PLAYBACK_PREF_SPEED:
+ return medialibrary::IMedia::MetadataType::Speed;
+ case VLC_ML_PLAYBACK_PREF_TITLE:
+ return medialibrary::IMedia::MetadataType::Title;
+ case VLC_ML_PLAYBACK_PREF_CHAPTER:
+ return medialibrary::IMedia::MetadataType::Chapter;
+ case VLC_ML_PLAYBACK_PREF_PROGRAM:
+ return medialibrary::IMedia::MetadataType::Program;
+ case VLC_ML_PLAYBACK_PREF_SEEN:
+ return medialibrary::IMedia::MetadataType::Seen;
+ case VLC_ML_PLAYBACK_PREF_VIDEO_TRACK:
+ return medialibrary::IMedia::MetadataType::VideoTrack;
+ case VLC_ML_PLAYBACK_PREF_ASPECT_RATIO:
+ return medialibrary::IMedia::MetadataType::AspectRatio;
+ case VLC_ML_PLAYBACK_PREF_ZOOM:
+ return medialibrary::IMedia::MetadataType::Zoom;
+ case VLC_ML_PLAYBACK_PREF_CROP:
+ return medialibrary::IMedia::MetadataType::Crop;
+ case VLC_ML_PLAYBACK_PREF_DEINTERLACE:
+ return medialibrary::IMedia::MetadataType::Deinterlace;
+ case VLC_ML_PLAYBACK_PREF_VIDEO_FILTER:
+ return medialibrary::IMedia::MetadataType::VideoFilter;
+ case VLC_ML_PLAYBACK_PREF_AUDIO_TRACK:
+ return medialibrary::IMedia::MetadataType::AudioTrack;
+ case VLC_ML_PLAYBACK_PREF_GAIN:
+ return medialibrary::IMedia::MetadataType::Gain;
+ case VLC_ML_PLAYBACK_PREF_AUDIO_DELAY:
+ return medialibrary::IMedia::MetadataType::AudioDelay;
+ case VLC_ML_PLAYBACK_PREF_SUBTITLE_TRACK:
+ return medialibrary::IMedia::MetadataType::SubtitleTrack;
+ case VLC_ML_PLAYBACK_PREF_SUBTITLE_DELAY:
+ return medialibrary::IMedia::MetadataType::SubtitleDelay;
+ case VLC_ML_PLAYBACK_PREF_APP_SPECIFIC:
+ return medialibrary::IMedia::MetadataType::ApplicationSpecific;
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+medialibrary::SortingCriteria MediaLibrary::sortingCriteria(int sort)
+{
+ switch ( sort )
+ {
+ case VLC_ML_SORTING_DEFAULT:
+ return medialibrary::SortingCriteria::Default;
+ case VLC_ML_SORTING_ALPHA:
+ return medialibrary::SortingCriteria::Alpha;
+ case VLC_ML_SORTING_DURATION:
+ return medialibrary::SortingCriteria::Duration;
+ case VLC_ML_SORTING_INSERTIONDATE:
+ return medialibrary::SortingCriteria::InsertionDate;
+ case VLC_ML_SORTING_LASTMODIFICATIONDATE:
+ return medialibrary::SortingCriteria::LastModificationDate;
+ case VLC_ML_SORTING_RELEASEDATE:
+ return medialibrary::SortingCriteria::ReleaseDate;
+ case VLC_ML_SORTING_FILESIZE:
+ return medialibrary::SortingCriteria::FileSize;
+ case VLC_ML_SORTING_ARTIST:
+ return medialibrary::SortingCriteria::Artist;
+ case VLC_ML_SORTING_PLAYCOUNT:
+ return medialibrary::SortingCriteria::PlayCount;
+ case VLC_ML_SORTING_ALBUM:
+ return medialibrary::SortingCriteria::Album;
+ case VLC_ML_SORTING_FILENAME:
+ return medialibrary::SortingCriteria::Filename;
+ case VLC_ML_SORTING_TRACKNUMBER:
+ return medialibrary::SortingCriteria::TrackNumber;
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+int MediaLibrary::getMeta( const medialibrary::IMedia& media, int meta, char** result )
+{
+ 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( medialibrary::IMedia& media, int meta, const char* value )
+{
+ 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;
+}
+
+int MediaLibrary::controlMedia( int query, va_list args )
+{
+ auto mediaId = va_arg( args, int64_t );
+ auto m = m_ml->media( mediaId );
+ if ( m == nullptr )
+ return VLC_EGENERIC;
+ switch( query )
+ {
+ case VLC_ML_MEDIA_INCREASE_PLAY_COUNT:
+ if ( m->increasePlayCount() == false )
+ return VLC_EGENERIC;
+ return VLC_SUCCESS;
+ case VLC_ML_MEDIA_GET_MEDIA_PLAYBACK_PREF:
+ {
+ auto meta = va_arg( args, int );
+ auto res = va_arg( args, char** );
+ return getMeta( *m, meta, res );
+ }
+ case VLC_ML_MEDIA_SET_MEDIA_PLAYBACK_PREF:
+ {
+ auto meta = va_arg( args, int );
+ auto value = va_arg( args, const char* );
+ return setMeta( *m, meta, value );
+ }
+ case VLC_ML_MEDIA_SET_THUMBNAIL:
+ {
+ auto mrl = va_arg( args, const char* );
+ m->setThumbnail( mrl );
+ return VLC_SUCCESS;
+ }
+ case VLC_ML_MEDIA_ADD_EXTERNAL_MRL:
+ {
+ auto mrl = va_arg( args, const char* );
+ auto type = va_arg( args, int );
+ medialibrary::IFile::Type mlType;
+ switch ( type )
+ {
+ case VLC_ML_FILE_TYPE_UNKNOWN:
+ // The type can't be main since this is added to an existing media
+ // which must already have a file
+ case VLC_ML_FILE_TYPE_MAIN:
+ case VLC_ML_FILE_TYPE_PLAYLIST:
+ return VLC_EGENERIC;
+ case VLC_ML_FILE_TYPE_PART:
+ mlType = medialibrary::IFile::Type::Part;
+ break;
+ case VLC_ML_FILE_TYPE_SOUNDTRACK:
+ mlType = medialibrary::IFile::Type::Soundtrack;
+ break;
+ case VLC_ML_FILE_TYPE_SUBTITLE:
+ mlType = medialibrary::IFile::Type::Subtitles;
+ break;
+ }
+ if ( m->addExternalMrl( mrl, mlType ) == nullptr )
+ return VLC_EGENERIC;
+ return VLC_SUCCESS;
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+int MediaLibrary::filterListChildrenQuery( int query, int parentType )
+{
+ switch( query )
+ {
+ case VLC_ML_LIST_MEDIA_OF:
+ switch ( parentType )
+ {
+ case VLC_ML_PARENT_ALBUM:
+ return VLC_ML_LIST_ALBUM_TRACKS;
+ case VLC_ML_PARENT_ARTIST:
+ return VLC_ML_LIST_ALBUM_TRACKS;
+ case VLC_ML_PARENT_SHOW:
+ return VLC_ML_LIST_SHOW_EPISODES;
+ case VLC_ML_PARENT_GENRE:
+ return VLC_ML_LIST_GENRE_TRACKS;
+ case VLC_ML_PARENT_PLAYLIST:
+ return VLC_ML_LIST_PLAYLIST_MEDIA;
+ default:
+ vlc_assert_unreachable();
+ }
+ case VLC_ML_COUNT_MEDIA_OF:
+ switch ( parentType )
+ {
+ case VLC_ML_PARENT_ALBUM:
+ return VLC_ML_COUNT_ALBUM_TRACKS;
+ case VLC_ML_PARENT_ARTIST:
+ return VLC_ML_COUNT_ALBUM_TRACKS;
+ case VLC_ML_PARENT_SHOW:
+ return VLC_ML_COUNT_SHOW_EPISODES;
+ case VLC_ML_PARENT_GENRE:
+ return VLC_ML_COUNT_GENRE_TRACKS;
+ case VLC_ML_PARENT_PLAYLIST:
+ return VLC_ML_COUNT_PLAYLIST_MEDIA;
+ default:
+ vlc_assert_unreachable();
+ }
+ case VLC_ML_LIST_ALBUMS_OF:
+ switch ( parentType )
+ {
+ case VLC_ML_PARENT_ARTIST:
+ return VLC_ML_LIST_ARTIST_ALBUMS;
+ case VLC_ML_PARENT_GENRE:
+ return VLC_ML_LIST_GENRE_ALBUMS;
+ default:
+ vlc_assert_unreachable();
+ }
+ case VLC_ML_COUNT_ALBUMS_OF:
+ switch ( parentType )
+ {
+ case VLC_ML_PARENT_ARTIST:
+ return VLC_ML_COUNT_ARTIST_ALBUMS;
+ case VLC_ML_PARENT_GENRE:
+ return VLC_ML_COUNT_GENRE_ALBUMS;
+ default:
+ vlc_assert_unreachable();
+ }
+ case VLC_ML_LIST_ARTISTS_OF:
+ switch ( parentType )
+ {
+ case VLC_ML_PARENT_ALBUM:
+ return VLC_ML_LIST_ALBUM_ARTISTS;
+ case VLC_ML_PARENT_ARTIST:
+ return VLC_ML_LIST_GENRE_ARTISTS;
+ default:
+ vlc_assert_unreachable();
+ }
+ case VLC_ML_COUNT_ARTISTS_OF:
+ switch ( parentType )
+ {
+ case VLC_ML_PARENT_ALBUM:
+ return VLC_ML_COUNT_ALBUM_ARTISTS;
+ case VLC_ML_PARENT_ARTIST:
+ return VLC_ML_COUNT_GENRE_ARTISTS;
+ default:
+ vlc_assert_unreachable();
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+int MediaLibrary::listAlbums( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
+{
+ auto album = m_ml->album( va_arg( args, int64_t ) );
+ if ( album == nullptr )
+ return VLC_EGENERIC;
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_ALBUM_TRACKS:
+ case VLC_ML_COUNT_ALBUM_TRACKS:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( pattern != nullptr )
+ query = album->searchTracks( pattern, paramsPtr );
+ else
+ query = album->tracks( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_ALBUM_TRACKS:
+ *va_arg( args, vlc_ml_media_list_t**) = ml_convert_list<vlc_ml_media_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_ALBUM_TRACKS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ case VLC_ML_LIST_ALBUM_ARTISTS:
+ case VLC_ML_COUNT_ALBUM_ARTISTS:
+ {
+ auto query = album->artists( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_ALBUM_ARTISTS:
+ *va_arg( args, vlc_ml_artist_list_t**) = ml_convert_list<vlc_ml_artist_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_ALBUM_ARTISTS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+int MediaLibrary::listArtists( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset,
+ va_list args )
+{
+ auto artist = m_ml->artist( va_arg( args, int64_t ) );
+ if ( artist == nullptr )
+ return VLC_EGENERIC;
+ switch( listQuery )
+ {
+ case VLC_ML_LIST_ARTIST_ALBUMS:
+ case VLC_ML_COUNT_ARTIST_ALBUMS:
+ {
+ medialibrary::Query<medialibrary::IAlbum> query;
+ if ( pattern != nullptr )
+ query = artist->searchAlbums( pattern, paramsPtr );
+ else
+ query = artist->albums( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_ARTIST_ALBUMS:
+ *va_arg( args, vlc_ml_album_list_t**) = ml_convert_list<vlc_ml_album_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_ARTIST_ALBUMS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ case VLC_ML_LIST_ARTIST_TRACKS:
+ case VLC_ML_COUNT_ARTIST_TRACKS:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( pattern != nullptr )
+ query = artist->searchTracks( pattern, paramsPtr );
+ else
+ query = artist->tracks( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_ARTIST_TRACKS:
+ *va_arg( args, vlc_ml_media_list_t**) = ml_convert_list<vlc_ml_media_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_ARTIST_TRACKS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+int MediaLibrary::listGenre( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
+{
+ auto genre = m_ml->genre( va_arg( args, int64_t ) );
+ if ( genre == nullptr )
+ return VLC_EGENERIC;
+ switch( listQuery )
+ {
+ case VLC_ML_LIST_GENRE_ARTISTS:
+ case VLC_ML_COUNT_GENRE_ARTISTS:
+ {
+ medialibrary::Query<medialibrary::IArtist> query;
+ if ( pattern != nullptr )
+ query = genre->searchArtists( pattern, paramsPtr );
+ else
+ query = genre->artists( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_GENRE_ARTISTS:
+ *va_arg( args, vlc_ml_artist_list_t**) = ml_convert_list<vlc_ml_artist_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_GENRE_ARTISTS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ case VLC_ML_LIST_GENRE_TRACKS:
+ case VLC_ML_COUNT_GENRE_TRACKS:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( pattern != nullptr )
+ query = genre->searchTracks( pattern, paramsPtr );
+ else
+ query = genre->tracks( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_GENRE_TRACKS:
+ *va_arg( args, vlc_ml_media_list_t**) = ml_convert_list<vlc_ml_media_list_t>( query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_GENRE_TRACKS:
+ *va_arg( args, size_t*) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ case VLC_ML_LIST_GENRE_ALBUMS:
+ case VLC_ML_COUNT_GENRE_ALBUMS:
+ {
+ medialibrary::Query<medialibrary::IAlbum> query;
+ if ( pattern != nullptr )
+ query = genre->searchAlbums( pattern, paramsPtr );
+ else
+ query = genre->albums( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_GENRE_ALBUMS:
+ *va_arg( args, vlc_ml_album_list_t**) = ml_convert_list<vlc_ml_album_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_GENRE_ALBUMS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+int MediaLibrary::listPlaylist( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
+{
+ switch( listQuery )
+ {
+ case VLC_ML_LIST_PLAYLISTS:
+ case VLC_ML_COUNT_PLAYLISTS:
+ {
+ medialibrary::Query<medialibrary::IPlaylist> query;
+ if ( pattern != nullptr )
+ query = m_ml->searchPlaylists( pattern, paramsPtr );
+ else
+ query = m_ml->playlists( paramsPtr );
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_PLAYLISTS:
+ *va_arg( args, vlc_ml_playlist_list_t** ) = ml_convert_list<vlc_ml_playlist_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_PLAYLISTS:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ case VLC_ML_LIST_PLAYLIST_MEDIA:
+ case VLC_ML_COUNT_PLAYLIST_MEDIA:
+ {
+ auto playlist = m_ml->playlist( va_arg( args, int64_t ) );
+ if ( playlist == nullptr )
+ return VLC_EGENERIC;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( pattern != nullptr )
+ query = playlist->searchMedia( pattern, paramsPtr );
+ else
+ query = playlist->media();
+ switch ( listQuery )
+ {
+ case VLC_ML_LIST_PLAYLIST_MEDIA:
+ *va_arg( args, vlc_ml_media_list_t**) = ml_convert_list<vlc_ml_media_list_t>(
+ query->items( nbItems, offset ) );
+ return VLC_SUCCESS;
+ case VLC_ML_COUNT_PLAYLIST_MEDIA:
+ *va_arg( args, size_t* ) = query->count();
+ return VLC_SUCCESS;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+ default:
+ vlc_assert_unreachable();
+ }
+}
+
+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 vlc_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;
+}
+
+#define ML_FOLDER_TEXT _( "Folders discovered by the media library" )
+#define ML_FOLDER_LONGTEXT _( "Semicolon separated list of folders to discover " \
+ "media from" )
+
+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)
+ add_string( "ml-folders", nullptr, ML_FOLDER_TEXT, ML_FOLDER_LONGTEXT, false )
+vlc_module_end()
diff --git a/modules/misc/medialibrary/medialibrary.h b/modules/misc/medialibrary/medialibrary.h
new file mode 100644
index 0000000000..16d1b4b3f1
--- /dev/null
+++ b/modules/misc/medialibrary/medialibrary.h
@@ -0,0 +1,223 @@
+/*****************************************************************************
+ * 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 <vlc_cxx_helpers.hpp>
+
+#include <cstdarg>
+
+struct vlc_event_t;
+struct vlc_object_t;
+
+class Logger;
+
+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( const vlc_input_event* event, ParseContext& ctx );
+ void onSubItemAdded( const vlc_event_t* event, ParseContext& ctx );
+ void populateItem( medialibrary::parser::IItem& item, input_item_t* inputItem );
+
+ static void onInputEvent( input_thread_t *input, void *user_data,
+ const struct vlc_input_event *event );
+ 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 );
+ bool Start();
+ int Control( int query, va_list args );
+ int List( int query, const vlc_ml_query_params_t* params, va_list args );
+ void* Get( int query, int64_t id );
+
+private:
+ int controlMedia( int query, va_list args );
+ int getMeta( const medialibrary::IMedia& media, int meta, char** result );
+ int setMeta( medialibrary::IMedia& media, int meta, const char* value );
+ int filterListChildrenQuery( int query, int parentType );
+ int listAlbums( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset, va_list args );
+ int listArtists( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset, va_list args );
+ int listGenre( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset, va_list args );
+ int listPlaylist( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+ const char* pattern, uint32_t nbItems, uint32_t offset, va_list args );
+
+ static medialibrary::IMedia::MetadataType metadataType( int meta );
+ static medialibrary::SortingCriteria sortingCriteria( int sort );
+
+private:
+ vlc_object_t* m_obj;
+ 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, vlc_ml_media_t& output );
+bool Convert( const medialibrary::IFile* input, vlc_ml_file_t& output );
+bool Convert( const medialibrary::IMovie* input, vlc_ml_movie_t& output );
+bool Convert( const medialibrary::IShowEpisode* input, vlc_ml_show_episode_t& output );
+bool Convert( const medialibrary::IAlbumTrack* input, vlc_ml_album_track_t& output );
+bool Convert( const medialibrary::IAlbum* input, vlc_ml_album_t& output );
+bool Convert( const medialibrary::IArtist* input, vlc_ml_artist_t& output );
+bool Convert( const medialibrary::IGenre* input, vlc_ml_genre_t& output );
+bool Convert( const medialibrary::IShow* input, vlc_ml_show_t& output );
+bool Convert( const medialibrary::ILabel* input, vlc_ml_label_t& output );
+bool Convert( const medialibrary::IPlaylist* input, vlc_ml_playlist_t& output );
+
+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_array<decltype(To::p_items)>::value == true,
+ "Missing or invalid p_items member" );
+
+ // Allocate the ml_*_list_t
+ using ItemType = typename std::remove_extent<decltype(To::p_items)>::type;
+ auto list = vlc::wrap_cptr(
+ reinterpret_cast<To*>( malloc( sizeof( To ) + input.size() * sizeof( ItemType ) ) ),
+ static_cast<void(*)(To*)>( &vlc_ml_release_obj ) );
+ if ( unlikely( list == nullptr ) )
+ return nullptr;
+
+ list->i_nb_items = 0;
+
+ 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 )
+{
+ if ( input == nullptr )
+ return nullptr;
+ auto res = vlc::wrap_cptr(
+ reinterpret_cast<T*>( malloc( sizeof( T ) ) ),
+ static_cast<void(*)(T*)>( &vlc_ml_release_obj ) );
+ 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