medialibrary: Add playlist support
[vlc.git] / modules / misc / medialibrary / medialibrary.cpp
blob1df3c5947377a6a26617f7088eb1db3725c5801b
1 /*****************************************************************************
2 * medialibrary.cpp: medialibrary module
3 *****************************************************************************
4 * Copyright © 2008-2018 VLC authors, VideoLAN and VideoLabs
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_url.h>
28 #include <vlc_media_library.h>
29 #include <vlc_dialog.h>
30 #include "medialibrary.h"
31 #include "fs/fs.h"
32 #include "fs/devicelister.h"
34 #include <medialibrary/IMedia.h>
35 #include <medialibrary/IAlbumTrack.h>
36 #include <medialibrary/IAlbum.h>
37 #include <medialibrary/IArtist.h>
38 #include <medialibrary/IGenre.h>
39 #include <medialibrary/IMetadata.h>
40 #include <medialibrary/IShow.h>
41 #include <medialibrary/IPlaylist.h>
42 #include <medialibrary/IBookmark.h>
44 #include <sstream>
45 #include <initializer_list>
47 class Logger : public medialibrary::ILogger
49 public:
50 Logger( vlc_object_t* obj ) : m_obj( obj ) {}
52 private:
53 virtual void Error( const std::string& msg ) override
55 msg_Err( m_obj, "%s", msg.c_str() );
57 virtual void Warning( const std::string& msg ) override
59 msg_Warn( m_obj, "%s", msg.c_str() );
61 virtual void Info( const std::string& msg ) override
63 msg_Dbg( m_obj, "%s", msg.c_str() );
65 virtual void Debug( const std::string& msg ) override
67 msg_Dbg( m_obj, "%s", msg.c_str() );
69 virtual void Verbose( const std::string& msg ) override
71 msg_Dbg( m_obj, "%s", msg.c_str() );
74 private:
75 vlc_object_t* m_obj;
78 namespace
81 void assignToEvent( vlc_ml_event_t* ev, vlc_ml_media_t* m ) { ev->creation.p_media = m; }
82 void assignToEvent( vlc_ml_event_t* ev, vlc_ml_artist_t* a ) { ev->creation.p_artist = a; }
83 void assignToEvent( vlc_ml_event_t* ev, vlc_ml_album_t* a ) { ev->creation.p_album = a; }
84 void assignToEvent( vlc_ml_event_t* ev, vlc_ml_genre_t* g ) { ev->creation.p_genre = g; }
85 void assignToEvent( vlc_ml_event_t* ev, vlc_ml_playlist_t* p ) { ev->creation.p_playlist = p; }
86 void assignToEvent( vlc_ml_event_t* ev, vlc_ml_bookmark_t* b ) { ev->creation.p_bookmark = b; }
88 template <typename To, typename From>
89 void wrapEntityCreatedEventCallback( vlc_medialibrary_module_t* ml,
90 const std::vector<From>& entities,
91 vlc_ml_event_type evType )
93 vlc_ml_event_t ev;
94 ev.i_type = evType;
95 for ( const auto& e : entities )
97 auto val = vlc::wrap_cptr<To>( static_cast<To*>( calloc( 1, sizeof( To ) ) ),
98 static_cast<void(*)(To*)>( vlc_ml_release ) );
99 if ( unlikely( val == nullptr ) )
100 return;
101 if ( Convert( e.get(), *val ) == false )
102 return;
103 assignToEvent( &ev, val.get() );
104 ml->cbs->pf_send_event( ml, &ev );
108 void wrapEntityModifiedEventCallback( vlc_medialibrary_module_t* ml,
109 const std::set<int64_t>& ids,
110 vlc_ml_event_type evType )
112 vlc_ml_event_t ev;
113 ev.i_type = evType;
114 for ( const auto& id : ids )
116 ev.modification.i_entity_id = id;
117 ml->cbs->pf_send_event( ml, &ev );
121 void wrapEntityDeletedEventCallback( vlc_medialibrary_module_t* ml,
122 const std::set<int64_t>& ids, vlc_ml_event_type evType )
124 vlc_ml_event_t ev;
125 ev.i_type = evType;
126 for ( const auto& id : ids )
128 ev.deletion.i_entity_id = id;
129 ml->cbs->pf_send_event( ml, &ev );
133 } // end of anonymous namespace
135 void MediaLibrary::onMediaAdded( std::vector<medialibrary::MediaPtr> media )
137 wrapEntityCreatedEventCallback<vlc_ml_media_t>( m_vlc_ml, media, VLC_ML_EVENT_MEDIA_ADDED );
140 void MediaLibrary::onMediaModified( std::set<int64_t> mediaIds )
142 wrapEntityModifiedEventCallback( m_vlc_ml, mediaIds, VLC_ML_EVENT_MEDIA_UPDATED );
145 void MediaLibrary::onMediaDeleted( std::set<int64_t> mediaIds )
147 wrapEntityDeletedEventCallback( m_vlc_ml, mediaIds, VLC_ML_EVENT_MEDIA_DELETED );
150 void MediaLibrary::onArtistsAdded( std::vector<medialibrary::ArtistPtr> artists )
152 wrapEntityCreatedEventCallback<vlc_ml_artist_t>( m_vlc_ml, artists, VLC_ML_EVENT_ARTIST_ADDED );
155 void MediaLibrary::onArtistsModified( std::set<int64_t> artistIds )
157 wrapEntityModifiedEventCallback( m_vlc_ml, artistIds, VLC_ML_EVENT_ARTIST_UPDATED );
160 void MediaLibrary::onArtistsDeleted( std::set<int64_t> artistIds )
162 wrapEntityDeletedEventCallback( m_vlc_ml, artistIds, VLC_ML_EVENT_ARTIST_DELETED );
165 void MediaLibrary::onAlbumsAdded( std::vector<medialibrary::AlbumPtr> albums )
167 wrapEntityCreatedEventCallback<vlc_ml_album_t>( m_vlc_ml, albums, VLC_ML_EVENT_ALBUM_ADDED );
170 void MediaLibrary::onAlbumsModified( std::set<int64_t> albumIds )
172 wrapEntityModifiedEventCallback( m_vlc_ml, albumIds, VLC_ML_EVENT_ALBUM_UPDATED );
175 void MediaLibrary::onAlbumsDeleted( std::set<int64_t> albumIds )
177 wrapEntityDeletedEventCallback( m_vlc_ml, albumIds, VLC_ML_EVENT_ALBUM_DELETED );
180 void MediaLibrary::onPlaylistsAdded( std::vector<medialibrary::PlaylistPtr> playlists )
182 wrapEntityCreatedEventCallback<vlc_ml_playlist_t>( m_vlc_ml, playlists, VLC_ML_EVENT_PLAYLIST_ADDED );
185 void MediaLibrary::onPlaylistsModified( std::set<int64_t> playlistIds )
187 wrapEntityModifiedEventCallback( m_vlc_ml, playlistIds, VLC_ML_EVENT_PLAYLIST_UPDATED );
190 void MediaLibrary::onPlaylistsDeleted( std::set<int64_t> playlistIds )
192 wrapEntityDeletedEventCallback( m_vlc_ml, playlistIds, VLC_ML_EVENT_PLAYLIST_DELETED );
195 void MediaLibrary::onGenresAdded( std::vector<medialibrary::GenrePtr> genres )
197 wrapEntityCreatedEventCallback<vlc_ml_genre_t>( m_vlc_ml, genres, VLC_ML_EVENT_GENRE_ADDED );
200 void MediaLibrary::onGenresModified( std::set<int64_t> genreIds )
202 wrapEntityModifiedEventCallback( m_vlc_ml, genreIds, VLC_ML_EVENT_GENRE_UPDATED );
205 void MediaLibrary::onGenresDeleted( std::set<int64_t> genreIds )
207 wrapEntityDeletedEventCallback( m_vlc_ml, genreIds, VLC_ML_EVENT_GENRE_DELETED );
210 void MediaLibrary::onMediaGroupsAdded( std::vector<medialibrary::MediaGroupPtr> )
214 void MediaLibrary::onMediaGroupsModified( std::set<int64_t> )
218 void MediaLibrary::onMediaGroupsDeleted( std::set<int64_t> )
222 void MediaLibrary::onBookmarksAdded( std::vector<medialibrary::BookmarkPtr> bookmarks )
224 wrapEntityCreatedEventCallback<vlc_ml_bookmark_t>( m_vlc_ml, bookmarks,
225 VLC_ML_EVENT_BOOKMARKS_ADDED );
228 void MediaLibrary::onBookmarksModified( std::set<int64_t> bookmarkIds )
230 wrapEntityModifiedEventCallback( m_vlc_ml, bookmarkIds,
231 VLC_ML_EVENT_BOOKMARKS_UPDATED );
234 void MediaLibrary::onBookmarksDeleted( std::set<int64_t> bookmarkIds )
236 wrapEntityDeletedEventCallback( m_vlc_ml, bookmarkIds,
237 VLC_ML_EVENT_BOOKMARKS_DELETED );
240 void MediaLibrary::onDiscoveryStarted( const std::string& entryPoint )
242 vlc_ml_event_t ev;
243 ev.i_type = VLC_ML_EVENT_DISCOVERY_STARTED;
244 ev.discovery_started.psz_entry_point = entryPoint.c_str();
245 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
248 void MediaLibrary::onDiscoveryProgress( const std::string& entryPoint )
250 vlc_ml_event_t ev;
251 ev.i_type = VLC_ML_EVENT_DISCOVERY_PROGRESS;
252 ev.discovery_progress.psz_entry_point = entryPoint.c_str();
253 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
256 void MediaLibrary::onDiscoveryCompleted( const std::string& entryPoint, bool success )
258 vlc_ml_event_t ev;
259 ev.i_type = VLC_ML_EVENT_DISCOVERY_COMPLETED;
260 ev.discovery_completed.psz_entry_point = entryPoint.c_str();
261 ev.discovery_completed.b_success = success;
262 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
265 void MediaLibrary::onReloadStarted( const std::string& entryPoint )
267 vlc_ml_event_t ev;
268 ev.i_type = VLC_ML_EVENT_RELOAD_STARTED;
269 ev.reload_started.psz_entry_point = entryPoint.c_str();
270 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
273 void MediaLibrary::onReloadCompleted( const std::string& entryPoint, bool success )
275 vlc_ml_event_t ev;
276 ev.i_type = VLC_ML_EVENT_RELOAD_COMPLETED;
277 ev.reload_completed.psz_entry_point = entryPoint.c_str();
278 ev.reload_completed.b_success = success;
279 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
282 void MediaLibrary::onEntryPointAdded( const std::string& entryPoint, bool success )
284 vlc_ml_event_t ev;
285 ev.i_type = VLC_ML_EVENT_ENTRY_POINT_ADDED;
286 ev.entry_point_added.psz_entry_point = entryPoint.c_str();
287 ev.entry_point_added.b_success = success;
288 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
291 void MediaLibrary::onEntryPointRemoved( const std::string& entryPoint, bool success )
293 vlc_ml_event_t ev;
294 ev.i_type = VLC_ML_EVENT_ENTRY_POINT_REMOVED;
295 ev.entry_point_removed.psz_entry_point = entryPoint.c_str();
296 ev.entry_point_removed.b_success = success;
297 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
300 void MediaLibrary::onEntryPointBanned( const std::string& entryPoint, bool success )
302 vlc_ml_event_t ev;
303 ev.i_type = VLC_ML_EVENT_ENTRY_POINT_BANNED;
304 ev.entry_point_banned.psz_entry_point = entryPoint.c_str();
305 ev.entry_point_banned.b_success = success;
306 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
309 void MediaLibrary::onEntryPointUnbanned( const std::string& entryPoint, bool success )
311 vlc_ml_event_t ev;
312 ev.i_type = VLC_ML_EVENT_ENTRY_POINT_UNBANNED;
313 ev.entry_point_unbanned.psz_entry_point = entryPoint.c_str();
314 ev.entry_point_unbanned.b_success = success;
315 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
318 void MediaLibrary::onParsingStatsUpdated( uint32_t progress )
320 vlc_ml_event_t ev;
321 ev.i_type = VLC_ML_EVENT_PARSING_PROGRESS_UPDATED;
322 ev.parsing_progress.i_percent = progress;
323 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
326 void MediaLibrary::onBackgroundTasksIdleChanged( bool idle )
328 vlc_ml_event_t ev;
329 ev.i_type = VLC_ML_EVENT_BACKGROUND_IDLE_CHANGED;
330 ev.background_idle_changed.b_idle = idle;
331 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
334 void MediaLibrary::onMediaThumbnailReady( medialibrary::MediaPtr media,
335 medialibrary::ThumbnailSizeType sizeType,
336 bool success )
338 vlc_ml_event_t ev;
339 ev.i_type = VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED;
340 ev.media_thumbnail_generated.b_success = success;
341 ev.media_thumbnail_generated.i_size = static_cast<vlc_ml_thumbnail_size_t>( sizeType );
342 auto mPtr = vlc::wrap_cptr<vlc_ml_media_t>(
343 static_cast<vlc_ml_media_t*>( calloc( 1, sizeof( vlc_ml_media_t ) ) ),
344 vlc_ml_media_release );
345 if ( unlikely( mPtr == nullptr ) )
346 return;
347 ev.media_thumbnail_generated.p_media = mPtr.get();
348 if ( Convert( media.get(), *mPtr ) == false )
349 return;
350 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
353 void MediaLibrary::onHistoryChanged( medialibrary::HistoryType historyType )
355 vlc_ml_event_t ev;
356 ev.i_type = VLC_ML_EVENT_HISTORY_CHANGED;
357 switch ( historyType )
359 case medialibrary::HistoryType::Media:
360 ev.history_changed.history_type = VLC_ML_HISTORY_TYPE_MEDIA;
361 break;
362 case medialibrary::HistoryType::Network:
363 ev.history_changed.history_type = VLC_ML_HISTORY_TYPE_NETWORK;
364 break;
365 default:
366 vlc_assert_unreachable();
368 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
371 void MediaLibrary::onRescanStarted()
373 vlc_ml_event_t ev;
374 ev.i_type = VLC_ML_EVENT_RESCAN_STARTED;
375 m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
378 MediaLibrary::MediaLibrary( vlc_medialibrary_module_t* ml )
379 : m_vlc_ml( ml )
381 m_ml.reset( NewMediaLibrary() );
383 m_logger.reset( new Logger( VLC_OBJECT( m_vlc_ml ) ) );
384 m_ml->setVerbosity( var_InheritBool( VLC_OBJECT( m_vlc_ml ), "ml-verbose" ) ?
385 medialibrary::LogLevel::Debug : medialibrary::LogLevel::Warning );
386 m_ml->setLogger( m_logger.get() );
389 bool MediaLibrary::Init()
391 vlc::threads::mutex_locker lock( m_mutex );
392 if( m_initialized )
393 return true;
395 auto userDir = vlc::wrap_cptr( config_GetUserDir( VLC_USERDATA_DIR ) );
396 std::string mlDir = std::string{ userDir.get() } + "/ml/";
398 m_ml->registerDeviceLister( std::make_shared<vlc::medialibrary::DeviceLister>(
399 VLC_OBJECT(m_vlc_ml) ), "smb://" );
400 m_ml->addFileSystemFactory( std::make_shared<vlc::medialibrary::SDFileSystemFactory>(
401 VLC_OBJECT( m_vlc_ml ), m_ml.get(), "file://") );
402 m_ml->addFileSystemFactory( std::make_shared<vlc::medialibrary::SDFileSystemFactory>(
403 VLC_OBJECT( m_vlc_ml ), m_ml.get(), "smb://") );
404 auto initStatus = m_ml->initialize( mlDir + "ml.db", mlDir + "/mlstorage/", this );
405 switch ( initStatus )
407 case medialibrary::InitializeResult::AlreadyInitialized:
408 assert(!"initialize() should not have been called if already initialized");
409 return true;
410 case medialibrary::InitializeResult::Failed:
411 msg_Err( m_vlc_ml, "Medialibrary failed to initialize" );
412 return false;
413 case medialibrary::InitializeResult::DbReset:
414 msg_Info( m_vlc_ml, "Database was reset" );
415 break;
416 case medialibrary::InitializeResult::Success:
417 msg_Dbg( m_vlc_ml, "MediaLibrary successfully initialized" );
418 break;
419 case medialibrary::InitializeResult::DbCorrupted:
421 auto res = vlc_dialog_wait_question(VLC_OBJECT( m_vlc_ml ),
422 VLC_DIALOG_QUESTION_NORMAL, _( "Ignore" ), _( "Recover" ),
423 _( "Recreate" ), _( "Media database corrupted" ),
424 "Your media database appears to be corrupted. You can try to "
425 "recover it, recreate it entirely, or ignore this error (the "
426 "mediacenter will be disabled)." );
427 switch ( res )
429 case 1:
430 m_ml->clearDatabase( true );
431 break;
432 case 2:
433 m_ml->clearDatabase( false );
434 break;
435 default:
436 return false;
438 break;
442 m_ml->addParserService( std::make_shared<MetadataExtractor>( VLC_OBJECT( m_vlc_ml ) ) );
445 m_ml->addThumbnailer( std::make_shared<Thumbnailer>( m_vlc_ml ) );
447 catch ( const std::runtime_error& ex )
449 msg_Err( m_vlc_ml, "Failed to provide a thumbnailer module to the "
450 "medialib: %s", ex.what() );
451 return false;
454 m_ml->setDiscoverNetworkEnabled( true );
456 m_initialized = true;
457 return true;
460 bool MediaLibrary::Start()
462 if ( Init() == false )
463 return false;
465 if ( m_started.test_and_set() )
466 /* The code below should be executed at most once */
467 return true;
470 * If we already provided the medialib with some entry points, then we have
471 * nothing left to do
473 auto entryPoints = m_ml->entryPoints()->all();
474 if ( entryPoints.empty() == false )
475 return true;
477 auto folders = vlc::wrap_cptr( var_InheritString( m_vlc_ml, "ml-folders" ) );
478 if ( folders != nullptr && strlen( folders.get() ) > 0 )
480 std::istringstream ss( folders.get() );
481 std::string folder;
482 while ( std::getline( ss, folder, ';' ) )
483 m_ml->discover( folder );
485 else
487 std::string varValue;
488 for( auto&& target : { VLC_VIDEOS_DIR, VLC_MUSIC_DIR } )
490 auto folder = vlc::wrap_cptr( config_GetUserDir( target ) );
491 if( folder == nullptr )
492 continue;
493 auto folderMrl = vlc::wrap_cptr( vlc_path2uri( folder.get(), nullptr ) );
494 m_ml->discover( folderMrl.get() );
495 varValue += std::string{ ";" } + folderMrl.get();
497 if ( varValue.empty() == false )
498 config_PutPsz( "ml-folders", varValue.c_str()+1 ); /* skip initial ';' */
500 return true;
503 int MediaLibrary::Control( int query, va_list args )
505 switch ( query )
507 case VLC_ML_ADD_FOLDER:
508 case VLC_ML_REMOVE_FOLDER:
509 case VLC_ML_BAN_FOLDER:
510 case VLC_ML_UNBAN_FOLDER:
511 case VLC_ML_RELOAD_FOLDER:
512 case VLC_ML_RESUME_BACKGROUND:
513 case VLC_ML_MEDIA_GENERATE_THUMBNAIL:
515 /* These operations require the media library to be started
516 * ie. that the background threads are started */
517 if ( Start() == false )
518 return VLC_EGENERIC;
519 break;
521 default:
522 break;
525 switch ( query )
527 case VLC_ML_ADD_FOLDER:
528 case VLC_ML_REMOVE_FOLDER:
529 case VLC_ML_BAN_FOLDER:
530 case VLC_ML_UNBAN_FOLDER:
532 const char* mrl = va_arg( args, const char* );
533 switch( query )
535 case VLC_ML_ADD_FOLDER:
536 m_ml->discover( mrl );
537 break;
538 case VLC_ML_REMOVE_FOLDER:
539 m_ml->removeEntryPoint( mrl );
540 break;
541 case VLC_ML_BAN_FOLDER:
542 m_ml->banFolder( mrl );
543 break;
544 case VLC_ML_UNBAN_FOLDER:
545 m_ml->unbanFolder( mrl );
546 break;
548 break;
550 case VLC_ML_LIST_FOLDERS:
552 auto entryPoints = m_ml->entryPoints()->all();
553 auto res = ml_convert_list<vlc_ml_entry_point_list_t,
554 vlc_ml_entry_point_t>( entryPoints );
555 *(va_arg( args, vlc_ml_entry_point_list_t**) ) = res;
556 break;
558 case VLC_ML_IS_INDEXED:
560 auto mrl = va_arg( args, const char* );
561 auto res = va_arg( args, bool* );
562 *res = m_ml->isIndexed( mrl );
563 break;
565 case VLC_ML_RELOAD_FOLDER:
567 auto mrl = va_arg( args, const char* );
568 if ( mrl == nullptr )
569 m_ml->reload();
570 else
571 m_ml->reload( mrl );
572 break;
574 case VLC_ML_PAUSE_BACKGROUND:
575 m_ml->pauseBackgroundOperations();
576 break;
577 case VLC_ML_RESUME_BACKGROUND:
578 m_ml->resumeBackgroundOperations();
579 break;
580 case VLC_ML_CLEAR_HISTORY:
581 m_ml->clearHistory();
582 break;
583 case VLC_ML_NEW_EXTERNAL_MEDIA:
585 auto mrl = va_arg( args, const char* );
586 auto media = m_ml->addExternalMedia( mrl, -1 );
587 if ( media == nullptr )
588 return VLC_EGENERIC;
589 *va_arg( args, vlc_ml_media_t**) = CreateAndConvert<vlc_ml_media_t>( media.get() );
590 return VLC_SUCCESS;
592 case VLC_ML_NEW_STREAM:
594 auto mrl = va_arg( args, const char* );
595 auto media = m_ml->addStream( mrl );
596 if ( media == nullptr )
597 return VLC_EGENERIC;
598 *va_arg( args, vlc_ml_media_t**) = CreateAndConvert<vlc_ml_media_t>( media.get() );
599 return VLC_SUCCESS;
601 case VLC_ML_MEDIA_GENERATE_THUMBNAIL:
603 auto mediaId = va_arg( args, int64_t );
604 auto sizeType = va_arg( args, int );
605 auto width = va_arg( args, uint32_t );
606 auto height = va_arg( args, uint32_t );
607 auto position = va_arg( args, double );
608 auto res = m_ml->requestThumbnail( mediaId,
609 static_cast<medialibrary::ThumbnailSizeType>( sizeType ),
610 width, height, position );
611 return res == true ? VLC_SUCCESS : VLC_EGENERIC;
613 case VLC_ML_MEDIA_UPDATE_PROGRESS:
614 case VLC_ML_MEDIA_GET_MEDIA_PLAYBACK_STATE:
615 case VLC_ML_MEDIA_SET_MEDIA_PLAYBACK_STATE:
616 case VLC_ML_MEDIA_GET_ALL_MEDIA_PLAYBACK_STATES:
617 case VLC_ML_MEDIA_SET_ALL_MEDIA_PLAYBACK_STATES:
618 case VLC_ML_MEDIA_SET_THUMBNAIL:
619 case VLC_ML_MEDIA_ADD_EXTERNAL_MRL:
620 case VLC_ML_MEDIA_SET_TYPE:
621 case VLC_ML_MEDIA_ADD_BOOKMARK:
622 case VLC_ML_MEDIA_REMOVE_BOOKMARK:
623 case VLC_ML_MEDIA_REMOVE_ALL_BOOKMARKS:
624 case VLC_ML_MEDIA_UPDATE_BOOKMARK:
625 return controlMedia( query, args );
626 case VLC_ML_PLAYLIST_CREATE:
628 auto name = va_arg( args, const char * );
629 auto playlist = m_ml->createPlaylist( name );
630 if ( playlist == nullptr )
631 return VLC_EGENERIC;
632 auto result = va_arg( args, vlc_ml_playlist_t** );
633 *result = CreateAndConvert<vlc_ml_playlist_t>( playlist.get() );
634 return VLC_SUCCESS;
636 case VLC_ML_PLAYLIST_DELETE:
638 if ( m_ml->deletePlaylist( va_arg( args, int64_t ) ) == false )
639 return VLC_EGENERIC;
640 return VLC_SUCCESS;
642 case VLC_ML_PLAYLIST_APPEND:
644 auto playlist = m_ml->playlist( va_arg( args, int64_t ) );
645 if ( playlist == nullptr )
646 return VLC_EGENERIC;
647 if ( playlist->append(va_arg( args, int64_t )) == false )
648 return VLC_EGENERIC;
649 return VLC_SUCCESS;
651 default:
652 return VLC_EGENERIC;
654 return VLC_SUCCESS;
657 int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_list args )
659 if ( Init() == false )
660 return VLC_EGENERIC;
662 medialibrary::QueryParameters p{};
663 medialibrary::QueryParameters* paramsPtr = nullptr;
664 uint32_t nbItems = 0;
665 uint32_t offset = 0;
666 const char* psz_pattern = nullptr;
667 if ( params )
669 p.desc = params->b_desc;
670 p.sort = sortingCriteria( params->i_sort );
671 nbItems = params->i_nbResults;
672 offset = params->i_offset;
673 psz_pattern = params->psz_pattern;
674 paramsPtr = &p;
676 switch ( listQuery )
678 case VLC_ML_LIST_MEDIA_OF:
679 case VLC_ML_COUNT_MEDIA_OF:
680 case VLC_ML_LIST_ARTISTS_OF:
681 case VLC_ML_COUNT_ARTISTS_OF:
682 case VLC_ML_LIST_ALBUMS_OF:
683 case VLC_ML_COUNT_ALBUMS_OF:
685 auto parentType = va_arg( args, int );
686 listQuery = filterListChildrenQuery( listQuery, parentType );
688 default:
689 break;
691 switch( listQuery )
693 case VLC_ML_LIST_ALBUM_TRACKS:
694 case VLC_ML_COUNT_ALBUM_TRACKS:
695 case VLC_ML_LIST_ALBUM_ARTISTS:
696 case VLC_ML_COUNT_ALBUM_ARTISTS:
697 return listAlbums( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
699 case VLC_ML_LIST_ARTIST_ALBUMS:
700 case VLC_ML_COUNT_ARTIST_ALBUMS:
701 case VLC_ML_LIST_ARTIST_TRACKS:
702 case VLC_ML_COUNT_ARTIST_TRACKS:
703 return listArtists( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
705 case VLC_ML_LIST_VIDEOS:
707 medialibrary::Query<medialibrary::IMedia> query;
708 if ( psz_pattern != nullptr )
709 query = m_ml->searchVideo( psz_pattern, paramsPtr );
710 else
711 query = m_ml->videoFiles( paramsPtr );
712 if ( query == nullptr )
713 return VLC_EGENERIC;
714 auto res = ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
715 query->items( nbItems, offset ) );
716 *va_arg( args, vlc_ml_media_list_t**) = res;
717 break;
719 case VLC_ML_COUNT_VIDEOS:
721 medialibrary::Query<medialibrary::IMedia> query;
722 if ( psz_pattern != nullptr )
723 query = m_ml->searchVideo( psz_pattern, paramsPtr );
724 else
725 query = m_ml->videoFiles( paramsPtr );
726 if ( query == nullptr )
727 return VLC_EGENERIC;
728 *va_arg( args, size_t* ) = query->count();
729 break;
731 case VLC_ML_LIST_AUDIOS:
733 medialibrary::Query<medialibrary::IMedia> query;
734 if ( psz_pattern != nullptr )
735 query = m_ml->searchAudio( psz_pattern, paramsPtr );
736 else
737 query = m_ml->audioFiles( paramsPtr );
738 if ( query == nullptr )
739 return VLC_EGENERIC;
740 auto res = ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
741 query->items( nbItems, offset ) );
742 *va_arg( args, vlc_ml_media_list_t**) = res;
743 break;
745 case VLC_ML_COUNT_AUDIOS:
747 medialibrary::Query<medialibrary::IMedia> query;
748 if ( psz_pattern != nullptr )
749 query = m_ml->searchAudio( psz_pattern, paramsPtr );
750 else
751 query = m_ml->audioFiles( paramsPtr );
752 if ( query == nullptr )
753 return VLC_EGENERIC;
754 *va_arg( args, size_t* ) = query->count();
755 break;
757 case VLC_ML_LIST_ALBUMS:
759 medialibrary::Query<medialibrary::IAlbum> query;
760 if ( psz_pattern != nullptr )
761 query = m_ml->searchAlbums( psz_pattern, paramsPtr );
762 else
763 query = m_ml->albums( paramsPtr );
764 if ( query == nullptr )
765 return VLC_EGENERIC;
766 auto res = ml_convert_list<vlc_ml_album_list_t, vlc_ml_album_t>(
767 query->items( nbItems, offset ) );
768 *va_arg( args, vlc_ml_album_list_t**) = res;
769 break;
771 case VLC_ML_COUNT_ALBUMS:
773 medialibrary::Query<medialibrary::IAlbum> query;
774 if ( psz_pattern != nullptr )
775 query = m_ml->searchAlbums( psz_pattern, paramsPtr );
776 else
777 query = m_ml->albums( paramsPtr );
778 if ( query == nullptr )
779 return VLC_EGENERIC;
780 *va_arg( args, size_t* ) = query->count();
781 break;
783 case VLC_ML_LIST_GENRES:
785 medialibrary::Query<medialibrary::IGenre> query;
786 if ( psz_pattern != nullptr )
787 query = m_ml->searchGenre( psz_pattern, paramsPtr );
788 else
789 query = m_ml->genres( paramsPtr );
790 if ( query == nullptr )
791 return VLC_EGENERIC;
792 auto res = ml_convert_list<vlc_ml_genre_list_t,vlc_ml_genre_t>(
793 query->items( nbItems, offset ) );
794 *va_arg( args, vlc_ml_genre_list_t**) = res;
795 break;
797 case VLC_ML_COUNT_GENRES:
799 medialibrary::Query<medialibrary::IGenre> query;
800 if ( psz_pattern != nullptr )
801 query = m_ml->searchGenre( psz_pattern, paramsPtr );
802 else
803 query = m_ml->genres( paramsPtr );
804 if ( query == nullptr )
805 return VLC_EGENERIC;
806 *va_arg( args, size_t* ) = query->count();
807 break;
809 case VLC_ML_LIST_ARTISTS:
811 medialibrary::Query<medialibrary::IArtist> query;
812 bool includeAll = va_arg( args, int ) != 0;
813 auto artistsIncluded = includeAll ? medialibrary::ArtistIncluded::All :
814 medialibrary::ArtistIncluded::AlbumArtistOnly;
815 if ( psz_pattern != nullptr )
816 query = m_ml->searchArtists( psz_pattern, artistsIncluded, paramsPtr );
817 else
818 query = m_ml->artists( artistsIncluded, paramsPtr );
819 if ( query == nullptr )
820 return VLC_EGENERIC;
821 auto res = ml_convert_list<vlc_ml_artist_list_t, vlc_ml_artist_t>(
822 query->items( nbItems, offset ) );
823 *va_arg( args, vlc_ml_artist_list_t**) = res;
824 break;
826 case VLC_ML_COUNT_ARTISTS:
828 medialibrary::Query<medialibrary::IArtist> query;
829 bool includeAll = va_arg( args, int ) != 0;
830 auto artistsIncluded = includeAll ? medialibrary::ArtistIncluded::All :
831 medialibrary::ArtistIncluded::AlbumArtistOnly;
832 if ( psz_pattern != nullptr )
833 query = m_ml->searchArtists( psz_pattern, artistsIncluded, paramsPtr );
834 else
835 query = m_ml->artists( artistsIncluded, paramsPtr );
836 if ( query == nullptr )
837 return VLC_EGENERIC;
838 *va_arg( args, size_t* ) = query->count();
839 break;
841 case VLC_ML_LIST_GENRE_ARTISTS:
842 case VLC_ML_COUNT_GENRE_ARTISTS:
843 case VLC_ML_LIST_GENRE_TRACKS:
844 case VLC_ML_COUNT_GENRE_TRACKS:
845 case VLC_ML_LIST_GENRE_ALBUMS:
846 case VLC_ML_COUNT_GENRE_ALBUMS:
847 return listGenre( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
849 case VLC_ML_LIST_MEDIA_LABELS:
850 case VLC_ML_COUNT_MEDIA_LABELS:
851 case VLC_ML_LIST_MEDIA_BOOKMARKS:
852 return listMedia( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
854 case VLC_ML_LIST_SHOWS:
856 medialibrary::Query<medialibrary::IShow> query;
857 if ( psz_pattern != nullptr )
858 query = m_ml->searchShows( psz_pattern, paramsPtr );
859 else
860 query = m_ml->shows( paramsPtr );
861 if ( query == nullptr )
862 return VLC_EGENERIC;
863 *va_arg( args, vlc_ml_show_list_t** ) =
864 ml_convert_list<vlc_ml_show_list_t, vlc_ml_show_t>(
865 query->items( nbItems, offset ) );
866 return VLC_SUCCESS;
868 case VLC_ML_COUNT_SHOWS:
870 auto query = m_ml->shows( paramsPtr );
871 if ( query == nullptr )
872 return VLC_EGENERIC;
873 *va_arg( args, int64_t* ) = query->count();
874 return VLC_SUCCESS;
876 case VLC_ML_LIST_SHOW_EPISODES:
877 case VLC_ML_COUNT_SHOW_EPISODES:
879 auto show = m_ml->show( va_arg( args, int64_t ) );
880 if ( show == nullptr )
881 return VLC_EGENERIC;
882 medialibrary::Query<medialibrary::IMedia> query;
883 if ( psz_pattern != nullptr )
884 query = show->searchEpisodes( psz_pattern, paramsPtr );
885 else
886 query = show->episodes( paramsPtr );
887 if ( query == nullptr )
888 return VLC_EGENERIC;
889 switch ( listQuery )
891 case VLC_ML_LIST_SHOW_EPISODES:
892 *va_arg( args, vlc_ml_media_list_t**) =
893 ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
894 query->items( nbItems, offset ) );
895 return VLC_SUCCESS;
896 case VLC_ML_COUNT_SHOW_EPISODES:
897 *va_arg( args, int64_t* ) = query->count();
898 return VLC_SUCCESS;
899 default:
900 vlc_assert_unreachable();
903 case VLC_ML_LIST_PLAYLIST_MEDIA:
904 case VLC_ML_COUNT_PLAYLIST_MEDIA:
905 case VLC_ML_LIST_PLAYLISTS:
906 case VLC_ML_COUNT_PLAYLISTS:
907 return listPlaylist( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
908 case VLC_ML_COUNT_HISTORY:
909 case VLC_ML_LIST_HISTORY:
910 case VLC_ML_COUNT_HISTORY_BY_TYPE:
911 case VLC_ML_LIST_HISTORY_BY_TYPE:
912 case VLC_ML_COUNT_STREAM_HISTORY:
913 case VLC_ML_LIST_STREAM_HISTORY:
915 medialibrary::Query<medialibrary::IMedia> query;
917 switch ( listQuery )
919 case VLC_ML_COUNT_HISTORY:
920 case VLC_ML_LIST_HISTORY:
921 query = m_ml->history();
922 break;
923 case VLC_ML_COUNT_HISTORY_BY_TYPE:
924 case VLC_ML_LIST_HISTORY_BY_TYPE:
926 auto type = va_arg(args, int);
927 query = m_ml->history(static_cast<medialibrary::IMedia::Type>( type ));
928 break;
930 case VLC_ML_COUNT_STREAM_HISTORY:
931 case VLC_ML_LIST_STREAM_HISTORY:
932 query = m_ml->streamHistory();
933 break;
934 default:
935 vlc_assert_unreachable();
938 if ( query == nullptr )
939 return VLC_EGENERIC;
941 switch ( listQuery )
943 case VLC_ML_LIST_HISTORY:
944 case VLC_ML_LIST_HISTORY_BY_TYPE:
945 case VLC_ML_LIST_STREAM_HISTORY:
946 *va_arg( args, vlc_ml_media_list_t**) =
947 ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
948 query->items( nbItems, offset ) );
949 return VLC_SUCCESS;
950 case VLC_ML_COUNT_HISTORY:
951 case VLC_ML_COUNT_HISTORY_BY_TYPE:
952 case VLC_ML_COUNT_STREAM_HISTORY:
953 *va_arg( args, size_t* ) = query->count();
954 return VLC_SUCCESS;
955 default:
956 vlc_assert_unreachable();
961 return VLC_SUCCESS;
964 void* MediaLibrary::Get( int query, va_list args )
966 if ( Init() == false )
967 return nullptr;
969 switch ( query )
971 case VLC_ML_GET_MEDIA:
973 auto id = va_arg( args, int64_t );
974 auto media = m_ml->media( id );
975 return CreateAndConvert<vlc_ml_media_t>( media.get() );
977 case VLC_ML_GET_INPUT_ITEM:
979 auto id = va_arg( args, int64_t );
980 auto media = m_ml->media( id );
981 return MediaToInputItem( media.get() );
983 case VLC_ML_GET_ALBUM:
985 auto id = va_arg( args, int64_t );
986 auto album = m_ml->album( id );
987 return CreateAndConvert<vlc_ml_album_t>( album.get() );
989 case VLC_ML_GET_ARTIST:
991 auto id = va_arg( args, int64_t );
992 auto artist = m_ml->artist( id );
993 return CreateAndConvert<vlc_ml_artist_t>( artist.get() );
995 case VLC_ML_GET_GENRE:
997 auto id = va_arg( args, int64_t );
998 auto genre = m_ml->genre( id );
999 return CreateAndConvert<vlc_ml_genre_t>( genre.get() );
1001 case VLC_ML_GET_SHOW:
1003 auto id = va_arg( args, int64_t );
1004 auto show = m_ml->show( id );
1005 return CreateAndConvert<vlc_ml_show_t>( show.get() );
1007 case VLC_ML_GET_PLAYLIST:
1009 auto id = va_arg( args, int64_t );
1010 auto playlist = m_ml->playlist( id );
1011 return CreateAndConvert<vlc_ml_playlist_t>( playlist.get() );
1013 case VLC_ML_GET_MEDIA_BY_MRL:
1015 auto mrl = va_arg( args, const char* );
1016 auto media = m_ml->media( mrl );
1017 return CreateAndConvert<vlc_ml_media_t>( media.get() );
1019 case VLC_ML_GET_INPUT_ITEM_BY_MRL:
1021 auto mrl = va_arg( args, const char* );
1022 auto media = m_ml->media( mrl );
1023 if ( media == nullptr )
1024 return nullptr;
1025 return MediaToInputItem( media.get() );
1027 default:
1028 vlc_assert_unreachable();
1031 return nullptr;
1034 medialibrary::IMedia::MetadataType MediaLibrary::metadataType( int meta )
1036 switch ( meta )
1038 case VLC_ML_PLAYBACK_STATE_RATING:
1039 return medialibrary::IMedia::MetadataType::Rating;
1040 case VLC_ML_PLAYBACK_STATE_SPEED:
1041 return medialibrary::IMedia::MetadataType::Speed;
1042 case VLC_ML_PLAYBACK_STATE_TITLE:
1043 return medialibrary::IMedia::MetadataType::Title;
1044 case VLC_ML_PLAYBACK_STATE_CHAPTER:
1045 return medialibrary::IMedia::MetadataType::Chapter;
1046 case VLC_ML_PLAYBACK_STATE_PROGRAM:
1047 return medialibrary::IMedia::MetadataType::Program;
1048 case VLC_ML_PLAYBACK_STATE_SEEN:
1049 return medialibrary::IMedia::MetadataType::Seen;
1050 case VLC_ML_PLAYBACK_STATE_VIDEO_TRACK:
1051 return medialibrary::IMedia::MetadataType::VideoTrack;
1052 case VLC_ML_PLAYBACK_STATE_ASPECT_RATIO:
1053 return medialibrary::IMedia::MetadataType::AspectRatio;
1054 case VLC_ML_PLAYBACK_STATE_ZOOM:
1055 return medialibrary::IMedia::MetadataType::Zoom;
1056 case VLC_ML_PLAYBACK_STATE_CROP:
1057 return medialibrary::IMedia::MetadataType::Crop;
1058 case VLC_ML_PLAYBACK_STATE_DEINTERLACE:
1059 return medialibrary::IMedia::MetadataType::Deinterlace;
1060 case VLC_ML_PLAYBACK_STATE_VIDEO_FILTER:
1061 return medialibrary::IMedia::MetadataType::VideoFilter;
1062 case VLC_ML_PLAYBACK_STATE_AUDIO_TRACK:
1063 return medialibrary::IMedia::MetadataType::AudioTrack;
1064 case VLC_ML_PLAYBACK_STATE_GAIN:
1065 return medialibrary::IMedia::MetadataType::Gain;
1066 case VLC_ML_PLAYBACK_STATE_AUDIO_DELAY:
1067 return medialibrary::IMedia::MetadataType::AudioDelay;
1068 case VLC_ML_PLAYBACK_STATE_SUBTITLE_TRACK:
1069 return medialibrary::IMedia::MetadataType::SubtitleTrack;
1070 case VLC_ML_PLAYBACK_STATE_SUBTITLE_DELAY:
1071 return medialibrary::IMedia::MetadataType::SubtitleDelay;
1072 case VLC_ML_PLAYBACK_STATE_APP_SPECIFIC:
1073 return medialibrary::IMedia::MetadataType::ApplicationSpecific;
1074 default:
1075 vlc_assert_unreachable();
1079 medialibrary::SortingCriteria MediaLibrary::sortingCriteria(int sort)
1081 switch ( sort )
1083 case VLC_ML_SORTING_DEFAULT:
1084 return medialibrary::SortingCriteria::Default;
1085 case VLC_ML_SORTING_ALPHA:
1086 return medialibrary::SortingCriteria::Alpha;
1087 case VLC_ML_SORTING_DURATION:
1088 return medialibrary::SortingCriteria::Duration;
1089 case VLC_ML_SORTING_INSERTIONDATE:
1090 return medialibrary::SortingCriteria::InsertionDate;
1091 case VLC_ML_SORTING_LASTMODIFICATIONDATE:
1092 return medialibrary::SortingCriteria::LastModificationDate;
1093 case VLC_ML_SORTING_RELEASEDATE:
1094 return medialibrary::SortingCriteria::ReleaseDate;
1095 case VLC_ML_SORTING_FILESIZE:
1096 return medialibrary::SortingCriteria::FileSize;
1097 case VLC_ML_SORTING_ARTIST:
1098 return medialibrary::SortingCriteria::Artist;
1099 case VLC_ML_SORTING_PLAYCOUNT:
1100 return medialibrary::SortingCriteria::PlayCount;
1101 case VLC_ML_SORTING_ALBUM:
1102 return medialibrary::SortingCriteria::Album;
1103 case VLC_ML_SORTING_FILENAME:
1104 return medialibrary::SortingCriteria::Filename;
1105 case VLC_ML_SORTING_TRACKNUMBER:
1106 return medialibrary::SortingCriteria::TrackNumber;
1107 default:
1108 vlc_assert_unreachable();
1112 int MediaLibrary::getMeta( const medialibrary::IMedia& media, int meta, char** result )
1114 auto& md = media.metadata( metadataType( meta ) );
1115 if ( md.isSet() == false )
1117 *result = nullptr;
1118 return VLC_SUCCESS;
1120 *result = strdup( md.asStr().c_str() );
1121 if ( *result == nullptr )
1122 return VLC_ENOMEM;
1123 return VLC_SUCCESS;
1126 int MediaLibrary::getMeta( const medialibrary::IMedia& media,
1127 vlc_ml_playback_states_all* res )
1129 auto metas = media.metadata();
1130 res->rate = .0f;
1131 res->zoom = -1.f;
1132 res->current_title = -1;
1133 // For tracks, -1 means disabled, so we can't use it for "unset"
1134 res->current_video_track = res->current_audio_track =
1135 res->current_subtitle_track = nullptr;
1136 res->aspect_ratio = res->crop = res->deinterlace =
1137 res->video_filter = nullptr;
1138 for ( const auto& meta : metas )
1140 #define COPY_META( field ) res->field = strdup( meta.second.c_str() ); \
1141 if ( res->field == nullptr ) return VLC_ENOMEM;
1143 switch ( meta.first )
1145 case medialibrary::IMedia::MetadataType::Speed:
1146 res->rate = atof( meta.second.c_str() );
1147 break;
1148 case medialibrary::IMedia::MetadataType::Title:
1149 res->current_title = atoi( meta.second.c_str() );
1150 break;
1151 case medialibrary::IMedia::MetadataType::VideoTrack:
1152 COPY_META( current_video_track );
1153 break;
1154 case medialibrary::IMedia::MetadataType::AspectRatio:
1155 COPY_META( aspect_ratio );
1156 break;
1157 case medialibrary::IMedia::MetadataType::Zoom:
1158 res->zoom = atof( meta.second.c_str() );
1159 break;
1160 case medialibrary::IMedia::MetadataType::Crop:
1161 COPY_META( crop );
1162 break;
1163 case medialibrary::IMedia::MetadataType::Deinterlace:
1164 COPY_META( deinterlace );
1165 break;
1166 case medialibrary::IMedia::MetadataType::VideoFilter:
1167 COPY_META( video_filter );
1168 break;
1169 case medialibrary::IMedia::MetadataType::AudioTrack:
1170 COPY_META( current_audio_track );
1171 break;
1172 case medialibrary::IMedia::MetadataType::SubtitleTrack:
1173 COPY_META( current_subtitle_track );
1174 break;
1175 default:
1176 break;
1178 #undef COPY_META
1180 return VLC_SUCCESS;
1183 int MediaLibrary::setMeta( medialibrary::IMedia& media, int meta, const char* value )
1185 bool res;
1186 if ( value == nullptr )
1187 res = media.unsetMetadata( metadataType( meta ) );
1188 else
1189 res = media.setMetadata( metadataType( meta ), value );
1190 if ( res == false )
1191 return VLC_EGENERIC;
1192 return VLC_SUCCESS;
1195 int MediaLibrary::setMeta( medialibrary::IMedia& media,
1196 const vlc_ml_playback_states_all* values )
1198 using MT = medialibrary::IMedia::MetadataType;
1199 std::unordered_map<MT, std::string> metas;
1200 if ( values->rate != .0f )
1201 metas[MT::Speed] = std::to_string( values->rate );
1202 if ( values->zoom != .0f )
1203 metas[MT::Zoom] = std::to_string( values->zoom );
1204 if ( values->current_title >= 0 )
1205 metas[MT::Title] = std::to_string( values->current_title );
1206 if ( values->aspect_ratio != nullptr )
1207 metas[MT::AspectRatio] = values->aspect_ratio;
1208 if ( values->crop != nullptr )
1209 metas[MT::Crop] = values->crop;
1210 if ( values->deinterlace != nullptr )
1211 metas[MT::Deinterlace] = values->deinterlace;
1212 if ( values->video_filter != nullptr )
1213 metas[MT::VideoFilter] = values->video_filter;
1214 if ( values->current_video_track != nullptr )
1215 metas[MT::VideoTrack] = values->current_video_track;
1216 if ( values->current_audio_track != nullptr )
1217 metas[MT::AudioTrack] = values->current_audio_track;
1218 if ( values->current_subtitle_track != nullptr )
1219 metas[MT::SubtitleTrack] = values->current_subtitle_track;
1221 if ( media.setMetadata( std::move( metas ) ) == false )
1222 return VLC_EGENERIC;
1223 return VLC_SUCCESS;
1226 int MediaLibrary::controlMedia( int query, va_list args )
1228 auto mediaId = va_arg( args, int64_t );
1229 auto m = m_ml->media( mediaId );
1230 if ( m == nullptr )
1231 return VLC_EGENERIC;
1232 switch( query )
1234 case VLC_ML_MEDIA_UPDATE_PROGRESS:
1235 if ( m->setProgress( va_arg( args, double ) ) == false )
1236 return VLC_EGENERIC;
1237 return VLC_SUCCESS;
1238 case VLC_ML_MEDIA_GET_MEDIA_PLAYBACK_STATE:
1240 auto meta = va_arg( args, int );
1241 auto res = va_arg( args, char** );
1242 return getMeta( *m, meta, res );
1244 case VLC_ML_MEDIA_SET_MEDIA_PLAYBACK_STATE:
1246 auto meta = va_arg( args, int );
1247 auto value = va_arg( args, const char* );
1248 return setMeta( *m, meta, value );
1250 case VLC_ML_MEDIA_GET_ALL_MEDIA_PLAYBACK_STATES:
1252 auto res = va_arg( args, vlc_ml_playback_states_all* );
1253 return getMeta( *m, res );
1255 case VLC_ML_MEDIA_SET_ALL_MEDIA_PLAYBACK_STATES:
1257 auto res = va_arg( args, const vlc_ml_playback_states_all* );
1258 return setMeta( *m, res );
1260 case VLC_ML_MEDIA_SET_THUMBNAIL:
1262 auto mrl = va_arg( args, const char* );
1263 auto sizeType = va_arg( args, int );
1264 m->setThumbnail( mrl, static_cast<medialibrary::ThumbnailSizeType>( sizeType ) );
1265 return VLC_SUCCESS;
1267 case VLC_ML_MEDIA_ADD_EXTERNAL_MRL:
1269 auto mrl = va_arg( args, const char* );
1270 auto type = va_arg( args, int );
1271 medialibrary::IFile::Type mlType;
1272 switch ( type )
1274 case VLC_ML_FILE_TYPE_UNKNOWN:
1275 // The type can't be main since this is added to an existing media
1276 // which must already have a file
1277 case VLC_ML_FILE_TYPE_MAIN:
1278 case VLC_ML_FILE_TYPE_PLAYLIST:
1279 return VLC_EGENERIC;
1280 case VLC_ML_FILE_TYPE_PART:
1281 mlType = medialibrary::IFile::Type::Part;
1282 break;
1283 case VLC_ML_FILE_TYPE_SOUNDTRACK:
1284 mlType = medialibrary::IFile::Type::Soundtrack;
1285 break;
1286 case VLC_ML_FILE_TYPE_SUBTITLE:
1287 mlType = medialibrary::IFile::Type::Subtitles;
1288 break;
1289 default:
1290 vlc_assert_unreachable();
1292 if ( m->addExternalMrl( mrl, mlType ) == nullptr )
1293 return VLC_EGENERIC;
1294 return VLC_SUCCESS;
1296 case VLC_ML_MEDIA_SET_TYPE:
1298 auto type = va_arg( args, int );
1299 if ( m->setType( static_cast<medialibrary::IMedia::Type>( type ) ) == false )
1300 return VLC_EGENERIC;
1301 return VLC_SUCCESS;
1303 case VLC_ML_MEDIA_ADD_BOOKMARK:
1305 auto time = va_arg( args, int64_t );
1306 if ( m->addBookmark( time ) == nullptr )
1307 return VLC_EGENERIC;
1308 return VLC_EGENERIC;
1310 case VLC_ML_MEDIA_REMOVE_BOOKMARK:
1312 auto time = va_arg( args, int64_t );
1313 if ( m->removeBookmark( time ) == false )
1314 return VLC_EGENERIC;
1315 return VLC_SUCCESS;
1317 case VLC_ML_MEDIA_REMOVE_ALL_BOOKMARKS:
1319 if ( m->removeAllBookmarks() == false )
1320 return VLC_EGENERIC;
1321 return VLC_SUCCESS;
1323 case VLC_ML_MEDIA_UPDATE_BOOKMARK:
1325 auto time = va_arg( args, int64_t );
1326 auto name = va_arg( args, const char* );
1327 auto desc = va_arg( args, const char* );
1328 auto bookmark = m->bookmark( time );
1329 if ( bookmark == nullptr )
1330 return VLC_EGENERIC;
1331 auto res = false;
1332 if ( name != nullptr && desc != nullptr )
1333 res = bookmark->setNameAndDescription( name, desc );
1334 else if ( name != nullptr )
1335 res = bookmark->setName( name );
1336 else if ( desc != nullptr )
1337 res = bookmark->setDescription( desc );
1338 return res ? VLC_SUCCESS : VLC_EGENERIC;
1340 default:
1341 vlc_assert_unreachable();
1345 int MediaLibrary::filterListChildrenQuery( int query, int parentType )
1347 switch( query )
1349 case VLC_ML_LIST_MEDIA_OF:
1350 switch ( parentType )
1352 case VLC_ML_PARENT_ALBUM:
1353 return VLC_ML_LIST_ALBUM_TRACKS;
1354 case VLC_ML_PARENT_ARTIST:
1355 return VLC_ML_LIST_ARTIST_TRACKS;
1356 case VLC_ML_PARENT_SHOW:
1357 return VLC_ML_LIST_SHOW_EPISODES;
1358 case VLC_ML_PARENT_GENRE:
1359 return VLC_ML_LIST_GENRE_TRACKS;
1360 case VLC_ML_PARENT_PLAYLIST:
1361 return VLC_ML_LIST_PLAYLIST_MEDIA;
1362 default:
1363 vlc_assert_unreachable();
1365 case VLC_ML_COUNT_MEDIA_OF:
1366 switch ( parentType )
1368 case VLC_ML_PARENT_ALBUM:
1369 return VLC_ML_COUNT_ALBUM_TRACKS;
1370 case VLC_ML_PARENT_ARTIST:
1371 return VLC_ML_COUNT_ARTIST_TRACKS;
1372 case VLC_ML_PARENT_SHOW:
1373 return VLC_ML_COUNT_SHOW_EPISODES;
1374 case VLC_ML_PARENT_GENRE:
1375 return VLC_ML_COUNT_GENRE_TRACKS;
1376 case VLC_ML_PARENT_PLAYLIST:
1377 return VLC_ML_COUNT_PLAYLIST_MEDIA;
1378 default:
1379 vlc_assert_unreachable();
1381 case VLC_ML_LIST_ALBUMS_OF:
1382 switch ( parentType )
1384 case VLC_ML_PARENT_ARTIST:
1385 return VLC_ML_LIST_ARTIST_ALBUMS;
1386 case VLC_ML_PARENT_GENRE:
1387 return VLC_ML_LIST_GENRE_ALBUMS;
1388 default:
1389 vlc_assert_unreachable();
1391 case VLC_ML_COUNT_ALBUMS_OF:
1392 switch ( parentType )
1394 case VLC_ML_PARENT_ARTIST:
1395 return VLC_ML_COUNT_ARTIST_ALBUMS;
1396 case VLC_ML_PARENT_GENRE:
1397 return VLC_ML_COUNT_GENRE_ALBUMS;
1398 default:
1399 vlc_assert_unreachable();
1401 case VLC_ML_LIST_ARTISTS_OF:
1402 switch ( parentType )
1404 case VLC_ML_PARENT_ALBUM:
1405 return VLC_ML_LIST_ALBUM_ARTISTS;
1406 case VLC_ML_PARENT_GENRE:
1407 return VLC_ML_LIST_GENRE_ARTISTS;
1408 default:
1409 vlc_assert_unreachable();
1411 case VLC_ML_COUNT_ARTISTS_OF:
1412 switch ( parentType )
1414 case VLC_ML_PARENT_ALBUM:
1415 return VLC_ML_COUNT_ALBUM_ARTISTS;
1416 case VLC_ML_PARENT_GENRE:
1417 return VLC_ML_COUNT_GENRE_ARTISTS;
1418 default:
1419 vlc_assert_unreachable();
1421 default:
1422 vlc_assert_unreachable();
1426 int MediaLibrary::listAlbums( int listQuery, const medialibrary::QueryParameters* paramsPtr,
1427 const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
1429 auto album = m_ml->album( va_arg( args, int64_t ) );
1430 if ( album == nullptr )
1431 return VLC_EGENERIC;
1432 switch ( listQuery )
1434 case VLC_ML_LIST_ALBUM_TRACKS:
1435 case VLC_ML_COUNT_ALBUM_TRACKS:
1437 medialibrary::Query<medialibrary::IMedia> query;
1438 if ( pattern != nullptr )
1439 query = album->searchTracks( pattern, paramsPtr );
1440 else
1441 query = album->tracks( paramsPtr );
1442 if ( query == nullptr )
1443 return VLC_EGENERIC;
1444 switch ( listQuery )
1446 case VLC_ML_LIST_ALBUM_TRACKS:
1447 *va_arg( args, vlc_ml_media_list_t**) =
1448 ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
1449 query->items( nbItems, offset ) );
1450 return VLC_SUCCESS;
1451 case VLC_ML_COUNT_ALBUM_TRACKS:
1452 *va_arg( args, size_t* ) = query->count();
1453 return VLC_SUCCESS;
1454 default:
1455 vlc_assert_unreachable();
1458 case VLC_ML_LIST_ALBUM_ARTISTS:
1459 case VLC_ML_COUNT_ALBUM_ARTISTS:
1461 auto query = album->artists( paramsPtr );
1462 if ( query == nullptr )
1463 return VLC_EGENERIC;
1464 switch ( listQuery )
1466 case VLC_ML_LIST_ALBUM_ARTISTS:
1467 *va_arg( args, vlc_ml_artist_list_t**) =
1468 ml_convert_list<vlc_ml_artist_list_t, vlc_ml_artist_t>(
1469 query->items( nbItems, offset ) );
1470 return VLC_SUCCESS;
1471 case VLC_ML_COUNT_ALBUM_ARTISTS:
1472 *va_arg( args, size_t* ) = query->count();
1473 return VLC_SUCCESS;
1474 default:
1475 vlc_assert_unreachable();
1478 default:
1479 vlc_assert_unreachable();
1483 int MediaLibrary::listArtists( int listQuery, const medialibrary::QueryParameters* paramsPtr,
1484 const char* pattern, uint32_t nbItems, uint32_t offset,
1485 va_list args )
1487 auto artist = m_ml->artist( va_arg( args, int64_t ) );
1488 if ( artist == nullptr )
1489 return VLC_EGENERIC;
1490 switch( listQuery )
1492 case VLC_ML_LIST_ARTIST_ALBUMS:
1493 case VLC_ML_COUNT_ARTIST_ALBUMS:
1495 medialibrary::Query<medialibrary::IAlbum> query;
1496 if ( pattern != nullptr )
1497 query = artist->searchAlbums( pattern, paramsPtr );
1498 else
1499 query = artist->albums( paramsPtr );
1500 if ( query == nullptr )
1501 return VLC_EGENERIC;
1502 switch ( listQuery )
1504 case VLC_ML_LIST_ARTIST_ALBUMS:
1505 *va_arg( args, vlc_ml_album_list_t**) =
1506 ml_convert_list<vlc_ml_album_list_t, vlc_ml_album_t>(
1507 query->items( nbItems, offset ) );
1508 return VLC_SUCCESS;
1509 case VLC_ML_COUNT_ARTIST_ALBUMS:
1510 *va_arg( args, size_t* ) = query->count();
1511 return VLC_SUCCESS;
1512 default:
1513 vlc_assert_unreachable();
1516 case VLC_ML_LIST_ARTIST_TRACKS:
1517 case VLC_ML_COUNT_ARTIST_TRACKS:
1519 medialibrary::Query<medialibrary::IMedia> query;
1520 if ( pattern != nullptr )
1521 query = artist->searchTracks( pattern, paramsPtr );
1522 else
1523 query = artist->tracks( paramsPtr );
1524 if ( query == nullptr )
1525 return VLC_EGENERIC;
1526 switch ( listQuery )
1528 case VLC_ML_LIST_ARTIST_TRACKS:
1529 *va_arg( args, vlc_ml_media_list_t**) =
1530 ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
1531 query->items( nbItems, offset ) );
1532 return VLC_SUCCESS;
1533 case VLC_ML_COUNT_ARTIST_TRACKS:
1534 *va_arg( args, size_t* ) = query->count();
1535 return VLC_SUCCESS;
1536 default:
1537 vlc_assert_unreachable();
1540 default:
1541 vlc_assert_unreachable();
1545 int MediaLibrary::listGenre( int listQuery, const medialibrary::QueryParameters* paramsPtr,
1546 const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
1548 auto genre = m_ml->genre( va_arg( args, int64_t ) );
1549 if ( genre == nullptr )
1550 return VLC_EGENERIC;
1551 switch( listQuery )
1553 case VLC_ML_LIST_GENRE_ARTISTS:
1554 case VLC_ML_COUNT_GENRE_ARTISTS:
1556 medialibrary::Query<medialibrary::IArtist> query;
1557 if ( pattern != nullptr )
1558 query = genre->searchArtists( pattern, paramsPtr );
1559 else
1560 query = genre->artists( paramsPtr );
1561 if ( query == nullptr )
1562 return VLC_EGENERIC;
1563 switch ( listQuery )
1565 case VLC_ML_LIST_GENRE_ARTISTS:
1566 *va_arg( args, vlc_ml_artist_list_t**) =
1567 ml_convert_list<vlc_ml_artist_list_t, vlc_ml_artist_t>(
1568 query->items( nbItems, offset ) );
1569 return VLC_SUCCESS;
1570 case VLC_ML_COUNT_GENRE_ARTISTS:
1571 *va_arg( args, size_t* ) = query->count();
1572 return VLC_SUCCESS;
1573 default:
1574 vlc_assert_unreachable();
1577 case VLC_ML_LIST_GENRE_TRACKS:
1578 case VLC_ML_COUNT_GENRE_TRACKS:
1580 medialibrary::Query<medialibrary::IMedia> query;
1581 if ( pattern != nullptr )
1582 query = genre->searchTracks( pattern, paramsPtr );
1583 else
1584 query = genre->tracks( medialibrary::IGenre::TracksIncluded::All,
1585 paramsPtr );
1586 if ( query == nullptr )
1587 return VLC_EGENERIC;
1588 switch ( listQuery )
1590 case VLC_ML_LIST_GENRE_TRACKS:
1591 *va_arg( args, vlc_ml_media_list_t**) =
1592 ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
1593 query->items( nbItems, offset ) );
1594 return VLC_SUCCESS;
1595 case VLC_ML_COUNT_GENRE_TRACKS:
1596 *va_arg( args, size_t*) = query->count();
1597 return VLC_SUCCESS;
1598 default:
1599 vlc_assert_unreachable();
1602 case VLC_ML_LIST_GENRE_ALBUMS:
1603 case VLC_ML_COUNT_GENRE_ALBUMS:
1605 medialibrary::Query<medialibrary::IAlbum> query;
1606 if ( pattern != nullptr )
1607 query = genre->searchAlbums( pattern, paramsPtr );
1608 else
1609 query = genre->albums( paramsPtr );
1610 if ( query == nullptr )
1611 return VLC_EGENERIC;
1612 switch ( listQuery )
1614 case VLC_ML_LIST_GENRE_ALBUMS:
1615 *va_arg( args, vlc_ml_album_list_t**) =
1616 ml_convert_list<vlc_ml_album_list_t, vlc_ml_album_t>(
1617 query->items( nbItems, offset ) );
1618 return VLC_SUCCESS;
1619 case VLC_ML_COUNT_GENRE_ALBUMS:
1620 *va_arg( args, size_t* ) = query->count();
1621 return VLC_SUCCESS;
1622 default:
1623 vlc_assert_unreachable();
1626 default:
1627 vlc_assert_unreachable();
1631 int MediaLibrary::listPlaylist( int listQuery, const medialibrary::QueryParameters* paramsPtr,
1632 const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
1634 switch( listQuery )
1636 case VLC_ML_LIST_PLAYLISTS:
1637 case VLC_ML_COUNT_PLAYLISTS:
1639 medialibrary::Query<medialibrary::IPlaylist> query;
1640 if ( pattern != nullptr )
1641 query = m_ml->searchPlaylists( pattern, paramsPtr );
1642 else
1643 query = m_ml->playlists( paramsPtr );
1644 if ( query == nullptr )
1645 return VLC_EGENERIC;
1646 switch ( listQuery )
1648 case VLC_ML_LIST_PLAYLISTS:
1649 *va_arg( args, vlc_ml_playlist_list_t** ) =
1650 ml_convert_list<vlc_ml_playlist_list_t, vlc_ml_playlist_t>(
1651 query->items( nbItems, offset ) );
1652 return VLC_SUCCESS;
1653 case VLC_ML_COUNT_PLAYLISTS:
1654 *va_arg( args, size_t* ) = query->count();
1655 return VLC_SUCCESS;
1656 default:
1657 vlc_assert_unreachable();
1660 case VLC_ML_LIST_PLAYLIST_MEDIA:
1661 case VLC_ML_COUNT_PLAYLIST_MEDIA:
1663 auto playlist = m_ml->playlist( va_arg( args, int64_t ) );
1664 if ( playlist == nullptr )
1665 return VLC_EGENERIC;
1666 medialibrary::Query<medialibrary::IMedia> query;
1667 if ( pattern != nullptr )
1668 query = playlist->searchMedia( pattern, paramsPtr );
1669 else
1670 query = playlist->media();
1671 if ( query == nullptr )
1672 return VLC_EGENERIC;
1673 switch ( listQuery )
1675 case VLC_ML_LIST_PLAYLIST_MEDIA:
1676 *va_arg( args, vlc_ml_media_list_t**) =
1677 ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
1678 query->items( nbItems, offset ) );
1679 return VLC_SUCCESS;
1680 case VLC_ML_COUNT_PLAYLIST_MEDIA:
1681 *va_arg( args, size_t* ) = query->count();
1682 return VLC_SUCCESS;
1683 default:
1684 vlc_assert_unreachable();
1687 default:
1688 vlc_assert_unreachable();
1692 int MediaLibrary::listMedia( int listQuery, const medialibrary::QueryParameters *params,
1693 const char *, uint32_t nbItems, uint32_t offset,
1694 va_list args )
1696 auto media = m_ml->media( va_arg( args, int64_t ) );
1697 if ( media == nullptr )
1698 return VLC_EGENERIC;
1699 switch ( listQuery )
1701 case VLC_ML_LIST_MEDIA_LABELS:
1702 case VLC_ML_COUNT_MEDIA_LABELS:
1704 auto query = media->labels();
1705 if ( query == nullptr )
1706 return VLC_EGENERIC;
1707 switch ( listQuery )
1709 case VLC_ML_LIST_MEDIA_LABELS:
1710 *va_arg( args, vlc_ml_label_list_t**) =
1711 ml_convert_list<vlc_ml_label_list_t, vlc_ml_label_t>(
1712 query->items( nbItems, offset ) );
1713 return VLC_SUCCESS;
1714 case VLC_ML_COUNT_MEDIA_LABELS:
1715 *va_arg( args, size_t* ) = query->count();
1716 return VLC_SUCCESS;
1717 default:
1718 vlc_assert_unreachable();
1721 case VLC_ML_LIST_MEDIA_BOOKMARKS:
1723 *va_arg( args, vlc_ml_bookmark_list_t** ) =
1724 ml_convert_list<vlc_ml_bookmark_list_t, vlc_ml_bookmark_t>(
1725 media->bookmarks( params )->all() );
1726 return VLC_SUCCESS;
1728 default:
1729 vlc_assert_unreachable();
1733 static void* Get( vlc_medialibrary_module_t* module, int query, va_list args )
1735 auto ml = static_cast<MediaLibrary*>( module->p_sys );
1736 return ml->Get( query, args );
1739 static int List( vlc_medialibrary_module_t* module, int query,
1740 const vlc_ml_query_params_t* params, va_list args )
1742 auto ml = static_cast<MediaLibrary*>( module->p_sys );
1743 return ml->List( query, params, args );
1746 static int Control( vlc_medialibrary_module_t* module, int query, va_list args )
1748 auto ml = static_cast<MediaLibrary*>( module->p_sys );
1749 return ml->Control( query, args );
1752 static int Open( vlc_object_t* obj )
1754 auto* p_ml = reinterpret_cast<vlc_medialibrary_module_t*>( obj );
1758 p_ml->p_sys = new MediaLibrary( p_ml );
1760 catch ( const std::exception& ex )
1762 msg_Err( obj, "Failed to instantiate/initialize medialibrary: %s", ex.what() );
1763 return VLC_EGENERIC;
1765 p_ml->pf_control = Control;
1766 p_ml->pf_get = Get;
1767 p_ml->pf_list = List;
1768 return VLC_SUCCESS;
1771 static void Close( vlc_object_t* obj )
1773 vlc_medialibrary_module_t *module = reinterpret_cast<vlc_medialibrary_module_t*>( obj );
1774 MediaLibrary* p_ml = static_cast<MediaLibrary*>( module->p_sys );
1775 delete p_ml;
1778 #define ML_FOLDER_TEXT _( "Folders discovered by the media library" )
1779 #define ML_FOLDER_LONGTEXT _( "Semicolon separated list of folders to discover " \
1780 "media from" )
1782 #define ML_VERBOSE _( "Extra verbose media library logs" )
1784 vlc_module_begin()
1785 set_shortname(N_("media library"))
1786 set_description(N_( "Organize your media" ))
1787 set_category(CAT_ADVANCED)
1788 set_subcategory(SUBCAT_ADVANCED_MISC)
1789 set_capability("medialibrary", 100)
1790 set_callbacks(Open, Close)
1791 add_string( "ml-folders", nullptr, ML_FOLDER_TEXT, ML_FOLDER_LONGTEXT, false )
1792 add_bool( "ml-verbose", false, ML_VERBOSE, ML_VERBOSE, false )
1793 vlc_module_end()