[vlc-commits] [Git][videolan/vlc][master] 9 commits: medialibrary: Implement functions to acquire movies' media items
Steve Lhomme (@robUx4)
gitlab at videolan.org
Tue Jul 15 08:40:47 UTC 2025
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
3c4e671f by Claudio Cambra at 2025-07-15T08:03:36+00:00
medialibrary: Implement functions to acquire movies' media items
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
39c78ae2 by Claudio Cambra at 2025-07-15T08:03:36+00:00
macosx: Implement movies in library model
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
8afb3182 by Claudio Cambra at 2025-07-15T08:03:36+00:00
macosx: Make VLCMediaLibraryMovie compliant with the media library item protocol
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
5ffa77ba by Claudio Cambra at 2025-07-15T08:03:36+00:00
macosx: Add a movies data source
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
31655ebf by Claudio Cambra at 2025-07-15T08:03:36+00:00
macosx: Publicly declare movies reset notification
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
64bd1f68 by Claudio Cambra at 2025-07-15T08:03:36+00:00
macosx: Set up and configure movies view in library video view controller
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
57395561 by Claudio Cambra at 2025-07-15T08:03:36+00:00
macosx: Add movies video library subsegment
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
38e0307c by Claudio Cambra at 2025-07-15T08:03:36+00:00
macosx: Fix handling of groupings table view when handling different video library views
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
85a47054 by Claudio Cambra at 2025-07-15T08:03:36+00:00
medialibrary: Add algorithm include to fix builds on some platforms
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/video-library/VLCLibraryMoviesDataSource.h
- + modules/gui/macosx/library/video-library/VLCLibraryMoviesDataSource.m
- modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.h
- modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.m
- modules/misc/medialibrary/medialibrary.cpp
Changes:
=====================================
extras/package/macosx/VLC.xcodeproj/project.pbxproj
=====================================
@@ -174,6 +174,7 @@
53F399802AC6D6B400B86241 /* VLCLibraryHomeViewVideoCarouselContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 53F3997F2AC6D6B400B86241 /* VLCLibraryHomeViewVideoCarouselContainerView.m */; };
53F7B2372D4D499400F3B38F /* VLCFileDragRecognisingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 53F7B2362D4D499400F3B38F /* VLCFileDragRecognisingView.m */; };
53F7B23C2D545FCF00F3B38F /* VLCLibraryAbstractMediaLibrarySegmentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53F7B23B2D545FCF00F3B38F /* VLCLibraryAbstractMediaLibrarySegmentViewController.m */; };
+ 53F89E372E23DC4C008080C1 /* VLCLibraryMoviesDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 53F89E362E23DC4C008080C1 /* VLCLibraryMoviesDataSource.m */; };
6B0292E61F43256300A50082 /* VLCBottomBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B0292E51F43256300A50082 /* VLCBottomBarView.m */; };
6B0AB0F01F1AC8B3003A1B4E /* VLCPlaybackProgressSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B0AB0ED1F1AC8B3003A1B4E /* VLCPlaybackProgressSlider.m */; };
6B0AB0F11F1AC8B3003A1B4E /* VLCPlaybackProgressSliderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B0AB0EF1F1AC8B3003A1B4E /* VLCPlaybackProgressSliderCell.m */; };
@@ -527,6 +528,8 @@
53F7B2362D4D499400F3B38F /* VLCFileDragRecognisingView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCFileDragRecognisingView.m; sourceTree = "<group>"; };
53F7B23A2D545FCF00F3B38F /* VLCLibraryAbstractMediaLibrarySegmentViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryAbstractMediaLibrarySegmentViewController.h; sourceTree = "<group>"; };
53F7B23B2D545FCF00F3B38F /* VLCLibraryAbstractMediaLibrarySegmentViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryAbstractMediaLibrarySegmentViewController.m; sourceTree = "<group>"; };
+ 53F89E352E23DC4C008080C1 /* VLCLibraryMoviesDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryMoviesDataSource.h; sourceTree = "<group>"; };
+ 53F89E362E23DC4C008080C1 /* VLCLibraryMoviesDataSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryMoviesDataSource.m; sourceTree = "<group>"; };
5CCED71014C0D4A90057F8D1 /* VLCExtensionsDialogProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCExtensionsDialogProvider.h; sourceTree = "<group>"; };
5CCED71114C0D4A90057F8D1 /* VLCExtensionsDialogProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCExtensionsDialogProvider.m; sourceTree = "<group>"; };
5CCED71214C0D4A90057F8D1 /* VLCExtensionsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCExtensionsManager.h; sourceTree = "<group>"; };
@@ -1724,6 +1727,8 @@
53B447EB293BB47A00857588 /* video-library */ = {
isa = PBXGroup;
children = (
+ 53F89E352E23DC4C008080C1 /* VLCLibraryMoviesDataSource.h */,
+ 53F89E362E23DC4C008080C1 /* VLCLibraryMoviesDataSource.m */,
5325720D2C4966630068DEC3 /* VLCLibraryShowsDataSource.h */,
5325720E2C4966630068DEC3 /* VLCLibraryShowsDataSource.m */,
536EFC37295E521600F4CB13 /* VLCLibraryVideoViewController.h */,
@@ -2406,6 +2411,7 @@
6B2EFC631F281A0900F3C0EA /* VLCVolumeSliderCell.m in Sources */,
5387FFF52A15127100A3A70A /* NSWindow+VLCAdditions.m in Sources */,
1C3113A71E508C6900D4DD76 /* VLCLogWindowController.m in Sources */,
+ 53F89E372E23DC4C008080C1 /* VLCLibraryMoviesDataSource.m in Sources */,
53F0E930299B17DF00491D49 /* VLCInputNodePathControl.m in Sources */,
1C3113A91E508C6900D4DD76 /* VLCDocumentController.m in Sources */,
1C3113AB1E508C6900D4DD76 /* VLCExtensionsDialogProvider.m in Sources */,
=====================================
include/vlc_media_library.h
=====================================
@@ -481,6 +481,8 @@ enum vlc_ml_list_queries
VLC_ML_COUNT_MEDIA, /**< arg1 (out): size_t* */
VLC_ML_LIST_VIDEOS, /**< arg1 (out): vlc_ml_media_list_t** */
VLC_ML_COUNT_VIDEOS, /**< arg1 (out): size_t* */
+ VLC_ML_LIST_MOVIES, /**< arg1 (out): vlc_ml_media_list_t** */
+ VLC_ML_COUNT_MOVIES, /**< arg1 (out): size_t* */
VLC_ML_LIST_AUDIOS, /**< arg1 (out): vlc_ml_media_list_t** */
VLC_ML_COUNT_AUDIOS, /**< arg1 (out): size_t* */
VLC_ML_LIST_ALBUMS, /**< arg1 (out): vlc_ml_album_list_t** */
@@ -1661,6 +1663,24 @@ static inline vlc_ml_show_list_t* vlc_ml_list_shows( vlc_medialibrary_t* p_ml, c
return res;
}
+static inline vlc_ml_media_list_t* vlc_ml_list_movies(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_MOVIES, params, &res) != VLC_SUCCESS)
+ return NULL;
+ return res;
+}
+
+static inline size_t vlc_ml_count_movies(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_MOVIES, params, &count) != VLC_SUCCESS)
+ return 0;
+ return count;
+}
+
static inline size_t vlc_ml_count_shows( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params )
{
vlc_assert( p_ml != NULL );
=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -257,6 +257,8 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/library/home-library/VLCLibraryHomeViewVideoContainerViewDataSource.m \
gui/macosx/library/home-library/VLCLibraryHomeViewVideoGridContainerView.h \
gui/macosx/library/home-library/VLCLibraryHomeViewVideoGridContainerView.m \
+ gui/macosx/library/video-library/VLCLibraryMoviesDataSource.h \
+ gui/macosx/library/video-library/VLCLibraryMoviesDataSource.m \
gui/macosx/library/video-library/VLCLibraryShowsDataSource.h \
gui/macosx/library/video-library/VLCLibraryShowsDataSource.m \
gui/macosx/library/video-library/VLCLibraryVideoViewController.h \
=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.h
=====================================
@@ -97,15 +97,6 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
@end
- at interface VLCMediaLibraryMovie : NSObject
-
-- (instancetype)initWithMovie:(struct vlc_ml_movie_t *)p_movie;
-
- at property (readonly) NSString *summary;
- at property (readonly) NSString *imdbID;
-
- at end
-
@interface VLCMediaLibraryShowEpisode : NSObject
- (instancetype)initWithShowEpisode:(struct vlc_ml_show_episode_t *)p_showEpisode;
@@ -117,8 +108,6 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
@end
-#pragma mark - Media library classes
-
@protocol VLCLocallyManipulableItemProtocol <NSObject>
- (void)revealInFinder;
@@ -154,6 +143,12 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
@end
+// Base abstract class with common implementations of properties used by media library items.
+// Do not use directly -- subclass to create new media library item types.
+ at interface VLCAbstractMediaLibraryItem : NSObject<VLCMediaLibraryItemProtocol>
+
+ at end
+
// Extended VLCMediaLibraryItemProtocol that includes additional properties for media library item
// audio groups (i.e. artists, genres, etc.)
@protocol VLCMediaLibraryAudioGroupProtocol <VLCMediaLibraryItemProtocol>
@@ -168,18 +163,14 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
@end
-// Base abstract class with common implementations of properties used by media library items.
-// Do not use directly -- subclass to create new media library item types.
- at interface VLCAbstractMediaLibraryItem : NSObject<VLCMediaLibraryItemProtocol>
-
- at end
-
// Like VLCAbstractMediaLibraryItem but with some additional functionality for audio groupings
// such as artists and genres. Do not use directly, subclass instead.
@interface VLCAbstractMediaLibraryAudioGroup : VLCAbstractMediaLibraryItem<VLCMediaLibraryAudioGroupProtocol>
@end
+#pragma mark - Media library classes
+
@interface VLCMediaLibraryArtist : VLCAbstractMediaLibraryAudioGroup<VLCMediaLibraryAudioGroupProtocol>
+ (nullable instancetype)artistWithID:(int64_t)artistID;
@@ -231,6 +222,15 @@ typedef NS_ENUM(NSUInteger, VLCMediaLibraryParentGroupType) {
@end
+ at interface VLCMediaLibraryMovie : VLCAbstractMediaLibraryItem <VLCMediaLibraryItemProtocol>
+
+- (instancetype)initWithMediaItem:(struct vlc_ml_media_t *)p_media;
+
+ at property (readonly) NSString *summary;
+ at property (readonly) NSString *imdbID;
+
+ at end
+
@interface VLCMediaLibraryGroup : VLCAbstractMediaLibraryItem
@property (readonly) NSString *name;
=====================================
modules/gui/macosx/library/VLCLibraryDataTypes.m
=====================================
@@ -263,20 +263,6 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
@end
- at implementation VLCMediaLibraryMovie
-
-- (instancetype)initWithMovie:(struct vlc_ml_movie_t *)p_movie
-{
- self = [super init];
- if (self && p_movie != NULL) {
- _summary = toNSStr(p_movie->psz_summary);
- _imdbID = toNSStr(p_movie->psz_imdb_id);
- }
- return self;
-}
-
- at end
-
@implementation VLCMediaLibraryShowEpisode
- (instancetype)initWithShowEpisode:(struct vlc_ml_show_episode_t *)p_showEpisode
@@ -1079,7 +1065,7 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
switch (p_mediaItem->i_subtype) {
case VLC_ML_MEDIA_SUBTYPE_MOVIE:
- _movie = [[VLCMediaLibraryMovie alloc] initWithMovie:&p_mediaItem->movie];
+ _movie = [[VLCMediaLibraryMovie alloc] initWithMediaItem:p_mediaItem];
break;
case VLC_ML_MEDIA_SUBTYPE_SHOW_EPISODE:
@@ -1634,6 +1620,48 @@ static NSString *genreArrayDisplayString(NSArray<VLCMediaLibraryGenre *> * const
@end
+ at implementation VLCMediaLibraryMovie
+
+- (instancetype)initWithMediaItem:(struct vlc_ml_media_t *)p_media
+{
+ self = [super init];
+ if (self && p_media != NULL) {
+ // Set up abstract item properties
+ self.libraryID = p_media->i_id;
+ self.smallArtworkGenerated = p_media->thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl != NULL;
+ self.smallArtworkMRL = self.smallArtworkGenerated ? toNSStr(p_media->thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl) : nil;
+ self.displayString = p_media->psz_title ? toNSStr(p_media->psz_title) : @"";
+ self.primaryDetailString = self.displayString;
+ self.secondaryDetailString = @"";
+ self.durationString = [NSString stringWithTime:p_media->i_duration / VLCMediaLibraryMediaItemDurationDenominator];
+
+ // Movie-specific
+ _summary = toNSStr(p_media->movie.psz_summary);
+ _imdbID = toNSStr(p_media->movie.psz_imdb_id);
+ }
+ return self;
+}
+
+- (NSArray<VLCMediaLibraryMediaItem *> *)mediaItems
+{
+ VLCMediaLibraryMediaItem *item = [VLCMediaLibraryMediaItem mediaItemForLibraryID:self.libraryID];
+ return item ? @[item] : @[];
+}
+
+- (VLCMediaLibraryMediaItem *)firstMediaItem
+{
+ return self.mediaItems.firstObject;
+}
+
+- (void)iterateMediaItemsWithBlock:(void (^)(VLCMediaLibraryMediaItem*))mediaItemBlock
+{
+ for (VLCMediaLibraryMediaItem *item in self.mediaItems) {
+ mediaItemBlock(item);
+ }
+}
+
+ at end
+
@implementation VLCMediaLibraryEntryPoint
- (instancetype)initWithEntryPoint:(struct vlc_ml_folder_t *)p_entryPoint
=====================================
modules/gui/macosx/library/VLCLibraryModel.h
=====================================
@@ -44,6 +44,7 @@ extern NSString * const VLCLibraryModelFavoriteGenresListReset;
extern NSString * const VLCLibraryModelRecentsMediaListReset;
extern NSString * const VLCLibraryModelRecentAudioMediaListReset;
extern NSString * const VLCLibraryModelListOfShowsReset;
+extern NSString * const VLCLibraryModelListOfMoviesReset;
extern NSString * const VLCLibraryModelListOfGroupsReset;
extern NSString * const VLCLibraryModelAudioMediaItemDeleted;
@@ -105,6 +106,9 @@ extern NSString * const VLCLibraryModelDiscoveryFailed;
@property (readonly) size_t numberOfShows;
@property (readonly) NSArray <VLCMediaLibraryShow *> *listOfShows;
+ at property (readonly) size_t numberOfMovies;
+ at property (readonly) NSArray <VLCMediaLibraryMovie *> *listOfMovies;
+
@property (readonly) size_t numberOfGroups;
@property (readonly) NSArray <VLCMediaLibraryGroup *> *listOfGroups;
=====================================
modules/gui/macosx/library/VLCLibraryModel.m
=====================================
@@ -45,6 +45,7 @@ NSString * const VLCLibraryModelFavoriteGenresListReset = @"VLCLibraryModelFavor
NSString * const VLCLibraryModelRecentsMediaListReset = @"VLCLibraryModelRecentsMediaListReset";
NSString * const VLCLibraryModelRecentAudioMediaListReset = @"VLCLibraryModelRecentAudioMediaListReset";
NSString * const VLCLibraryModelListOfShowsReset = @"VLCLibraryModelListOfShowsReset";
+NSString * const VLCLibraryModelListOfMoviesReset = @"VLCLibraryModelListOfMoviesReset";
NSString * const VLCLibraryModelListOfGroupsReset = @"VLCLibraryModelListOfGroupsReset";
NSString * const VLCLibraryModelPlaylistAdded = @"VLCLibraryModelPlaylistAdded";
@@ -90,6 +91,7 @@ NSString * const VLCLibraryModelDiscoveryFailed = @"VLCLibraryModelDiscoveryFail
size_t _initialArtistCount;
size_t _initialGenreCount;
size_t _initialShowCount;
+ size_t _initialMovieCount;
size_t _initialGroupCount;
size_t _initialRecentsCount;
size_t _initialRecentAudioCount;
@@ -109,6 +111,7 @@ NSString * const VLCLibraryModelDiscoveryFailed = @"VLCLibraryModelDiscoveryFail
@property (readwrite, atomic) NSArray *cachedGenres;
@property (readwrite, atomic) NSArray *cachedVideoMedia;
@property (readwrite, atomic) NSArray *cachedListOfShows;
+ at property (readwrite, atomic) NSArray *cachedListOfMovies;
@property (readwrite, atomic) NSArray *cachedListOfGroups;
@property (readwrite, atomic) NSArray *cachedRecentMedia;
@property (readwrite, atomic) NSArray *cachedRecentAudioMedia;
@@ -321,6 +324,7 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
self->_initialArtistCount = vlc_ml_count_artists(self->_p_mediaLibrary, &queryParameters, true);
self->_initialGenreCount = vlc_ml_count_genres(self->_p_mediaLibrary, &queryParameters);
self->_initialShowCount = vlc_ml_count_shows(self->_p_mediaLibrary, &queryParameters);
+ self->_initialMovieCount = vlc_ml_count_movies(self->_p_mediaLibrary, &queryParameters);
self->_initialGroupCount = vlc_ml_count_groups(self->_p_mediaLibrary, &queryParameters);
queryParameters.i_nbResults = self->_recentMediaLimit;
@@ -672,6 +676,34 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
});
}
+- (void)resetCachedListOfMovies
+{
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
+ vlc_ml_media_list_t * const p_movie_list = vlc_ml_list_movies(self->_p_mediaLibrary, NULL);
+ if (p_movie_list == NULL) {
+ return;
+ }
+ const size_t itemCount = p_movie_list->i_nb_items;
+ NSMutableArray * const mutableArray = [[NSMutableArray alloc] initWithCapacity:itemCount];
+ for (size_t x = 0; x < p_movie_list->i_nb_items; x++) {
+ vlc_ml_media_t * const p_vlc_media = &p_movie_list->p_items[x];
+ // Defensive: only add if subtype is MOVIE
+ if (p_vlc_media->i_subtype == VLC_ML_MEDIA_SUBTYPE_MOVIE) {
+ VLCMediaLibraryMovie * const movie = [[VLCMediaLibraryMovie alloc] initWithMediaItem:p_vlc_media];
+ if (movie) {
+ [mutableArray addObject:movie];
+ }
+ }
+ }
+ vlc_ml_media_list_release(p_movie_list);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.cachedListOfMovies = mutableArray.copy;
+ [self.changeDelegate notifyChange:VLCLibraryModelListOfMoviesReset withObject:self];
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryModelListOfMoviesReset object:self];
+ });
+ });
+}
+
- (size_t)numberOfShows
{
if (!_cachedListOfShows) {
@@ -682,6 +714,16 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
return _cachedListOfShows.count;
}
+- (size_t)numberOfMovies
+{
+ if (!_cachedListOfMovies) {
+ [self resetCachedListOfMovies];
+ // Return initial count here, otherwise it will return 0 on the first time
+ return _initialMovieCount;
+ }
+ return _cachedListOfMovies.count;
+}
+
- (NSArray<VLCMediaLibraryShow *> *)listOfShows
{
if (!_cachedListOfShows) {
@@ -690,6 +732,14 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
return _cachedListOfShows;
}
+- (NSArray<VLCMediaLibraryMovie *> *)listOfMovies
+{
+ if (!_cachedListOfMovies) {
+ [self resetCachedListOfMovies];
+ }
+ return _cachedListOfMovies;
+}
+
- (size_t)numberOfGroups
{
if (!_cachedListOfGroups) {
=====================================
modules/gui/macosx/library/VLCLibrarySegment.h
=====================================
@@ -37,6 +37,7 @@ typedef NS_ENUM(NSInteger, VLCLibrarySegmentType) {
VLCLibraryHeaderSegmentType,
VLCLibraryVideoSegmentType,
VLCLibraryShowsVideoSubSegmentType,
+ VLCLibraryMoviesVideoSubSegmentType,
VLCLibraryMusicSegmentType,
VLCLibraryArtistsMusicSubSegmentType,
VLCLibraryAlbumsMusicSubSegmentType,
=====================================
modules/gui/macosx/library/VLCLibrarySegment.m
=====================================
@@ -209,6 +209,43 @@ NSArray<NSString *> *defaultBookmarkedLocations()
@interface VLCLibraryVideoShowsSubSegment : VLCLibrarySegment
@end
+ at interface VLCLibraryVideoMoviesSubSegment : VLCLibrarySegment
+ at end
+
+ at implementation VLCLibraryVideoMoviesSubSegment
+
+- (instancetype)init
+{
+ self = [super initWithSegmentType:VLCLibraryMoviesVideoSubSegmentType];
+ if (self) {
+ self.internalDisplayString = _NS("Movies");
+ if (@available(macOS 11.0, *)) {
+ self.internalDisplayImage = [NSImage imageWithSystemSymbolName:@"film"
+ accessibilityDescription:@"Movies icon"];
+ } else {
+ self.internalDisplayImage = [NSImage imageNamed:@"sidebar-movie"];
+ self.internalDisplayImage.template = YES;
+ }
+ self.internalLibraryViewControllerClass = VLCLibraryVideoViewController.class;
+ self.internalLibraryViewControllerCreator = ^{
+ return [[VLCLibraryVideoViewController alloc] initWithLibraryWindow:VLCMain.sharedInstance.libraryWindow];
+ };
+ self.internalLibraryViewPresenter = ^(VLCLibraryAbstractSegmentViewController * const controller) {
+ [(VLCLibraryVideoViewController *)controller presentMoviesView];
+ };
+ self.internalSaveViewModePreference = ^(const NSInteger viewMode) {
+ VLCLibraryWindowPersistentPreferences.sharedInstance.moviesLibraryViewMode = viewMode;
+ };
+ self.internalGetViewModePreference = ^{
+ return VLCLibraryWindowPersistentPreferences.sharedInstance.moviesLibraryViewMode;
+ };
+ self.internalToolbarDisplayFlags = standardLibraryViewToolbarDisplayFlags;
+ }
+ return self;
+}
+
+ at end
+
@implementation VLCLibraryVideoShowsSubSegment
- (instancetype)init
@@ -275,7 +312,10 @@ NSArray<NSString *> *defaultBookmarkedLocations()
return VLCLibraryWindowPersistentPreferences.sharedInstance.videoLibraryViewMode;
};
self.internalToolbarDisplayFlags = standardLibraryViewToolbarDisplayFlags;
- self.internalChildNodes = @[[[VLCLibraryVideoShowsSubSegment alloc] init]];
+ self.internalChildNodes = @[
+ [[VLCLibraryVideoShowsSubSegment alloc] init],
+ [[VLCLibraryVideoMoviesSubSegment alloc] init]
+ ];
}
return self;
}
@@ -877,6 +917,8 @@ NSArray<NSString *> *defaultBookmarkedLocations()
return [[VLCLibraryVideoSegment alloc] init];
case VLCLibraryShowsVideoSubSegmentType:
return [[VLCLibraryVideoShowsSubSegment alloc] init];
+ case VLCLibraryMoviesVideoSubSegmentType:
+ return [[VLCLibraryVideoMoviesSubSegment alloc] init];
case VLCLibraryMusicSegmentType:
return [[VLCLibraryMusicSegment alloc] init];
case VLCLibraryArtistsMusicSubSegmentType:
@@ -923,6 +965,8 @@ NSArray<NSString *> *defaultBookmarkedLocations()
} else if (validMediaItem && mediaItem.mediaType == VLC_ML_MEDIA_TYPE_VIDEO) {
if (mediaItem.mediaSubType == VLC_ML_MEDIA_SUBTYPE_SHOW_EPISODE) {
return [VLCLibrarySegment segmentWithSegmentType:VLCLibraryShowsVideoSubSegmentType];
+ } else if (mediaItem.mediaSubType == VLC_ML_MEDIA_SUBTYPE_MOVIE) {
+ return [VLCLibrarySegment segmentWithSegmentType:VLCLibraryMoviesVideoSubSegmentType];
}
return [VLCLibrarySegment segmentWithSegmentType:VLCLibraryVideoSegmentType];
}
=====================================
modules/gui/macosx/library/VLCLibraryWindowPersistentPreferences.h
=====================================
@@ -34,6 +34,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (readwrite, nonatomic) VLCLibraryViewModeSegment favoritesLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment videoLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment showsLibraryViewMode;
+ at property (readwrite, nonatomic) VLCLibraryViewModeSegment moviesLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment albumLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment genreLibraryViewMode;
@property (readwrite, nonatomic) VLCLibraryViewModeSegment songsLibraryViewMode;
=====================================
modules/gui/macosx/library/VLCLibraryWindowPersistentPreferences.m
=====================================
@@ -28,6 +28,7 @@ NSString * const VLCLibraryHomeLibraryViewModePreferenceKey = @"HomeLibraryViewM
NSString * const VLCLibraryFavoritesLibraryViewModePreferenceKey = @"FavoritesLibraryViewMode";
NSString * const VLCLibraryVideoLibraryViewModePreferenceKey = @"VideoLibraryViewMode";
NSString * const VLCLibraryShowsLibraryViewModePreferenceKey = @"ShowsLibraryViewMode";
+NSString * const VLCLibraryMoviesLibraryViewModePreferenceKey = @"MoviesLibraryViewMode";
NSString * const VLCLibraryAlbumLibraryViewModePreferenceKey = @"AlbumLibraryViewMode";
NSString * const VLCLibraryGenreLibraryViewModePreferenceKey = @"GenreLibraryViewMode";
NSString * const VLCLibrarySongsLibraryViewModePreferenceKey = @"SongsLibraryViewMode";
@@ -131,6 +132,17 @@ static VLCLibraryWindowPersistentPreferences *sharedInstance = nil;
value:showsLibraryViewMode];
}
+- (VLCLibraryViewModeSegment)moviesLibraryViewMode
+{
+ return [self libraryViewModePreferenceWithKey:VLCLibraryMoviesLibraryViewModePreferenceKey];
+}
+
+- (void)setMoviesLibraryViewMode:(VLCLibraryViewModeSegment)moviesLibraryViewMode
+{
+ [self setLibraryWindowViewModePreferenceWithKey:VLCLibraryMoviesLibraryViewModePreferenceKey
+ value:moviesLibraryViewMode];
+}
+
- (VLCLibraryViewModeSegment)albumLibraryViewMode
{
return [self libraryViewModePreferenceWithKey:VLCLibraryAlbumLibraryViewModePreferenceKey];
=====================================
modules/gui/macosx/library/video-library/VLCLibraryMoviesDataSource.h
=====================================
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * VLCLibraryMoviesDataSource.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/VLCLibraryTableViewDataSource.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at class VLCLibraryModel;
+
+ at interface VLCLibraryMoviesDataSource : NSObject <VLCLibraryCollectionViewDataSource, VLCLibraryTableViewDataSource>
+
+ at property (readwrite, weak) VLCLibraryModel *libraryModel;
+ at property (readwrite, weak) NSCollectionView *collectionView;
+ at property (readwrite, weak) NSTableView *tableView;
+
+ at end
+
+NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/library/video-library/VLCLibraryMoviesDataSource.m
=====================================
@@ -0,0 +1,166 @@
+/*****************************************************************************
+ * VLCLibraryMoviesDataSource.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 "VLCLibraryMoviesDataSource.h"
+#import "library/VLCLibraryModel.h"
+#import "library/VLCLibraryRepresentedItem.h"
+#import "library/VLCLibraryCollectionViewMediaItemSupplementaryDetailView.h"
+#import "library/VLCLibraryCollectionViewItem.h"
+
+NSString * const VLCLibraryMoviesDataSourceDisplayedCollectionChangedNotification = @"VLCLibraryMoviesDataSourceDisplayedCollectionChangedNotification";
+
+ at implementation VLCLibraryMoviesDataSource
+
+- (instancetype)init
+{
+ self = [super init];
+ if (self) {
+ [self connect];
+ }
+ return self;
+}
+
+- (void)connect
+{
+ NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter;
+ [notificationCenter addObserver:self
+ selector:@selector(libraryModelMoviesListReset:)
+ name:VLCLibraryModelListOfMoviesReset
+ object:nil];
+ [self reloadData];
+}
+
+- (void)disconnect
+{
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+}
+
+- (void)libraryModelMoviesListReset:(NSNotification *)notification
+{
+ [self reloadData];
+}
+
+#pragma mark - NSCollectionViewDataSource
+
+- (NSInteger)numberOfSectionsInCollectionView:(NSCollectionView *)collectionView
+{
+ return 1;
+}
+
+- (NSInteger)collectionView:(NSCollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
+{
+ return self.libraryModel.numberOfMovies;
+}
+
+- (NSCollectionViewItem *)collectionView:(NSCollectionView *)collectionView itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath
+{
+ VLCLibraryCollectionViewItem * const item = [collectionView makeItemWithIdentifier:VLCLibraryCellIdentifier forIndexPath:indexPath];
+ const id<VLCMediaLibraryItemProtocol> movie = [self libraryItemAtIndexPath:indexPath forCollectionView:collectionView];
+ VLCLibraryRepresentedItem * const representedItem = [[VLCLibraryRepresentedItem alloc] initWithItem:movie parentType:self.currentParentType];
+ item.representedItem = representedItem;
+ return item;
+}
+
+- (NSView *)collectionView:(NSCollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
+{
+ if ([kind isEqualToString:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind]) {
+ VLCLibraryCollectionViewMediaItemSupplementaryDetailView * const detailView =
+ [collectionView makeSupplementaryViewOfKind:kind
+ withIdentifier:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind
+ forIndexPath:indexPath];
+ const id<VLCMediaLibraryItemProtocol> movie = [self libraryItemAtIndexPath:indexPath forCollectionView:collectionView];
+ VLCLibraryRepresentedItem * const representedItem = [[VLCLibraryRepresentedItem alloc] initWithItem:movie parentType:self.currentParentType];
+ detailView.representedItem = representedItem;
+ detailView.selectedItem = [collectionView itemAtIndexPath:indexPath];
+ return detailView;
+ }
+ return nil;
+}
+
+#pragma mark - VLCLibraryCollectionViewDataSource
+
+- (id<VLCMediaLibraryItemProtocol>)libraryItemAtIndexPath:(NSIndexPath *)indexPath forCollectionView:(NSCollectionView *)collectionView
+{
+ return self.libraryModel.listOfMovies[indexPath.item];
+}
+
+- (NSIndexPath *)indexPathForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
+{
+ const NSUInteger idx = [self.libraryModel.listOfMovies indexOfObject:libraryItem];
+ if (idx == NSNotFound) return nil;
+ return [NSIndexPath indexPathForItem:idx inSection:0];
+}
+
+- (NSArray<VLCLibraryRepresentedItem *> *)representedItemsAtIndexPaths:(NSSet<NSIndexPath *> *const)indexPaths forCollectionView:(NSCollectionView *)collectionView
+{
+ NSMutableArray * const items = [NSMutableArray arrayWithCapacity:indexPaths.count];
+ for (NSIndexPath * const indexPath in indexPaths) {
+ const id<VLCMediaLibraryItemProtocol> item = [self libraryItemAtIndexPath:indexPath forCollectionView:collectionView];
+ if (item) {
+ [items addObject:[[VLCLibraryRepresentedItem alloc] initWithItem:item parentType:self.currentParentType]];
+ }
+ }
+ return items;
+}
+
+- (NSString *)supplementaryDetailViewKind
+{
+ return VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind;
+}
+
+- (void)reloadData
+{
+ // Notify all attached views to reload their data
+ if (self.collectionView) {
+ [self.collectionView reloadData];
+ }
+ if (self.tableView) {
+ [self.tableView reloadData];
+ }
+ [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryMoviesDataSourceDisplayedCollectionChangedNotification
+ object:self
+ userInfo:nil];
+}
+
+#pragma mark - VLCLibraryTableViewDataSource
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
+{
+ return self.libraryModel.numberOfMovies;
+}
+
+- (id<VLCMediaLibraryItemProtocol>)libraryItemAtRow:(NSInteger)row forTableView:(nullable NSTableView *)tableView
+{
+ return self.libraryModel.listOfMovies[row];
+}
+
+- (NSInteger)rowForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
+{
+ return [self.libraryModel.listOfMovies indexOfObject:libraryItem];
+}
+
+- (VLCMediaLibraryParentGroupType)currentParentType
+{
+ return VLCMediaLibraryParentGroupTypeVideoLibrary;
+}
+
+ at end
=====================================
modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.h
=====================================
@@ -29,6 +29,7 @@
@class VLCLibraryWindow;
@class VLCLibraryVideoDataSource;
@class VLCLibraryShowsDataSource;
+ at class VLCLibraryMoviesDataSource;
@protocol VLCMediaLibraryItemProtocol;
@@ -48,10 +49,12 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly, nullable) VLCLibraryVideoDataSource *libraryVideoDataSource;
@property (readonly, nullable) VLCLibraryShowsDataSource *libraryShowsDataSource;
+ at property (readonly, nullable) VLCLibraryMoviesDataSource *libraryMoviesDataSource;
- (instancetype)initWithLibraryWindow:(VLCLibraryWindow *)libraryWindow;
- (void)presentVideoView;
- (void)presentShowsView;
+- (void)presentMoviesView;
- (void)presentLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem;
@end
=====================================
modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.m
=====================================
@@ -48,6 +48,7 @@
#import "library/video-library/VLCLibraryShowsDataSource.h"
#import "library/video-library/VLCLibraryVideoDataSource.h"
#import "library/video-library/VLCLibraryVideoTableViewDelegate.h"
+#import "library/video-library/VLCLibraryMoviesDataSource.h"
#import "main/VLCMain.h"
@@ -166,6 +167,15 @@
self.libraryShowsDataSource.detailTableView = self.videoLibraryGroupSelectionTableView;
}
+- (void)setupMoviesDataSource
+{
+ _libraryMoviesDataSource = [[VLCLibraryMoviesDataSource alloc] init];
+ self.libraryMoviesDataSource.libraryModel =
+ VLCMain.sharedInstance.libraryController.libraryModel;
+ self.libraryMoviesDataSource.tableView = self.videoLibraryGroupSelectionTableView;
+ self.libraryMoviesDataSource.collectionView = self.videoLibraryCollectionView;
+}
+
- (void)setupCollectionView
{
_collectionViewLayout = [[VLCLibraryCollectionViewFlowLayout alloc] init];
@@ -256,6 +266,8 @@
return self.libraryVideoDataSource;
} else if (librarySegmentType == VLCLibraryShowsVideoSubSegmentType) {
return self.libraryShowsDataSource;
+ } else if (librarySegmentType == VLCLibraryMoviesVideoSubSegmentType) {
+ return self.libraryMoviesDataSource;
} else {
return nil;
}
@@ -264,12 +276,14 @@
- (void)updatePresentedVideoLibraryView
{
_libraryShowsDataSource = nil;
+ _libraryMoviesDataSource = nil;
[self setupVideoDataSource];
self.videoLibraryCollectionView.dataSource = self.libraryVideoDataSource;
self.videoLibraryGroupsTableView.dataSource = self.libraryVideoDataSource;
self.videoLibraryGroupsTableView.target = self.libraryVideoDataSource;
self.videoLibraryGroupsTableView.delegate = _videoLibraryTableViewDelegate;
+ self.videoLibraryGroupsTableViewScrollView.hidden = NO;
self.videoLibraryGroupSelectionTableView.dataSource = self.libraryVideoDataSource;
self.videoLibraryGroupSelectionTableView.target = self.libraryVideoDataSource;
@@ -291,12 +305,14 @@
- (void)updatePresentedShowsLibraryView
{
_libraryVideoDataSource = nil;
+ _libraryMoviesDataSource = nil;
[self setupShowsDataSource];
self.videoLibraryCollectionView.dataSource = self.libraryShowsDataSource;
self.videoLibraryGroupsTableView.dataSource = self.libraryShowsDataSource;
self.videoLibraryGroupsTableView.target = self.libraryShowsDataSource;
self.videoLibraryGroupsTableView.delegate = _videoLibraryTableViewDelegate;
+ self.videoLibraryGroupsTableViewScrollView.hidden = NO;
self.videoLibraryGroupSelectionTableView.dataSource = self.libraryShowsDataSource;
self.videoLibraryGroupSelectionTableView.target = self.libraryShowsDataSource;
@@ -320,6 +336,40 @@
[self updatePresentedVideoLibraryView];
}
+- (void)updatePresentedMoviesLibraryView
+{
+ _libraryVideoDataSource = nil;
+ _libraryShowsDataSource = nil;
+ [self setupMoviesDataSource];
+ self.videoLibraryCollectionView.dataSource = self.libraryMoviesDataSource;
+
+ self.videoLibraryGroupsTableView.dataSource = nil;
+ self.videoLibraryGroupsTableView.target = nil;
+ self.videoLibraryGroupsTableView.delegate = nil;
+ self.videoLibraryGroupsTableViewScrollView.hidden = YES;
+
+ self.videoLibraryGroupSelectionTableView.dataSource = self.libraryMoviesDataSource;
+ self.videoLibraryGroupSelectionTableView.target = self.libraryMoviesDataSource;
+ self.videoLibraryGroupSelectionTableView.delegate = _videoLibraryTableViewDelegate;
+
+ [self.libraryMoviesDataSource reloadData];
+
+ const BOOL anyMovies = self.libraryMoviesDataSource.libraryModel.numberOfMovies > 0;
+ if (anyMovies) {
+ const VLCLibraryViewModeSegment viewModeSegment = VLCLibraryWindowPersistentPreferences.sharedInstance.moviesLibraryViewMode;
+ [self presentVideoLibraryView:viewModeSegment];
+ } else if (self.libraryMoviesDataSource.libraryModel.filterString.length > 0) {
+ [self.libraryWindow displayNoResultsMessage];
+ } else {
+ [self presentPlaceholderVideoLibraryView];
+ }
+}
+
+- (void)presentMoviesView
+{
+ [self updatePresentedMoviesLibraryView];
+}
+
- (void)presentShowsView
{
[self updatePresentedShowsLibraryView];
@@ -365,6 +415,12 @@
self.libraryWindow.videoViewController.view.hidden) {
[self updatePresentedShowsLibraryView];
+ } else if (self.libraryWindow.librarySegmentType == VLCLibraryMoviesVideoSubSegmentType &&
+ ((model.numberOfMovies == 0 && ![self.libraryTargetView.subviews containsObject:self.emptyLibraryView]) ||
+ (model.numberOfMovies > 0 && ![self.libraryTargetView.subviews containsObject:_videoLibraryView])) &&
+ self.libraryWindow.videoViewController.view.hidden) {
+
+ [self updatePresentedMoviesLibraryView];
}
}
=====================================
modules/misc/medialibrary/medialibrary.cpp
=====================================
@@ -43,6 +43,7 @@
#include <medialibrary/IBookmark.h>
#include <medialibrary/IFolder.h>
+#include <algorithm>
#include <sstream>
#include <initializer_list>
@@ -1019,6 +1020,49 @@ int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_l
*va_arg( args, size_t* ) = query->count();
break;
}
+ case VLC_ML_LIST_MOVIES:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchVideo( psz_pattern, paramsPtr );
+ else
+ query = m_ml->videoFiles( paramsPtr );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ // Filter for movies only
+ auto items = query->items(nbItems, offset);
+ items.erase(
+ std::remove_if(
+ items.begin(),
+ items.end(),
+ [](const auto& item) {
+ return item->subType() != medialibrary::IMedia::SubType::Movie;
+ }),
+ items.end());
+ const auto res = ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>(items);
+ *va_arg( args, vlc_ml_media_list_t**) = res;
+ break;
+ }
+ case VLC_ML_COUNT_MOVIES:
+ {
+ medialibrary::Query<medialibrary::IMedia> query;
+ if ( psz_pattern != nullptr )
+ query = m_ml->searchVideo( psz_pattern, paramsPtr );
+ else
+ query = m_ml->videoFiles( paramsPtr );
+ if ( query == nullptr )
+ return VLC_EGENERIC;
+ // Filter for movies only
+ const auto items = query->items( 0, 0 );
+ const size_t count = std::count_if(
+ items.cbegin(),
+ items.cend(),
+ [](const auto& item) {
+ return item->subType() == medialibrary::IMedia::SubType::Movie;
+ });
+ *va_arg( args, size_t* ) = count;
+ break;
+ }
case VLC_ML_LIST_AUDIOS:
{
medialibrary::Query<medialibrary::IMedia> query;
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0351db7f2fd75d634f637d8091c6592caab7c5cf...85a47054a970c100302bf68cb69b724c676e9175
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0351db7f2fd75d634f637d8091c6592caab7c5cf...85a47054a970c100302bf68cb69b724c676e9175
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