[vlc-commits] [Git][videolan/vlc][master] 16 commits: medialibrary: Add functions to retrieve favorites from media library
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Sat Jul 12 15:11:39 UTC 2025
Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits:
4763efc4 by Claudio Cambra at 2025-07-12T14:49:06+00:00
medialibrary: Add functions to retrieve favorites from media library
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
c31d50c5 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Implement media library favorites arrays in library model
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
a4ddea20 by Claudio Cambra at 2025-07-12T14:49:06+00:00
medialibrary: Send event when favorites change
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
3778130e by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Add VLCLibraryFavoritesViewController
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
30f01ec4 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Add VLCLibraryFavoritesDataSource
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
78a72612 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Handle favorites change event in library model
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
a8b00d0a by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Add persistent view mode preferences for favorites
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
5ea5e9f2 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Implement basic loading of items into favorites data source
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
b2f94e53 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Implement conformance to collection view data source in favorites data source
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
c6b70ddb by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Implement master/detail table view conformance in favorites data source
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
75d9d4f8 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Configure and present collection view (or placeholder view) for favorites view controller
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
9888c5c1 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Add favorites library segment
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
f3a40bbb by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Configure and setup table view for favorites library
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
2d6186d9 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Implement presenting library item for table view in favorites vc
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
60f89431 by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Emit notification on displayed collection change in favorites data source
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
8d0a5ccc by Claudio Cambra at 2025-07-12T14:49:06+00:00
macosx: Allow creating dummy media library items with media items
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
16 changed files:
- extras/package/macosx/VLC.xcodeproj/project.pbxproj
- include/vlc_media_library.h
- modules/gui/macosx/Makefile.am
- modules/gui/macosx/library/VLCLibraryDataTypes.h
- modules/gui/macosx/library/VLCLibraryDataTypes.m
- modules/gui/macosx/library/VLCLibraryModel.h
- modules/gui/macosx/library/VLCLibraryModel.m
- modules/gui/macosx/library/VLCLibrarySegment.h
- modules/gui/macosx/library/VLCLibrarySegment.m
- modules/gui/macosx/library/VLCLibraryWindowPersistentPreferences.h
- modules/gui/macosx/library/VLCLibraryWindowPersistentPreferences.m
- + modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesDataSource.h
- + modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesDataSource.m
- + modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesViewController.h
- + modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesViewController.m
- modules/misc/medialibrary/medialibrary.cpp
Changes:
=====================================
extras/package/macosx/VLC.xcodeproj/project.pbxproj
=====================================
@@ -157,6 +157,8 @@
53D0213D2CC25520003C008F /* VLCPlayerTitle.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D0213C2CC25520003C008F /* VLCPlayerTitle.m */; };
53D21A1F2BB465600085C71B /* VLCLibraryWindowPlayQueueSidebarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D21A1E2BB465600085C71B /* VLCLibraryWindowPlayQueueSidebarViewController.m */; };
53D8ED9A2B583AAF00142EAD /* VLCLibraryModelChangeDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D8ED992B583AAF00142EAD /* VLCLibraryModelChangeDelegate.m */; };
+ 53E511282E1AD14A0048CEFC /* VLCLibraryFavoritesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E511272E1AD14A0048CEFC /* VLCLibraryFavoritesViewController.m */; };
+ 53E5112B2E1ADA9F0048CEFC /* VLCLibraryFavoritesDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E5112A2E1ADA9F0048CEFC /* VLCLibraryFavoritesDataSource.m */; };
53ED472329C74D1F00795DB1 /* VLCLibraryAudioTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53ED472229C74D1F00795DB1 /* VLCLibraryAudioTableViewDelegate.m */; };
53ED472629C78FE700795DB1 /* VLCLibraryAudioGroupTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53ED472529C78FE700795DB1 /* VLCLibraryAudioGroupTableViewDelegate.m */; };
53ED472B29C8FF9D00795DB1 /* VLCLibraryAlbumTracksTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53ED472A29C8FF9D00795DB1 /* VLCLibraryAlbumTracksTableViewDelegate.m */; };
@@ -489,6 +491,10 @@
53D6664E2B6B82940042C03D /* VLCLibraryTableViewDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryTableViewDataSource.h; sourceTree = "<group>"; };
53D8ED982B583AAF00142EAD /* VLCLibraryModelChangeDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryModelChangeDelegate.h; sourceTree = "<group>"; };
53D8ED992B583AAF00142EAD /* VLCLibraryModelChangeDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryModelChangeDelegate.m; sourceTree = "<group>"; };
+ 53E511262E1AD14A0048CEFC /* VLCLibraryFavoritesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryFavoritesViewController.h; sourceTree = "<group>"; };
+ 53E511272E1AD14A0048CEFC /* VLCLibraryFavoritesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryFavoritesViewController.m; sourceTree = "<group>"; };
+ 53E511292E1ADA9F0048CEFC /* VLCLibraryFavoritesDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryFavoritesDataSource.h; sourceTree = "<group>"; };
+ 53E5112A2E1ADA9F0048CEFC /* VLCLibraryFavoritesDataSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryFavoritesDataSource.m; sourceTree = "<group>"; };
53ED472129C74D1F00795DB1 /* VLCLibraryAudioTableViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryAudioTableViewDelegate.h; sourceTree = "<group>"; };
53ED472229C74D1F00795DB1 /* VLCLibraryAudioTableViewDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryAudioTableViewDelegate.m; sourceTree = "<group>"; };
53ED472429C78FE700795DB1 /* VLCLibraryAudioGroupTableViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryAudioGroupTableViewDelegate.h; sourceTree = "<group>"; };
@@ -1372,6 +1378,7 @@
isa = PBXGroup;
children = (
5325C5742930026600B2B63A /* audio-library */,
+ 53E511252E1ACB6C0048CEFC /* favorites-library */,
537BD6832C59212A00446ED0 /* groups-library */,
5350E4EB2B1B210E00F276CB /* home-library */,
7DFBDCB8226CED3700B700A5 /* media-source */,
@@ -1731,6 +1738,17 @@
path = "video-library";
sourceTree = "<group>";
};
+ 53E511252E1ACB6C0048CEFC /* favorites-library */ = {
+ isa = PBXGroup;
+ children = (
+ 53E511262E1AD14A0048CEFC /* VLCLibraryFavoritesViewController.h */,
+ 53E511272E1AD14A0048CEFC /* VLCLibraryFavoritesViewController.m */,
+ 53E511292E1ADA9F0048CEFC /* VLCLibraryFavoritesDataSource.h */,
+ 53E5112A2E1ADA9F0048CEFC /* VLCLibraryFavoritesDataSource.m */,
+ );
+ path = "favorites-library";
+ sourceTree = "<group>";
+ };
6B8224481E4D2B0500833BE1 /* Template Images */ = {
isa = PBXGroup;
children = (
@@ -2436,6 +2454,7 @@
1C3113D31E508C6900D4DD76 /* VLCResumeDialogController.m in Sources */,
7D2FFA40227B8A5B0085D649 /* VLCLinearProgressIndicator.m in Sources */,
538DC4E32A6B69B50082DECD /* VLCLibraryAudioGroupHeaderView.m in Sources */,
+ 53E5112B2E1ADA9F0048CEFC /* VLCLibraryFavoritesDataSource.m in Sources */,
7DFBDCB1226A518400B700A5 /* VLCLibraryMenuController.m in Sources */,
536283F8291146BC00640C15 /* VLCLibrarySongTableCellView.m in Sources */,
53A8F9D12A7E1E6300BC11BF /* VLCLibraryPlaylistViewController.m in Sources */,
@@ -2449,6 +2468,7 @@
1C3113D91E508C6900D4DD76 /* VLCSimplePrefsController.m in Sources */,
7DFBDCC4226E445500B700A5 /* VLCMediaSourceBaseDataSource.m in Sources */,
6B2EFC601F2819F700F3C0EA /* VLCVolumeSlider.m in Sources */,
+ 53E511282E1AD14A0048CEFC /* VLCLibraryFavoritesViewController.m in Sources */,
53F399802AC6D6B400B86241 /* VLCLibraryHomeViewVideoCarouselContainerView.m in Sources */,
7DE2F0472282D5D10040DD0A /* VLCLibraryTableCellView.m in Sources */,
53C1EF8C2B466B13001AEEF5 /* VLCLibraryHomeViewStackViewController.m in Sources */,
=====================================
include/vlc_media_library.h
=====================================
@@ -551,6 +551,24 @@ enum vlc_ml_list_queries
VLC_ML_LIST_FOLDER_MEDIA, /**< arg1: folder id; arg2 (out): vlc_ml_media_list_t** */
VLC_ML_COUNT_FOLDER_MEDIA, /**< arg1: folder id; arg2 (out): size_t* */
+ /* Favorites listings */
+ VLC_ML_LIST_FAVORITE_MEDIA, /**< arg1 (out): vlc_ml_media_list_t** */
+ VLC_ML_COUNT_FAVORITE_MEDIA, /**< arg1 (out): size_t* */
+ VLC_ML_LIST_FAVORITE_VIDEOS, /**< arg1 (out): vlc_ml_media_list_t** */
+ VLC_ML_COUNT_FAVORITE_VIDEOS, /**< arg1 (out): size_t* */
+ VLC_ML_LIST_FAVORITE_AUDIOS, /**< arg1 (out): vlc_ml_media_list_t** */
+ VLC_ML_COUNT_FAVORITE_AUDIOS, /**< arg1 (out): size_t* */
+ VLC_ML_LIST_FAVORITE_ALBUMS, /**< arg1 (out): vlc_ml_album_list_t** */
+ VLC_ML_COUNT_FAVORITE_ALBUMS, /**< arg1 (out): size_t* */
+ VLC_ML_LIST_FAVORITE_ARTISTS, /**< arg1 (out): vlc_ml_artist_list_t** */
+ VLC_ML_COUNT_FAVORITE_ARTISTS,/**< arg1 (out): size_t* */
+ VLC_ML_LIST_FAVORITE_GENRES, /**< arg1 (out): vlc_ml_genre_list_t** */
+ VLC_ML_COUNT_FAVORITE_GENRES, /**< arg1 (out): size_t* */
+ VLC_ML_LIST_FAVORITE_PLAYLISTS,/**< arg1 (out): vlc_ml_playlist_list_t** */
+ VLC_ML_COUNT_FAVORITE_PLAYLISTS,/**< arg1 (out): size_t* */
+ VLC_ML_LIST_FAVORITE_FOLDERS, /**< arg1 (out): vlc_ml_folder_list_t** */
+ VLC_ML_COUNT_FAVORITE_FOLDERS,/**< arg1 (out): size_t* */
+
/* Children entities listing */
VLC_ML_LIST_MEDIA_OF, /**< arg1: parent entity type; arg2: parent entity id; arg3(out): ml_media_list_t** */
VLC_ML_COUNT_MEDIA_OF, /**< arg1: parent entity type; arg2: parent entity id; arg3(out): size_t* */
@@ -813,6 +831,12 @@ enum vlc_ml_event_type
* Sent when an application requested rescan starts being processed.
*/
VLC_ML_EVENT_RESCAN_STARTED,
+ /**
+ * Sent when favorites list changes. This includes when entities are
+ * marked/unmarked as favorites.
+ * The entity type and id are stored in vlc_ml_event_t::favorites_changed
+ */
+ VLC_ML_EVENT_FAVORITES_CHANGED,
};
typedef struct vlc_ml_event_t
@@ -885,6 +909,12 @@ typedef struct vlc_ml_event_t
{
vlc_ml_history_type_t history_type;
} history_changed;
+ struct
+ {
+ int64_t i_entity_id;
+ int i_entity_type; /**< One of VLC_ML_PARENT_* values */
+ bool b_favorite; /**< true if marked as favorite, false if unmarked */
+ } favorites_changed;
};
} vlc_ml_event_t;
@@ -2011,6 +2041,153 @@ static inline int vlc_ml_playlist_set_favorite( vlc_medialibrary_t *p_ml, int64_
return vlc_ml_control( p_ml, VLC_ML_PLAYLIST_SET_FAVORITE, i_playlist_id, (int)b_favorite );
}
+//-------------------------------------------------------------------------------------------------
+// Favorites
+
+static inline vlc_ml_media_list_t* vlc_ml_list_favorite_media( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_media_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_MEDIA, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_media( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_MEDIA, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
+static inline vlc_ml_media_list_t* vlc_ml_list_favorite_videos( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_media_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_VIDEOS, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_videos( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_VIDEOS, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
+static inline vlc_ml_media_list_t* vlc_ml_list_favorite_audios( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_media_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_AUDIOS, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_audios( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_AUDIOS, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
+static inline vlc_ml_album_list_t* vlc_ml_list_favorite_albums( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_album_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_ALBUMS, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_albums( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_ALBUMS, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
+static inline vlc_ml_artist_list_t* vlc_ml_list_favorite_artists( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_artist_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_ARTISTS, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_artists( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_ARTISTS, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
+static inline vlc_ml_genre_list_t* vlc_ml_list_favorite_genres( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_genre_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_GENRES, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_genres( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_GENRES, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
+static inline vlc_ml_playlist_list_t* vlc_ml_list_favorite_playlists( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_playlist_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_PLAYLISTS, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_playlists( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_PLAYLISTS, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
+static inline vlc_ml_folder_list_t* vlc_ml_list_favorite_folders( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ vlc_ml_folder_list_t* res;
+ if ( vlc_ml_list( p_ml, VLC_ML_LIST_FAVORITE_FOLDERS, params, &res ) != VLC_SUCCESS )
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_favorite_folders( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
+{
+ vlc_assert( p_ml != NULL );
+ size_t count;
+ if ( vlc_ml_list( p_ml, VLC_ML_COUNT_FAVORITE_FOLDERS, params, &count ) != VLC_SUCCESS )
+ return 0;
+ return count;
+}
+
#ifdef __cplusplus
}
#endif /* C++ */
=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -289,6 +289,10 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/library/audio-library/VLCLibrarySongTableCellView.m \
gui/macosx/library/audio-library/VLCLibrarySongsTableViewSongPlayingTableCellView.h \
gui/macosx/library/audio-library/VLCLibrarySongsTableViewSongPlayingTableCellView.m \
+ gui/macosx/library/favorites-library/VLCLibraryFavoritesDataSource.h \
+ gui/macosx/library/favorites-library/VLCLibraryFavoritesDataSource.m \
+ gui/macosx/library/favorites-library/VLCLibraryFavoritesViewController.h \
+ gui/macosx/library/favorites-library/VLCLibraryFavoritesViewController.m \
gui/macosx/library/media-source/VLCLibraryMediaSourceViewController.h \
gui/macosx/library/media-source/VLCLibraryMediaSourceViewController.m \
gui/macosx/library/media-source/VLCMediaSource.h \
=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.h
=====================================
@@ -357,6 +357,9 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
withPrimaryDetailString:(nullable NSString *)primaryDetailString
withSecondaryDetailString:(nullable NSString *)secondaryDetailString;
+- (instancetype)initWithDisplayString:(NSString *)displayString
+ withMediaItems:(NSArray<VLCMediaLibraryMediaItem *> *)mediaItems;
+
@end
NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.m
=====================================
@@ -1695,22 +1695,37 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
return self;
}
+- (instancetype)initWithDisplayString:(NSString *)displayString
+ withMediaItems:(NSArray<VLCMediaLibraryMediaItem *> *)mediaItems
+{
+
+ self = [self initWithDisplayString:displayString
+ withPrimaryDetailString:[NSString stringWithFormat:@"%lu items", (unsigned long)mediaItems.count]
+ withSecondaryDetailString:@""];
+ if (self) {
+ _mediaItems = mediaItems;
+ _firstMediaItem = mediaItems.firstObject;
+ }
+ return self;
+}
+
- (void)moveToTrash
{
- [self doesNotRecognizeSelector:_cmd];
- return;
+ [self iterateMediaItemsWithBlock:^(VLCMediaLibraryMediaItem * const item) {
+ [item moveToTrash];
+ }];
}
- (void)revealInFinder
{
- [self doesNotRecognizeSelector:_cmd];
- return;
+ [self.mediaItems.firstObject revealInFinder];
}
- (void)iterateMediaItemsWithBlock:(nonnull void (^)(VLCMediaLibraryMediaItem * _Nonnull))mediaItemBlock
{
- [self doesNotRecognizeSelector:_cmd];
- return;
+ for (VLCMediaLibraryMediaItem * const childItem in self.mediaItems) {
+ mediaItemBlock(childItem);
+ }
}
@end
=====================================
modules/gui/macosx/library/VLCLibraryModel.h
=====================================
@@ -36,6 +36,11 @@ extern NSString * const VLCLibraryModelMediaItemThumbnailGenerated;
extern NSString * const VLCLibraryModelAudioMediaListReset;
extern NSString * const VLCLibraryModelVideoMediaListReset;
+extern NSString * const VLCLibraryModelFavoriteAudioMediaListReset;
+extern NSString * const VLCLibraryModelFavoriteVideoMediaListReset;
+extern NSString * const VLCLibraryModelFavoriteAlbumsListReset;
+extern NSString * const VLCLibraryModelFavoriteArtistsListReset;
+extern NSString * const VLCLibraryModelFavoriteGenresListReset;
extern NSString * const VLCLibraryModelRecentsMediaListReset;
extern NSString * const VLCLibraryModelRecentAudioMediaListReset;
extern NSString * const VLCLibraryModelListOfShowsReset;
@@ -120,6 +125,22 @@ extern NSString * const VLCLibraryModelDiscoveryFailed;
- (void)sortByCriteria:(enum vlc_ml_sorting_criteria_t)sortCriteria
andDescending:(bool)descending;
+// Favorites support
+ at property (readonly) size_t numberOfFavoriteAudioMedia;
+ at property (readonly) NSArray <VLCMediaLibraryMediaItem *> *listOfFavoriteAudioMedia;
+
+ at property (readonly) size_t numberOfFavoriteVideoMedia;
+ at property (readonly) NSArray <VLCMediaLibraryMediaItem *> *listOfFavoriteVideoMedia;
+
+ at property (readonly) size_t numberOfFavoriteAlbums;
+ at property (readonly) NSArray <VLCMediaLibraryAlbum *> *listOfFavoriteAlbums;
+
+ at property (readonly) size_t numberOfFavoriteArtists;
+ at property (readonly) NSArray <VLCMediaLibraryArtist *> *listOfFavoriteArtists;
+
+ at property (readonly) size_t numberOfFavoriteGenres;
+ at property (readonly) NSArray <VLCMediaLibraryGenre *> *listOfFavoriteGenres;
+
@end
NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/library/VLCLibraryModel.m
=====================================
@@ -37,6 +37,11 @@ NSString * const VLCLibraryModelMediaItemThumbnailGenerated = @"VLCLibraryModelM
NSString * const VLCLibraryModelAudioMediaListReset = @"VLCLibraryModelAudioMediaListReset";
NSString * const VLCLibraryModelVideoMediaListReset = @"VLCLibraryModelVideoMediaListReset";
+NSString * const VLCLibraryModelFavoriteAudioMediaListReset = @"VLCLibraryModelFavoriteAudioMediaListReset";
+NSString * const VLCLibraryModelFavoriteVideoMediaListReset = @"VLCLibraryModelFavoriteVideoMediaListReset";
+NSString * const VLCLibraryModelFavoriteAlbumsListReset = @"VLCLibraryModelFavoriteAlbumsListReset";
+NSString * const VLCLibraryModelFavoriteArtistsListReset = @"VLCLibraryModelFavoriteArtistsListReset";
+NSString * const VLCLibraryModelFavoriteGenresListReset = @"VLCLibraryModelFavoriteGenresListReset";
NSString * const VLCLibraryModelRecentsMediaListReset = @"VLCLibraryModelRecentsMediaListReset";
NSString * const VLCLibraryModelRecentAudioMediaListReset = @"VLCLibraryModelRecentAudioMediaListReset";
NSString * const VLCLibraryModelListOfShowsReset = @"VLCLibraryModelListOfShowsReset";
@@ -218,6 +223,17 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
[libraryModel resetCachedListOfRecentMedia];
[libraryModel resetCachedListOfRecentAudioMedia];
break;
+ case VLC_ML_EVENT_FAVORITES_CHANGED:
+ {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteAudioMediaListReset object:libraryModel];
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteVideoMediaListReset object:libraryModel];
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteAlbumsListReset object:libraryModel];
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteArtistsListReset object:libraryModel];
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelFavoriteGenresListReset object:libraryModel];
+ });
+ break;
+ }
case VLC_ML_EVENT_DISCOVERY_STARTED:
dispatch_async(dispatch_get_main_queue(), ^{
NSNotificationCenter * const defaultCenter = NSNotificationCenter.defaultCenter;
@@ -1334,4 +1350,117 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
});
}
+#pragma mark - Favorites Support
+
+- (NSArray<VLCMediaLibraryMediaItem *> *)listOfFavoriteAudioMedia
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_media_list_t * const p_media_list = vlc_ml_list_favorite_audios(_p_mediaLibrary, &queryParams);
+ NSArray * const mediaArray = [NSArray arrayFromVlcMediaList:p_media_list];
+ if (mediaArray == nil) {
+ return @[];
+ }
+ vlc_ml_media_list_release(p_media_list);
+ return mediaArray;
+}
+
+- (NSArray<VLCMediaLibraryMediaItem *> *)listOfFavoriteVideoMedia
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_media_list_t * const p_media_list = vlc_ml_list_favorite_videos(_p_mediaLibrary, &queryParams);
+ NSArray * const mediaArray = [NSArray arrayFromVlcMediaList:p_media_list];
+ if (mediaArray == nil) {
+ return @[];
+ }
+ vlc_ml_media_list_release(p_media_list);
+ return mediaArray;
+}
+
+- (NSArray<VLCMediaLibraryAlbum *> *)listOfFavoriteAlbums
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_album_list_t * const p_album_list = vlc_ml_list_favorite_albums(_p_mediaLibrary, &queryParams);
+ if (p_album_list == NULL) {
+ return @[];
+ }
+
+ NSMutableArray * const mutableArray = [[NSMutableArray alloc] initWithCapacity:p_album_list->i_nb_items];
+ for (size_t x = 0; x < p_album_list->i_nb_items; x++) {
+ VLCMediaLibraryAlbum * const album = [[VLCMediaLibraryAlbum alloc] initWithAlbum:&p_album_list->p_items[x]];
+ if (album != nil) {
+ [mutableArray addObject:album];
+ }
+ }
+ vlc_ml_album_list_release(p_album_list);
+ return mutableArray.copy;
+}
+
+- (NSArray<VLCMediaLibraryArtist *> *)listOfFavoriteArtists
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_artist_list_t * const p_artist_list = vlc_ml_list_favorite_artists(_p_mediaLibrary, &queryParams);
+ if (p_artist_list == NULL) {
+ return @[];
+ }
+
+ NSMutableArray * const mutableArray = [[NSMutableArray alloc] initWithCapacity:p_artist_list->i_nb_items];
+ for (size_t x = 0; x < p_artist_list->i_nb_items; x++) {
+ VLCMediaLibraryArtist * const artist = [[VLCMediaLibraryArtist alloc] initWithArtist:&p_artist_list->p_items[x]];
+ if (artist != nil) {
+ [mutableArray addObject:artist];
+ }
+ }
+ vlc_ml_artist_list_release(p_artist_list);
+ return mutableArray.copy;
+}
+
+- (NSArray<VLCMediaLibraryGenre *> *)listOfFavoriteGenres
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ vlc_ml_genre_list_t * const p_genre_list = vlc_ml_list_favorite_genres(_p_mediaLibrary, &queryParams);
+ if (p_genre_list == NULL) {
+ return @[];
+ }
+
+ NSMutableArray * const mutableArray = [[NSMutableArray alloc] initWithCapacity:p_genre_list->i_nb_items];
+ for (size_t x = 0; x < p_genre_list->i_nb_items; x++) {
+ VLCMediaLibraryGenre * const genre = [[VLCMediaLibraryGenre alloc] initWithGenre:&p_genre_list->p_items[x]];
+ if (genre != nil) {
+ [mutableArray addObject:genre];
+ }
+ }
+ vlc_ml_genre_list_release(p_genre_list);
+ return mutableArray.copy;
+}
+
+- (size_t)numberOfFavoriteAudioMedia
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ return vlc_ml_count_favorite_audios(_p_mediaLibrary, &queryParams);
+}
+
+- (size_t)numberOfFavoriteVideoMedia
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ return vlc_ml_count_favorite_videos(_p_mediaLibrary, &queryParams);
+}
+
+- (size_t)numberOfFavoriteAlbums
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ return vlc_ml_count_favorite_albums(_p_mediaLibrary, &queryParams);
+}
+
+- (size_t)numberOfFavoriteArtists
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ return vlc_ml_count_favorite_artists(_p_mediaLibrary, &queryParams);
+}
+
+- (size_t)numberOfFavoriteGenres
+{
+ const vlc_ml_query_params_t queryParams = [self queryParams];
+ return vlc_ml_count_favorite_genres(_p_mediaLibrary, &queryParams);
+}
+
@end
=====================================
modules/gui/macosx/library/VLCLibrarySegment.h
=====================================
@@ -33,6 +33,7 @@ extern NSString * const VLCLibraryBookmarkedLocationsChanged;
typedef NS_ENUM(NSInteger, VLCLibrarySegmentType) {
VLCLibraryLowSentinelSegment = -1,
VLCLibraryHomeSegmentType,
+ VLCLibraryFavoritesSegmentType,
VLCLibraryHeaderSegmentType,
VLCLibraryVideoSegmentType,
VLCLibraryShowsVideoSubSegmentType,
=====================================
modules/gui/macosx/library/VLCLibrarySegment.m
=====================================
@@ -35,6 +35,8 @@
#import "library/audio-library/VLCLibraryAudioViewController.h"
+#import "library/favorites-library/VLCLibraryFavoritesViewController.h"
+
#import "library/groups-library/VLCLibraryGroupsViewController.h"
#import "library/home-library/VLCLibraryHomeViewController.h"
@@ -163,6 +165,44 @@ NSArray<NSString *> *defaultBookmarkedLocations()
@end
+// MARK: - VLCLibraryFavoritesSegment
+
+ at interface VLCLibraryFavoritesSegment : VLCLibrarySegment
+ at end
+
+ at implementation VLCLibraryFavoritesSegment
+
+- (instancetype)init
+{
+ self = [super initWithSegmentType:VLCLibraryFavoritesSegmentType];
+ if (self) {
+ self.internalDisplayString = _NS("Favorites");
+ if (@available(macOS 11.0, *)) {
+ self.internalDisplayImage = [NSImage imageWithSystemSymbolName:@"heart"
+ accessibilityDescription:@"Favorites icon"];
+ } else {
+ self.internalDisplayImage = [NSImage imageNamed:@"bw-home"];
+ self.internalDisplayImage.template = YES;
+ }
+ self.internalLibraryViewControllerClass = VLCLibraryFavoritesViewController.class;
+ self.internalLibraryViewControllerCreator = ^{
+ return [[VLCLibraryFavoritesViewController alloc] initWithLibraryWindow:VLCMain.sharedInstance.libraryWindow];
+ };
+ self.internalLibraryViewPresenter = ^(VLCLibraryAbstractSegmentViewController * const controller) {
+ [(VLCLibraryFavoritesViewController *)controller presentFavoritesView];
+ };
+ self.internalSaveViewModePreference = ^(const NSInteger viewMode) {
+ VLCLibraryWindowPersistentPreferences.sharedInstance.favoritesLibraryViewMode = viewMode;
+ };
+ self.internalGetViewModePreference = ^{
+ return VLCLibraryWindowPersistentPreferences.sharedInstance.favoritesLibraryViewMode;
+ };
+ self.internalToolbarDisplayFlags = standardLibraryViewToolbarDisplayFlags;
+ }
+ return self;
+}
+
+ at end
// MARK: - Video library view segments
@@ -815,6 +855,7 @@ NSArray<NSString *> *defaultBookmarkedLocations()
return @[
[[VLCLibraryHomeSegment alloc] init],
[[VLCLibraryHeaderSegment alloc] initWithDisplayString:_NS("Library")],
+ [[VLCLibraryFavoritesSegment alloc] init],
[[VLCLibraryVideoSegment alloc] init],
[[VLCLibraryMusicSegment alloc] init],
[[VLCLibraryPlaylistSegment alloc] init],
@@ -830,6 +871,8 @@ NSArray<NSString *> *defaultBookmarkedLocations()
switch (segmentType) {
case VLCLibraryHomeSegmentType:
return [[VLCLibraryHomeSegment alloc] init];
+ case VLCLibraryFavoritesSegmentType:
+ return [[VLCLibraryFavoritesSegment alloc] init];
case VLCLibraryVideoSegmentType:
return [[VLCLibraryVideoSegment alloc] init];
case VLCLibraryShowsVideoSubSegmentType:
=====================================
modules/gui/macosx/library/VLCLibraryWindowPersistentPreferences.h
=====================================
@@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (class, readonly) VLCLibraryWindowPersistentPreferences *sharedInstance;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment homeLibraryViewMode;
+ at property (readwrite, nonatomic) VLCLibraryViewModeSegment favoritesLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment videoLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment showsLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment albumLibraryViewMode;
=====================================
modules/gui/macosx/library/VLCLibraryWindowPersistentPreferences.m
=====================================
@@ -25,6 +25,7 @@
NSString * const VLCLibraryWindowPreferencePrefix = @"VLCLibraryWindow";
NSString * const VLCLibraryHomeLibraryViewModePreferenceKey = @"HomeLibraryViewMode";
+NSString * const VLCLibraryFavoritesLibraryViewModePreferenceKey = @"FavoritesLibraryViewMode";
NSString * const VLCLibraryVideoLibraryViewModePreferenceKey = @"VideoLibraryViewMode";
NSString * const VLCLibraryShowsLibraryViewModePreferenceKey = @"ShowsLibraryViewMode";
NSString * const VLCLibraryAlbumLibraryViewModePreferenceKey = @"AlbumLibraryViewMode";
@@ -97,6 +98,17 @@ static VLCLibraryWindowPersistentPreferences *sharedInstance = nil;
value:homeLibraryViewMode];
}
+- (VLCLibraryViewModeSegment)favoritesLibraryViewMode
+{
+ return [self libraryViewModePreferenceWithKey:VLCLibraryFavoritesLibraryViewModePreferenceKey];
+}
+
+- (void)setFavoritesLibraryViewMode:(VLCLibraryViewModeSegment)favoritesLibraryViewMode
+{
+ [self setLibraryWindowViewModePreferenceWithKey:VLCLibraryFavoritesLibraryViewModePreferenceKey
+ value:favoritesLibraryViewMode];
+}
+
- (VLCLibraryViewModeSegment)videoLibraryViewMode
{
return [self libraryViewModePreferenceWithKey:VLCLibraryVideoLibraryViewModePreferenceKey];
=====================================
modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesDataSource.h
=====================================
@@ -0,0 +1,55 @@
+/*****************************************************************************
+ * VLCLibraryFavoritesDataSource.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+
+#import "library/VLCLibraryCollectionViewDataSource.h"
+#import "library/VLCLibraryMasterDetailViewTableViewDataSource.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at class VLCLibraryModel;
+
+extern NSString * const VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification;
+
+typedef NS_ENUM(NSUInteger, VLCLibraryFavoritesSection) {
+ VLCLibraryFavoritesSectionVideoMedia = 0,
+ VLCLibraryFavoritesSectionAudioMedia,
+ VLCLibraryFavoritesSectionAlbums,
+ VLCLibraryFavoritesSectionArtists,
+ VLCLibraryFavoritesSectionGenres,
+ VLCLibraryFavoritesSectionCount
+};
+
+ at interface VLCLibraryFavoritesDataSource : NSObject <VLCLibraryCollectionViewDataSource, VLCLibraryMasterDetailViewTableViewDataSource>
+
+ at property (readwrite, weak) VLCLibraryModel *libraryModel;
+ at property (readwrite, weak) NSCollectionView *collectionView;
+ at property (readwrite, weak) NSTableView *masterTableView;
+ at property (readwrite, weak) NSTableView *detailTableView;
+
+- (void)reloadData;
+- (NSInteger)rowForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem;
+
+ at end
+
+NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesDataSource.m
=====================================
@@ -0,0 +1,450 @@
+/*****************************************************************************
+ * VLCLibraryFavoritesDataSource.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import "VLCLibraryFavoritesDataSource.h"
+
+#import "library/VLCLibraryCollectionViewFlowLayout.h"
+#import "library/VLCLibraryCollectionViewItem.h"
+#import "library/VLCLibraryCollectionViewMediaItemSupplementaryDetailView.h"
+#import "library/VLCLibraryCollectionViewSupplementaryElementView.h"
+#import "library/VLCLibraryModel.h"
+#import "library/VLCLibraryDataTypes.h"
+#import "library/VLCLibraryRepresentedItem.h"
+#import "library/VLCLibraryTableCellView.h"
+
+#import "views/VLCImageView.h"
+
+#import "main/CompatibilityFixes.h"
+#import "main/VLCMain.h"
+
+#import "extensions/NSIndexSet+VLCAdditions.h"
+#import "extensions/NSString+Helpers.h"
+#import "extensions/NSPasteboardItem+VLCAdditions.h"
+
+NSString * const VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification = @"VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification";
+
+ at interface VLCLibraryFavoritesDataSource ()
+{
+ NSArray *_favoriteVideoMediaArray;
+ NSArray *_favoriteAudioMediaArray;
+ NSArray *_favoriteAlbumsArray;
+ NSArray *_favoriteArtistsArray;
+ NSArray *_favoriteGenresArray;
+ VLCLibraryCollectionViewFlowLayout *_collectionViewFlowLayout;
+ NSArray<NSNumber *> *_visibleSectionMapping; // Maps visible sections to VLCLibraryFavoritesSection values
+}
+
+ at end
+
+ at implementation VLCLibraryFavoritesDataSource
+
+- (instancetype)init
+{
+ self = [super init];
+ if (self) {
+ [self connect];
+ }
+ return self;
+}
+
+- (NSArray *)arrayForSection:(VLCLibraryFavoritesSection)section
+{
+ switch (section) {
+ case VLCLibraryFavoritesSectionVideoMedia:
+ return _favoriteVideoMediaArray;
+ case VLCLibraryFavoritesSectionAudioMedia:
+ return _favoriteAudioMediaArray;
+ case VLCLibraryFavoritesSectionAlbums:
+ return _favoriteAlbumsArray;
+ case VLCLibraryFavoritesSectionArtists:
+ return _favoriteArtistsArray;
+ case VLCLibraryFavoritesSectionGenres:
+ return _favoriteGenresArray;
+ default:
+ return @[];
+ }
+}
+
+- (NSString *)titleForSection:(VLCLibraryFavoritesSection)section
+{
+ switch (section) {
+ case VLCLibraryFavoritesSectionVideoMedia:
+ return _NS("Favorite Videos");
+ case VLCLibraryFavoritesSectionAudioMedia:
+ return _NS("Favorite Music");
+ case VLCLibraryFavoritesSectionAlbums:
+ return _NS("Favorite Albums");
+ case VLCLibraryFavoritesSectionArtists:
+ return _NS("Favorite Artists");
+ case VLCLibraryFavoritesSectionGenres:
+ return _NS("Favorite Genres");
+ default:
+ return @"";
+ }
+}
+
+- (VLCMediaLibraryParentGroupType)parentTypeForSection:(VLCLibraryFavoritesSection)section
+{
+ switch (section) {
+ case VLCLibraryFavoritesSectionVideoMedia:
+ return VLCMediaLibraryParentGroupTypeVideoLibrary;
+ case VLCLibraryFavoritesSectionAudioMedia:
+ return VLCMediaLibraryParentGroupTypeAudioLibrary;
+ case VLCLibraryFavoritesSectionAlbums:
+ return VLCMediaLibraryParentGroupTypeAlbum;
+ case VLCLibraryFavoritesSectionArtists:
+ return VLCMediaLibraryParentGroupTypeArtist;
+ case VLCLibraryFavoritesSectionGenres:
+ return VLCMediaLibraryParentGroupTypeGenre;
+ default:
+ return VLCMediaLibraryParentGroupTypeUnknown;
+ }
+}
+
+- (VLCLibraryFavoritesSection)sectionForVisibleIndex:(NSInteger)visibleIndex
+{
+ if (visibleIndex < 0 || visibleIndex >= _visibleSectionMapping.count) {
+ return VLCLibraryFavoritesSectionCount; // Invalid
+ }
+ return (VLCLibraryFavoritesSection)[_visibleSectionMapping[visibleIndex] integerValue];
+}
+
+- (NSInteger)visibleIndexForSection:(VLCLibraryFavoritesSection)section
+{
+ NSNumber * const sectionNumber = @(section);
+ NSUInteger index = [_visibleSectionMapping indexOfObject:sectionNumber];
+ return index == NSNotFound ? -1 : (NSInteger)index;
+}
+
+- (void)updateVisibleSectionMapping
+{
+ NSMutableArray<NSNumber *> * const visibleSections = [NSMutableArray array];
+
+ for (NSUInteger i = 0; i < VLCLibraryFavoritesSectionCount; i++) {
+ NSArray * const sectionArray = [self arrayForSection:i];
+ if (sectionArray.count > 0) {
+ [visibleSections addObject:@(i)];
+ }
+ }
+
+ _visibleSectionMapping = [visibleSections copy];
+}
+
+- (NSUInteger)indexOfMediaItem:(const NSUInteger)libraryId inArray:(NSArray const *)array
+{
+ return [array indexOfObjectPassingTest:^BOOL(id<VLCMediaLibraryItemProtocol> const findMediaItem, const NSUInteger idx, BOOL * const stop) {
+ NSAssert(findMediaItem != nil, @"Collection should not contain nil media items");
+ return findMediaItem.libraryID == libraryId;
+ }];
+}
+
+- (id<VLCMediaLibraryItemProtocol>)createGroupDescriptorForSection:(VLCLibraryFavoritesSection)section
+{
+ return [[VLCMediaLibraryDummyItem alloc] initWithDisplayString:[self titleForSection:section]
+ withMediaItems:[self arrayForSection:section]];
+}
+
+#pragma mark - Notification handlers
+
+- (void)libraryModelFavoriteVideoMediaListReset:(NSNotification * const)notification
+{
+ [self reloadData];
+}
+
+- (void)libraryModelFavoriteAudioMediaListReset:(NSNotification * const)notification
+{
+ [self reloadData];
+}
+
+- (void)libraryModelFavoriteAlbumsListReset:(NSNotification * const)notification
+{
+ [self reloadData];
+}
+
+- (void)libraryModelFavoriteArtistsListReset:(NSNotification * const)notification
+{
+ [self reloadData];
+}
+
+- (void)libraryModelFavoriteGenresListReset:(NSNotification * const)notification
+{
+ [self reloadData];
+}
+
+#pragma mark - VLCLibraryDataSource
+
+- (void)connect
+{
+ NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter;
+
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelFavoriteVideoMediaListReset:)
+ name:VLCLibraryModelFavoriteVideoMediaListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelFavoriteAudioMediaListReset:)
+ name:VLCLibraryModelFavoriteAudioMediaListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelFavoriteAlbumsListReset:)
+ name:VLCLibraryModelFavoriteAlbumsListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelFavoriteArtistsListReset:)
+ name:VLCLibraryModelFavoriteArtistsListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelFavoriteGenresListReset:)
+ name:VLCLibraryModelFavoriteGenresListReset
+ object:nil];
+
+ [self reloadData];
+}
+
+- (void)disconnect
+{
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+}
+
+- (void)reloadData
+{
+ if (!_libraryModel) {
+ return;
+ }
+
+ [_collectionViewFlowLayout resetLayout];
+
+ _favoriteVideoMediaArray = [self.libraryModel listOfFavoriteVideoMedia];
+ _favoriteAudioMediaArray = [self.libraryModel listOfFavoriteAudioMedia];
+ _favoriteAlbumsArray = [self.libraryModel listOfFavoriteAlbums];
+ _favoriteArtistsArray = [self.libraryModel listOfFavoriteArtists];
+ _favoriteGenresArray = [self.libraryModel listOfFavoriteGenres];
+
+ [self updateVisibleSectionMapping];
+
+ if (self.masterTableView.dataSource == self) {
+ [self.masterTableView reloadData];
+ }
+ if (self.detailTableView.dataSource == self) {
+ [self.detailTableView reloadData];
+ }
+ if (self.collectionView.dataSource == self) {
+ [self.collectionView reloadData];
+ }
+
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification
+ object:self
+ userInfo:nil];
+}
+
+#pragma mark - NSTableViewDataSource (Master-Detail View)
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
+{
+ if (tableView == self.masterTableView) {
+ return _visibleSectionMapping.count;
+ } else if (tableView == self.detailTableView && self.masterTableView.selectedRow > -1) {
+ const VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:self.masterTableView.selectedRow];
+ return [self arrayForSection:section].count;
+ }
+
+ return 0;
+}
+
+- (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row
+{
+ const id<VLCMediaLibraryItemProtocol> libraryItem = [self libraryItemAtRow:row forTableView:tableView];
+ return [NSPasteboardItem pasteboardItemWithLibraryItem:libraryItem];
+}
+
+- (id<VLCMediaLibraryItemProtocol>)libraryItemAtRow:(NSInteger)row
+ forTableView:(NSTableView *)tableView
+{
+ if (tableView == self.masterTableView) {
+ // For master table, return a group descriptor object
+ if (row >= 0 && row < _visibleSectionMapping.count) {
+ const VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:row];
+ return [self createGroupDescriptorForSection:section];
+ }
+ } else if (tableView == self.detailTableView && self.masterTableView.selectedRow > -1) {
+ VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:self.masterTableView.selectedRow];
+ NSArray * const sectionArray = [self arrayForSection:section];
+ if (row >= 0 && row < sectionArray.count) {
+ return sectionArray[row];
+ }
+ }
+
+ return nil;
+}
+
+- (NSInteger)rowForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
+{
+ if (libraryItem == nil) {
+ return NSNotFound;
+ }
+
+ // Search through all visible sections
+ for (NSNumber * const sectionNumber in _visibleSectionMapping) {
+ const VLCLibraryFavoritesSection section = (VLCLibraryFavoritesSection)[sectionNumber integerValue];
+ NSArray * const sectionArray = [self arrayForSection:section];
+ const NSInteger index = [self indexOfMediaItem:libraryItem.libraryID inArray:sectionArray];
+ if (index != NSNotFound) {
+ return index;
+ }
+ }
+
+ return NSNotFound;
+}
+
+- (VLCMediaLibraryParentGroupType)currentParentType
+{
+ if (self.masterTableView.selectedRow > -1) {
+ const VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:self.masterTableView.selectedRow];
+ return [self parentTypeForSection:section];
+ }
+ return VLCMediaLibraryParentGroupTypeVideoLibrary; // Default fallback
+}
+
+#pragma mark - NSCollectionViewDataSource
+
+- (NSInteger)numberOfSectionsInCollectionView:(NSCollectionView *)collectionView
+{
+ return _visibleSectionMapping.count;
+}
+
+- (NSInteger)collectionView:(NSCollectionView *)collectionView
+ numberOfItemsInSection:(NSInteger)section
+{
+ const VLCLibraryFavoritesSection favoritesSection = [self sectionForVisibleIndex:section];
+ return [self arrayForSection:favoritesSection].count;
+}
+
+- (NSCollectionViewItem *)collectionView:(NSCollectionView *)collectionView
+ itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath
+{
+ VLCLibraryCollectionViewItem * const viewItem =
+ [collectionView makeItemWithIdentifier:VLCLibraryCellIdentifier forIndexPath:indexPath];
+
+ const VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:indexPath.section];
+ const VLCMediaLibraryParentGroupType parentType = [self parentTypeForSection:section];
+ const id<VLCMediaLibraryItemProtocol> item =
+ [self libraryItemAtIndexPath:indexPath forCollectionView:collectionView];
+
+ VLCLibraryRepresentedItem * const representedItem =
+ [[VLCLibraryRepresentedItem alloc] initWithItem:item parentType:parentType];
+ viewItem.representedItem = representedItem;
+ return viewItem;
+}
+
+- (NSView *)collectionView:(NSCollectionView *)collectionView
+viewForSupplementaryElementOfKind:(NSCollectionViewSupplementaryElementKind)kind
+ atIndexPath:(NSIndexPath *)indexPath
+{
+ if([kind isEqualToString:NSCollectionElementKindSectionHeader]) {
+ VLCLibraryCollectionViewSupplementaryElementView * const sectionHeadingView =
+ [collectionView makeSupplementaryViewOfKind:kind
+ withIdentifier:VLCLibrarySupplementaryElementViewIdentifier
+ forIndexPath:indexPath];
+
+ const VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:indexPath.section];
+ sectionHeadingView.stringValue = [self titleForSection:section];
+ return sectionHeadingView;
+
+ } else if ([kind isEqualToString:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind]) {
+ VLCLibraryCollectionViewMediaItemSupplementaryDetailView * const mediaItemSupplementaryDetailView =
+ [collectionView makeSupplementaryViewOfKind:kind
+ withIdentifier:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind
+ forIndexPath:indexPath];
+
+ const id<VLCMediaLibraryItemProtocol> item = [self libraryItemAtIndexPath:indexPath forCollectionView:collectionView];
+ VLCLibraryRepresentedItem * const representedItem = [[VLCLibraryRepresentedItem alloc] initWithItem:item parentType:self.currentParentType];
+
+ mediaItemSupplementaryDetailView.representedItem = representedItem;
+ mediaItemSupplementaryDetailView.selectedItem = [collectionView itemAtIndexPath:indexPath];
+ return mediaItemSupplementaryDetailView;
+ }
+
+ return nil;
+}
+
+#pragma mark - VLCLibraryCollectionViewDataSource
+
+- (id<VLCMediaLibraryItemProtocol>)libraryItemAtIndexPath:(NSIndexPath *)indexPath
+ forCollectionView:(NSCollectionView *)collectionView
+{
+ const VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:indexPath.section];
+ NSArray * const sectionArray = [self arrayForSection:section];
+
+ if (indexPath.item >= 0 && indexPath.item < sectionArray.count) {
+ return sectionArray[indexPath.item];
+ }
+
+ return nil;
+}
+
+- (NSIndexPath *)indexPathForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
+{
+ if (libraryItem == nil) {
+ return nil;
+ }
+
+ // Search through all visible sections
+ for (NSUInteger visibleIndex = 0; visibleIndex < _visibleSectionMapping.count; visibleIndex++) {
+ const VLCLibraryFavoritesSection section = (VLCLibraryFavoritesSection)[_visibleSectionMapping[visibleIndex] integerValue];
+ NSArray * const sectionArray = [self arrayForSection:section];
+ const NSInteger itemIndex = [self indexOfMediaItem:libraryItem.libraryID inArray:sectionArray];
+ if (itemIndex != NSNotFound) {
+ return [NSIndexPath indexPathForItem:itemIndex inSection:visibleIndex];
+ }
+ }
+
+ return nil;
+}
+
+- (NSArray<VLCLibraryRepresentedItem *> *)representedItemsAtIndexPaths:(NSSet<NSIndexPath *> *)indexPaths
+ forCollectionView:(NSCollectionView *)collectionView
+{
+ NSMutableArray<VLCLibraryRepresentedItem *> * const representedItems =
+ [NSMutableArray arrayWithCapacity:indexPaths.count];
+
+ for (NSIndexPath * const indexPath in indexPaths) {
+ const VLCLibraryFavoritesSection section = [self sectionForVisibleIndex:indexPath.section];
+ const VLCMediaLibraryParentGroupType parentType = [self parentTypeForSection:section];
+ const id<VLCMediaLibraryItemProtocol> libraryItem =
+ [self libraryItemAtIndexPath:indexPath forCollectionView:collectionView];
+
+ if (libraryItem) {
+ VLCLibraryRepresentedItem * const representedItem =
+ [[VLCLibraryRepresentedItem alloc] initWithItem:libraryItem parentType:parentType];
+ [representedItems addObject:representedItem];
+ }
+ }
+
+ return representedItems;
+}
+
+- (NSString *)supplementaryDetailViewKind
+{
+ return VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind;
+}
+
+ at end
=====================================
modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesViewController.h
=====================================
@@ -0,0 +1,54 @@
+/*****************************************************************************
+ * VLCLibraryFavoritesViewController.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+
+#import "library/VLCLibraryAbstractMediaLibrarySegmentViewController.h"
+#import "library/VLCLibraryDataTypes.h"
+#import "library/VLCLibraryItemPresentingCapable.h"
+
+ at class VLCLibraryCollectionView;
+ at class VLCLibraryWindow;
+ at class VLCLibraryFavoritesDataSource;
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at interface VLCLibraryFavoritesViewController : VLCLibraryAbstractMediaLibrarySegmentViewController<VLCLibraryItemPresentingCapable>
+
+ at property (readonly, weak) NSView *favoritesLibraryView;
+ at property (readonly, weak) NSSplitView *favoritesLibrarySplitView;
+ at property (readonly, weak) NSScrollView *favoritesLibraryCollectionViewScrollView;
+ at property (readonly, weak) VLCLibraryCollectionView *favoritesLibraryCollectionView;
+ at property (readonly, weak) NSScrollView *favoritesLibraryGroupSelectionTableViewScrollView;
+ at property (readonly, weak) NSTableView *favoritesLibraryGroupSelectionTableView;
+ at property (readonly, weak) NSScrollView *favoritesLibraryGroupsTableViewScrollView;
+ at property (readonly, weak) NSTableView *favoritesLibraryGroupsTableView;
+
+ at property (readwrite, strong) VLCLibraryFavoritesDataSource *libraryFavoritesDataSource;
+
+- (instancetype)initWithLibraryWindow:(VLCLibraryWindow *)libraryWindow;
+- (void)presentFavoritesView;
+- (void)presentLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem;
+
+ at end
+
+NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/library/favorites-library/VLCLibraryFavoritesViewController.m
=====================================
@@ -0,0 +1,363 @@
+/*****************************************************************************
+ * VLCLibraryFavoritesViewController.m MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import "VLCLibraryFavoritesViewController.h"
+
+#import "extensions/NSString+Helpers.h"
+#import "library/VLCLibraryCollectionView.h"
+#import "library/VLCLibraryCollectionViewDelegate.h"
+#import "library/VLCLibraryCollectionViewFlowLayout.h"
+#import "library/VLCLibraryCollectionViewItem.h"
+#import "library/VLCLibraryCollectionViewMediaItemSupplementaryDetailView.h"
+#import "library/VLCLibraryCollectionViewSupplementaryElementView.h"
+#import "library/VLCLibraryController.h"
+#import "library/VLCLibraryModel.h"
+#import "library/VLCLibrarySegment.h"
+#import "library/VLCLibraryMasterDetailViewTableViewDelegate.h"
+#import "library/VLCLibraryTableCellView.h"
+#import "library/VLCLibraryTwoPaneSplitViewDelegate.h"
+#import "library/VLCLibraryUIUnits.h"
+#import "library/VLCLibraryWindow.h"
+#import "library/VLCLibraryWindowPersistentPreferences.h"
+#import "library/favorites-library/VLCLibraryFavoritesDataSource.h"
+#import "main/VLCMain.h"
+
+ at interface VLCLibraryFavoritesViewController ()
+{
+ VLCLibraryMasterDetailViewTableViewDelegate *_favoritesLibraryTableViewDelegate;
+ VLCLibraryTwoPaneSplitViewDelegate *_splitViewDelegate;
+ VLCLibraryCollectionViewDelegate *_collectionViewDelegate;
+ VLCLibraryCollectionViewFlowLayout *_collectionViewLayout;
+ NSArray<NSLayoutConstraint *> *_internalPlaceholderImageViewSizeConstraints;
+
+ id<VLCMediaLibraryItemProtocol> _awaitingPresentingLibraryItem;
+}
+
+ at property (readwrite, weak) NSView *favoritesLibraryView;
+ at property (readwrite, weak) NSSplitView *favoritesLibrarySplitView;
+ at property (readwrite, weak) NSScrollView *favoritesLibraryCollectionViewScrollView;
+ at property (readwrite, weak) VLCLibraryCollectionView *favoritesLibraryCollectionView;
+ at property (readwrite, weak) NSScrollView *favoritesLibraryGroupSelectionTableViewScrollView;
+ at property (readwrite, weak) NSTableView *favoritesLibraryGroupSelectionTableView;
+ at property (readwrite, weak) NSScrollView *favoritesLibraryGroupsTableViewScrollView;
+ at property (readwrite, weak) NSTableView *favoritesLibraryGroupsTableView;
+
+ at end
+
+ at implementation VLCLibraryFavoritesViewController
+
+- (instancetype)initWithLibraryWindow:(VLCLibraryWindow *)libraryWindow
+{
+ self = [super initWithLibraryWindow:libraryWindow];
+
+ if (self) {
+ _favoritesLibraryTableViewDelegate = [[VLCLibraryMasterDetailViewTableViewDelegate alloc] init];
+ _splitViewDelegate = [[VLCLibraryTwoPaneSplitViewDelegate alloc] init];
+
+ [self setupPropertiesFromLibraryWindow:libraryWindow];
+ [self setupTableViews];
+ [self setupCollectionView];
+ [self setupFavoritesDataSource];
+ [self setupFavoritesPlaceholderView];
+ [self setupFavoritesLibraryViews];
+ [self setupNotifications];
+ }
+
+ return self;
+}
+
+- (void)presentFavoritesView
+{
+ [self updatePresentedFavoritesView];
+}
+
+- (void)setupPropertiesFromLibraryWindow:(VLCLibraryWindow *)libraryWindow
+{
+ NSParameterAssert(libraryWindow);
+ _favoritesLibraryView = libraryWindow.videoLibraryView;
+ _favoritesLibrarySplitView = libraryWindow.videoLibrarySplitView;
+ _favoritesLibraryCollectionViewScrollView = libraryWindow.videoLibraryCollectionViewScrollView;
+ _favoritesLibraryCollectionView = libraryWindow.videoLibraryCollectionView;
+ _favoritesLibraryGroupSelectionTableViewScrollView = libraryWindow.videoLibraryGroupSelectionTableViewScrollView;
+ _favoritesLibraryGroupSelectionTableView = libraryWindow.videoLibraryGroupSelectionTableView;
+ _favoritesLibraryGroupsTableViewScrollView = libraryWindow.videoLibraryGroupsTableViewScrollView;
+ _favoritesLibraryGroupsTableView = libraryWindow.videoLibraryGroupsTableView;
+}
+
+- (void)setupTableViews
+{
+ self.favoritesLibrarySplitView.delegate = _splitViewDelegate;
+ [_splitViewDelegate resetDefaultSplitForSplitView:self.favoritesLibrarySplitView];
+
+ NSNib * const tableCellViewNib =
+ [[NSNib alloc] initWithNibNamed:NSStringFromClass(VLCLibraryTableCellView.class)
+ bundle:nil];
+ [self.favoritesLibraryGroupsTableView registerNib:tableCellViewNib
+ forIdentifier:@"VLCLibraryTableViewCellIdentifier"];
+ [self.favoritesLibraryGroupSelectionTableView registerNib:tableCellViewNib
+ forIdentifier:@"VLCLibraryTableViewCellIdentifier"];
+}
+
+- (void)setupCollectionView
+{
+ _collectionViewLayout = [[VLCLibraryCollectionViewFlowLayout alloc] init];
+
+ const CGFloat collectionItemSpacing = VLCLibraryUIUnits.collectionViewItemSpacing;
+ const NSEdgeInsets collectionViewSectionInset = VLCLibraryUIUnits.collectionViewSectionInsets;
+ _collectionViewLayout.headerReferenceSize = VLCLibraryCollectionViewSupplementaryElementView.defaultHeaderSize;
+ _collectionViewLayout.minimumLineSpacing = collectionItemSpacing;
+ _collectionViewLayout.minimumInteritemSpacing = collectionItemSpacing;
+ _collectionViewLayout.sectionInset = collectionViewSectionInset;
+
+ NSCollectionView * const collectionView = self.favoritesLibraryCollectionView;
+ collectionView.collectionViewLayout = _collectionViewLayout;
+
+ _collectionViewDelegate = [[VLCLibraryCollectionViewDelegate alloc] init];
+ collectionView.delegate = _collectionViewDelegate;
+
+ [collectionView registerClass:VLCLibraryCollectionViewItem.class
+ forItemWithIdentifier:VLCLibraryCellIdentifier];
+
+ [collectionView registerClass:VLCLibraryCollectionViewSupplementaryElementView.class
+ forSupplementaryViewOfKind:NSCollectionElementKindSectionHeader
+ withIdentifier:VLCLibrarySupplementaryElementViewIdentifier];
+
+ NSString * const mediaItemSupplementaryDetailViewString =
+ NSStringFromClass(VLCLibraryCollectionViewMediaItemSupplementaryDetailView.class);
+ NSNib * const mediaItemSupplementaryDetailViewNib =
+ [[NSNib alloc] initWithNibNamed:mediaItemSupplementaryDetailViewString bundle:nil];
+
+ [collectionView registerNib:mediaItemSupplementaryDetailViewNib
+ forSupplementaryViewOfKind:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind
+ withIdentifier:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewIdentifier];
+}
+
+- (void)setupFavoritesDataSource
+{
+ _libraryFavoritesDataSource = [[VLCLibraryFavoritesDataSource alloc] init];
+ self.libraryFavoritesDataSource.libraryModel = VLCMain.sharedInstance.libraryController.libraryModel;
+ self.libraryFavoritesDataSource.collectionView = self.favoritesLibraryCollectionView;
+ self.libraryFavoritesDataSource.masterTableView = self.favoritesLibraryGroupsTableView;
+ self.libraryFavoritesDataSource.detailTableView = self.favoritesLibraryGroupSelectionTableView;
+}
+
+- (void)setupFavoritesLibraryViews
+{
+ _favoritesLibraryGroupsTableView.rowHeight = VLCLibraryUIUnits.mediumTableViewRowHeight;
+ _favoritesLibraryGroupSelectionTableView.rowHeight = VLCLibraryUIUnits.mediumTableViewRowHeight;
+
+ const NSEdgeInsets defaultInsets = VLCLibraryUIUnits.libraryViewScrollViewContentInsets;
+ const NSEdgeInsets scrollerInsets = VLCLibraryUIUnits.libraryViewScrollViewScrollerInsets;
+
+ _favoritesLibraryCollectionViewScrollView.automaticallyAdjustsContentInsets = NO;
+ _favoritesLibraryCollectionViewScrollView.contentInsets = defaultInsets;
+ _favoritesLibraryCollectionViewScrollView.scrollerInsets = scrollerInsets;
+
+ _favoritesLibraryGroupsTableViewScrollView.automaticallyAdjustsContentInsets = NO;
+ _favoritesLibraryGroupsTableViewScrollView.contentInsets = defaultInsets;
+ _favoritesLibraryGroupsTableViewScrollView.scrollerInsets = scrollerInsets;
+ _favoritesLibraryGroupSelectionTableViewScrollView.automaticallyAdjustsContentInsets = NO;
+ _favoritesLibraryGroupSelectionTableViewScrollView.contentInsets = defaultInsets;
+ _favoritesLibraryGroupSelectionTableViewScrollView.scrollerInsets = scrollerInsets;
+}
+
+- (void)setupFavoritesPlaceholderView
+{
+ _internalPlaceholderImageViewSizeConstraints = @[
+ [NSLayoutConstraint constraintWithItem:self.placeholderImageView
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
+ multiplier:0.f
+ constant:182.f],
+ [NSLayoutConstraint constraintWithItem:self.placeholderImageView
+ attribute:NSLayoutAttributeHeight
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
+ multiplier:0.f
+ constant:114.f],
+ ];
+}
+
+- (void)setupNotifications
+{
+ NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter;
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelUpdated:)
+ name:VLCLibraryModelFavoriteVideoMediaListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelUpdated:)
+ name:VLCLibraryModelFavoriteAudioMediaListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelUpdated:)
+ name:VLCLibraryModelFavoriteAlbumsListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelUpdated:)
+ name:VLCLibraryModelFavoriteArtistsListReset
+ object:nil];
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelUpdated:)
+ name:VLCLibraryModelFavoriteGenresListReset
+ object:nil];
+}
+
+#pragma mark - VLCLibraryAbstractMediaLibrarySegmentViewController
+
+- (NSArray<NSLayoutConstraint *> *)placeholderImageViewSizeConstraints
+{
+ return _internalPlaceholderImageViewSizeConstraints;
+}
+
+- (id<VLCLibraryDataSource>)currentDataSource
+{
+ return self.libraryFavoritesDataSource;
+}
+
+#pragma mark - Public methods
+
+- (void)updatePresentedFavoritesView
+{
+ self.favoritesLibraryCollectionView.dataSource = self.libraryFavoritesDataSource;
+
+ self.favoritesLibraryGroupsTableView.dataSource = self.libraryFavoritesDataSource;
+ self.favoritesLibraryGroupsTableView.target = self.libraryFavoritesDataSource;
+ self.favoritesLibraryGroupsTableView.delegate = _favoritesLibraryTableViewDelegate;
+
+ self.favoritesLibraryGroupSelectionTableView.dataSource = self.libraryFavoritesDataSource;
+ self.favoritesLibraryGroupSelectionTableView.target = self.libraryFavoritesDataSource;
+ self.favoritesLibraryGroupSelectionTableView.delegate = _favoritesLibraryTableViewDelegate;
+
+ [self.libraryFavoritesDataSource reloadData];
+
+ if ([self hasFavoriteItems]) {
+ const VLCLibraryViewModeSegment viewModeSegment = VLCLibraryWindowPersistentPreferences.sharedInstance.favoritesLibraryViewMode;
+ [self presentFavoritesLibraryView:viewModeSegment];
+ } else if (self.libraryFavoritesDataSource.libraryModel.filterString.length > 0) {
+ [self.libraryWindow displayNoResultsMessage];
+ } else {
+ [self presentPlaceholderFavoritesView];
+ }
+}
+
+- (BOOL)hasFavoriteItems
+{
+ VLCLibraryModel * const libraryModel = self.libraryFavoritesDataSource.libraryModel;
+ return libraryModel.numberOfFavoriteVideoMedia > 0 ||
+ libraryModel.numberOfFavoriteAudioMedia > 0 ||
+ libraryModel.numberOfFavoriteAlbums > 0 ||
+ libraryModel.numberOfFavoriteArtists > 0 ||
+ libraryModel.numberOfFavoriteGenres > 0;
+}
+
+- (void)presentFavoritesCollectionView
+{
+ [self.libraryWindow displayLibraryView:self.favoritesLibraryView];
+ self.favoritesLibraryCollectionViewScrollView.hidden = NO;
+}
+
+- (void)presentPlaceholderFavoritesView
+{
+ [self.libraryWindow displayLibraryPlaceholderViewWithImage:[NSImage imageNamed:@"placeholder-favorites"]
+ usingConstraints:self.placeholderImageViewSizeConstraints
+ displayingMessage:_NS("Your favorite items will appear here.\nMark items as favorites to see them in this view.")];
+}
+
+- (void)presentFavoritesLibraryView:(VLCLibraryViewModeSegment)viewModeSegment
+{
+ [self.libraryWindow displayLibraryView:self.favoritesLibraryView];
+ if (viewModeSegment == VLCLibraryGridViewModeSegment) {
+ self.favoritesLibrarySplitView.hidden = YES;
+ self.favoritesLibraryCollectionViewScrollView.hidden = NO;
+ } else if (viewModeSegment == VLCLibraryListViewModeSegment) {
+ self.favoritesLibrarySplitView.hidden = NO;
+ self.favoritesLibraryCollectionViewScrollView.hidden = YES;
+ } else {
+ NSAssert(false, @"View mode must be grid or list mode");
+ }
+}
+
+- (void)presentLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
+{
+ if (libraryItem == nil) {
+ return;
+ }
+
+ _awaitingPresentingLibraryItem = libraryItem;
+
+ const VLCLibraryViewModeSegment viewModeSegment = VLCLibraryWindowPersistentPreferences.sharedInstance.favoritesLibraryViewMode;
+
+ if (viewModeSegment == VLCLibraryGridViewModeSegment) {
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(presentLibraryItemWaitForCollectionViewDataSourceFinished:)
+ name:VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification
+ object:self.libraryFavoritesDataSource];
+ } else if (viewModeSegment == VLCLibraryListViewModeSegment) {
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(presentLibraryItemWaitForTableViewDataSourceFinished:)
+ name:VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification
+ object:self.libraryFavoritesDataSource];
+ } else {
+ NSAssert(false, @"View mode must be grid or list mode");
+ }
+}
+
+- (void)presentLibraryItemWaitForCollectionViewDataSourceFinished:(NSNotification *)notification
+{
+ [NSNotificationCenter.defaultCenter removeObserver:self
+ name:VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification
+ object:self.libraryFavoritesDataSource];
+
+ _awaitingPresentingLibraryItem = nil;
+}
+
+- (void)presentLibraryItemWaitForTableViewDataSourceFinished:(NSNotification *)notification
+{
+ [NSNotificationCenter.defaultCenter removeObserver:self
+ name:VLCLibraryFavoritesDataSourceDisplayedCollectionChangedNotification
+ object:self.libraryFavoritesDataSource];
+
+ const NSInteger rowForLibraryItem = [self.libraryFavoritesDataSource rowForLibraryItem:_awaitingPresentingLibraryItem];
+ if (rowForLibraryItem != NSNotFound) {
+ NSIndexSet * const indexSet = [NSIndexSet indexSetWithIndex:rowForLibraryItem];
+ [self.favoritesLibraryGroupsTableView selectRowIndexes:indexSet byExtendingSelection:NO];
+ [self.favoritesLibraryGroupsTableView scrollRowToVisible:rowForLibraryItem];
+ }
+
+ _awaitingPresentingLibraryItem = nil;
+}
+
+#pragma mark - Notification handlers
+
+- (void)libraryModelUpdated:(NSNotification *)notification
+{
+ NSParameterAssert(notification);
+ if (self.libraryWindow.librarySegmentType == VLCLibraryFavoritesSegmentType) {
+ [self updatePresentedFavoritesView];
+ }
+}
+
+ at end
=====================================
modules/misc/medialibrary/medialibrary.cpp
=====================================
@@ -793,6 +793,14 @@ int MediaLibrary::Control( int query, va_list args )
return VLC_EGENERIC;
if ( folder->setFavorite( favorite ) == false )
return VLC_EGENERIC;
+
+ vlc_ml_event_t ev;
+ ev.i_type = VLC_ML_EVENT_FAVORITES_CHANGED;
+ ev.favorites_changed.i_entity_id = folder->id();
+ ev.favorites_changed.i_entity_type = VLC_ML_PARENT_FOLDER;
+ ev.favorites_changed.b_favorite = favorite;
+ m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
+
return VLC_SUCCESS;
}
case VLC_ML_ARTIST_SET_FAVORITE:
@@ -804,6 +812,14 @@ int MediaLibrary::Control( int query, va_list args )
return VLC_EGENERIC;
if ( artist->setFavorite( favorite ) == false )
return VLC_EGENERIC;
+
+ vlc_ml_event_t ev;
+ ev.i_type = VLC_ML_EVENT_FAVORITES_CHANGED;
+ ev.favorites_changed.i_entity_id = artistId;
+ ev.favorites_changed.i_entity_type = VLC_ML_PARENT_ARTIST;
+ ev.favorites_changed.b_favorite = favorite;
+ m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
+
return VLC_SUCCESS;
}
case VLC_ML_ALBUM_SET_FAVORITE:
@@ -815,6 +831,14 @@ int MediaLibrary::Control( int query, va_list args )
return VLC_EGENERIC;
if ( album->setFavorite( favorite ) == false )
return VLC_EGENERIC;
+
+ vlc_ml_event_t ev;
+ ev.i_type = VLC_ML_EVENT_FAVORITES_CHANGED;
+ ev.favorites_changed.i_entity_id = albumId;
+ ev.favorites_changed.i_entity_type = VLC_ML_PARENT_ALBUM;
+ ev.favorites_changed.b_favorite = favorite;
+ m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
+
return VLC_SUCCESS;
}
case VLC_ML_GENRE_SET_FAVORITE:
@@ -826,6 +850,14 @@ int MediaLibrary::Control( int query, va_list args )
return VLC_EGENERIC;
if ( genre->setFavorite( favorite ) == false)
return VLC_EGENERIC;
+
+ vlc_ml_event_t ev;
+ ev.i_type = VLC_ML_EVENT_FAVORITES_CHANGED;
+ ev.favorites_changed.i_entity_id = genreId;
+ ev.favorites_changed.i_entity_type = VLC_ML_PARENT_GENRE;
+ ev.favorites_changed.b_favorite = favorite;
+ m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
+
return VLC_SUCCESS;
}
case VLC_ML_PLAYLIST_SET_FAVORITE:
@@ -837,6 +869,14 @@ int MediaLibrary::Control( int query, va_list args )
return VLC_EGENERIC;
if ( playlist->setFavorite( favorite ) == false )
return VLC_EGENERIC;
+
+ vlc_ml_event_t ev;
+ ev.i_type = VLC_ML_EVENT_FAVORITES_CHANGED;
+ ev.favorites_changed.i_entity_id = playlistId;
+ ev.favorites_changed.i_entity_type = VLC_ML_PARENT_PLAYLIST;
+ ev.favorites_changed.b_favorite = favorite;
+ m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
+
return VLC_SUCCESS;
}
default:
@@ -1247,6 +1287,246 @@ int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_l
*( va_arg( args, size_t* ) ) = query ? query->count() : 0;
break;
}
+ case VLC_ML_LIST_FAVORITE_MEDIA:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchMedia( psz_pattern, &favParams );
+ else
+ query = m_ml->mediaFiles( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_media_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_MEDIA:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchMedia( psz_pattern, &favParams );
+ else
+ query = m_ml->mediaFiles( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_FAVORITE_VIDEOS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchVideo( psz_pattern, &favParams );
+ else
+ query = m_ml->videoFiles( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_media_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_VIDEOS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchVideo( psz_pattern, &favParams );
+ else
+ query = m_ml->videoFiles( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_FAVORITE_AUDIOS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAudio( psz_pattern, &favParams );
+ else
+ query = m_ml->audioFiles( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_media_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_AUDIOS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAudio( psz_pattern, &favParams );
+ else
+ query = m_ml->audioFiles( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_FAVORITE_ALBUMS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IAlbum> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAlbums( psz_pattern, &favParams );
+ else
+ query = m_ml->albums( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_album_list_t, vlc_ml_album_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_album_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_ALBUMS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IAlbum> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchAlbums( psz_pattern, &favParams );
+ else
+ query = m_ml->albums( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_FAVORITE_ARTISTS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IArtist> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchArtists( psz_pattern, medialibrary::ArtistIncluded::All, &favParams );
+ else
+ query = m_ml->artists( medialibrary::ArtistIncluded::All, &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_artist_list_t, vlc_ml_artist_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_artist_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_ARTISTS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IArtist> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchArtists( psz_pattern, medialibrary::ArtistIncluded::All, &favParams );
+ else
+ query = m_ml->artists( medialibrary::ArtistIncluded::All, &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_FAVORITE_GENRES:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IGenre> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchGenre( psz_pattern, &favParams );
+ else
+ query = m_ml->genres( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_genre_list_t,vlc_ml_genre_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_genre_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_GENRES:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IGenre> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchGenre( psz_pattern, &favParams );
+ else
+ query = m_ml->genres( &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_FAVORITE_PLAYLISTS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IPlaylist> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchPlaylists( psz_pattern, medialibrary::PlaylistType::All, &favParams );
+ else
+ query = m_ml->playlists( medialibrary::PlaylistType::All, &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_playlist_list_t, vlc_ml_playlist_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_playlist_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_PLAYLISTS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IPlaylist> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchPlaylists( psz_pattern, medialibrary::PlaylistType::All, &favParams );
+ else
+ query = m_ml->playlists( medialibrary::PlaylistType::All, &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
+ case VLC_ML_LIST_FAVORITE_FOLDERS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IFolder> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchFolders( psz_pattern, medialibrary::IMedia::Type::Unknown, &favParams );
+ else
+ query = m_ml->folders( medialibrary::IMedia::Type::Unknown, &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ auto res = ml_convert_list<vlc_ml_folder_list_t, vlc_ml_folder_t>(
+ query->items( nbItems, offset ) );
+ *va_arg( args, vlc_ml_folder_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_FAVORITE_FOLDERS:
+ {
+ medialibrary::QueryParameters favParams = paramsPtr ? *paramsPtr : medialibrary::QueryParameters{};
+ favParams.favoriteOnly = true;
+ medialibrary::Query<medialibrary::IFolder> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchFolders( psz_pattern, medialibrary::IMedia::Type::Unknown, &favParams );
+ else
+ query = m_ml->folders( medialibrary::IMedia::Type::Unknown, &favParams );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ *va_arg( args, size_t* ) = query->count();
+ break;
+ }
case VLC_ML_LIST_SUBFOLDERS:
{
const auto parent = m_ml->folder( va_arg( args, int64_t ) );
@@ -1640,6 +1920,14 @@ int MediaLibrary::controlMedia( int query, va_list args )
bool favorite = va_arg( args, int );
if ( m->setFavorite( favorite ) == false )
return VLC_EGENERIC;
+
+ vlc_ml_event_t ev;
+ ev.i_type = VLC_ML_EVENT_FAVORITES_CHANGED;
+ ev.favorites_changed.i_entity_id = mediaId;
+ ev.favorites_changed.i_entity_type = VLC_ML_PARENT_UNKNOWN; // Media doesn't have a parent type in VLC_ML_PARENT_*
+ ev.favorites_changed.b_favorite = favorite;
+ m_vlc_ml->cbs->pf_send_event( m_vlc_ml, &ev );
+
return VLC_SUCCESS;
}
case VLC_ML_MEDIA_ADD_BOOKMARK:
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d913e5c0a5625f930c8ef8fc67aa2524637d973e...8d0a5ccc2a5b32d92827739a263477bf8504377b
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d913e5c0a5625f930c8ef8fc67aa2524637d973e...8d0a5ccc2a5b32d92827739a263477bf8504377b
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list