[vlc-commits] [Git][videolan/vlc][master] 12 commits: macosx: Add starter VLCMediaLibraryFolderObserver

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Fri Feb 14 05:47:03 UTC 2025



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
2765dfca by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Add starter VLCMediaLibraryFolderObserver

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
16317f0b by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Given an NSURL, create an fs event stream in media library folder observer

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
992b3e40 by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Generate and store folder observers for each ml entry point in library model

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
d1349cb0 by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Emit notification when receiving fs event on observed folder

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
1145cbdc by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Fix general issues around core foundation types handling in folder observer

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
5e28d09b by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Do not do anything if fs events count is 0

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
0e9077fd by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Refresh observers on monitored folders refresh

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
f76058b3 by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Ensure message is valid before trying to remove it

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
3a5ec9e8 by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Refresh monitored folder in library controller after receiving notification

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
ac292499 by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Store and retrieve last fs event id to get events since last check

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
b33b59d4 by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Fix warning regarding const fs context

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
b3f9613c by Claudio Cambra at 2025-02-14T05:23:26+00:00
macosx: Prevent crash from event ids being deallocated during access

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -


7 changed files:

- extras/package/macosx/VLC.xcodeproj/project.pbxproj
- modules/gui/macosx/Makefile.am
- modules/gui/macosx/library/VLCLibraryController.m
- modules/gui/macosx/library/VLCLibraryModel.m
- + modules/gui/macosx/library/VLCMediaLibraryFolderObserver.h
- + modules/gui/macosx/library/VLCMediaLibraryFolderObserver.m
- modules/gui/macosx/views/VLCStatusNotifierView.m


Changes:

=====================================
extras/package/macosx/VLC.xcodeproj/project.pbxproj
=====================================
@@ -143,6 +143,7 @@
 		53B447CA2939823E00857588 /* VLCLibrarySongsTableViewSongPlayingTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 53B447C92939823E00857588 /* VLCLibrarySongsTableViewSongPlayingTableCellView.m */; };
 		53B447F9293BB47B00857588 /* VLCLibraryVideoDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 53B447F3293BB47A00857588 /* VLCLibraryVideoDataSource.m */; };
 		53B447FA293BB47B00857588 /* VLCLibraryVideoGroupDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 53B447F4293BB47A00857588 /* VLCLibraryVideoGroupDescriptor.m */; };
+		53BDE19F2D5C8D1C00254560 /* VLCMediaLibraryFolderObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BDE19E2D5C8D1C00254560 /* VLCMediaLibraryFolderObserver.m */; };
 		53C1EF8B2B466B13001AEEF5 /* VLCLibraryHomeViewVideoGridContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C1EF852B466B13001AEEF5 /* VLCLibraryHomeViewVideoGridContainerView.m */; };
 		53C1EF8C2B466B13001AEEF5 /* VLCLibraryHomeViewStackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C1EF862B466B13001AEEF5 /* VLCLibraryHomeViewStackViewController.m */; };
 		53CBE30B2C159A4D006BF2E3 /* VLCNoResultsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 53CBE30A2C159A4D006BF2E3 /* VLCNoResultsLabel.m */; };
@@ -445,6 +446,8 @@
 		53B447F0293BB47A00857588 /* VLCLibraryVideoGroupDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCLibraryVideoGroupDescriptor.h; sourceTree = "<group>"; };
 		53B447F3293BB47A00857588 /* VLCLibraryVideoDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryVideoDataSource.m; sourceTree = "<group>"; };
 		53B447F4293BB47A00857588 /* VLCLibraryVideoGroupDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryVideoGroupDescriptor.m; sourceTree = "<group>"; };
+		53BDE19D2D5C8D1C00254560 /* VLCMediaLibraryFolderObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCMediaLibraryFolderObserver.h; sourceTree = "<group>"; };
+		53BDE19E2D5C8D1C00254560 /* VLCMediaLibraryFolderObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCMediaLibraryFolderObserver.m; sourceTree = "<group>"; };
 		53BFB1E02A6A72160065EA7A /* VLCLibraryAudioGroupHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = VLCLibraryAudioGroupHeaderView.xib; sourceTree = "<group>"; };
 		53C1EF842B466B13001AEEF5 /* VLCLibraryHomeViewContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCLibraryHomeViewContainerView.h; sourceTree = "<group>"; };
 		53C1EF852B466B13001AEEF5 /* VLCLibraryHomeViewVideoGridContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryHomeViewVideoGridContainerView.m; sourceTree = "<group>"; };
@@ -1431,6 +1434,8 @@
 				53D021332CC225C6003C008F /* VLCLibraryWindowTitlesSidebarViewController.m */,
 				53F020A22A91D4A100E79705 /* VLCLibraryWindowToolbarDelegate.h */,
 				53F020A32A91D4A100E79705 /* VLCLibraryWindowToolbarDelegate.m */,
+				53BDE19D2D5C8D1C00254560 /* VLCMediaLibraryFolderObserver.h */,
+				53BDE19E2D5C8D1C00254560 /* VLCMediaLibraryFolderObserver.m */,
 			);
 			path = library;
 			sourceTree = "<group>";
@@ -2361,6 +2366,7 @@
 				7D0F64062202047900FDB91F /* VLCLibraryCollectionViewItem.m in Sources */,
 				7D713D322201AE350042BEB7 /* VLCLibraryWindow.m in Sources */,
 				7D22A8F422BC14F80063ECD2 /* VLCLibrarySortingMenuController.m in Sources */,
+				53BDE19F2D5C8D1C00254560 /* VLCMediaLibraryFolderObserver.m in Sources */,
 				1C3113C91E508C6900D4DD76 /* VLCOutput.m in Sources */,
 				535F1BC02B4892A200C78D98 /* VLCLibraryHomeViewAudioCarouselContainerView.m in Sources */,
 				7D445D842202524D00263D34 /* VLCPlayQueueItem.m in Sources */,


=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -190,6 +190,8 @@ libmacosx_plugin_la_SOURCES = \
 	gui/macosx/library/VLCLibraryWindowTitlesSidebarViewController.m \
 	gui/macosx/library/VLCLibraryWindowToolbarDelegate.h \
 	gui/macosx/library/VLCLibraryWindowToolbarDelegate.m \
+	gui/macosx/library/VLCMediaLibraryFolderObserver.h \
+	gui/macosx/library/VLCMediaLibraryFolderObserver.m \
 	gui/macosx/library/groups-library/VLCLibraryGroupsDataSource.h \
 	gui/macosx/library/groups-library/VLCLibraryGroupsDataSource.m \
 	gui/macosx/library/groups-library/VLCLibraryGroupsViewController.h \


=====================================
modules/gui/macosx/library/VLCLibraryController.m
=====================================
@@ -30,6 +30,7 @@
 #import "library/VLCInputItem.h"
 #import "library/VLCLibraryModel.h"
 #import "library/VLCLibraryDataTypes.h"
+#import "library/VLCMediaLibraryFolderObserver.h"
 
 #import <vlc_media_library.h>
 
@@ -60,6 +61,10 @@ typedef int (*folder_action_f)(vlc_medialibrary_t*, const char*);
                                       selector:@selector(playbackStateChanged:)
                                           name:VLCPlayerStateChanged
                                         object:nil];
+        [defaultNotificationCenter addObserver:self
+                                      selector:@selector(monitoredFolderChanged:)
+                                          name:VLCMediaLibraryFolderFSEvent
+                                        object:nil];
     }
     return self;
 }
@@ -87,6 +92,12 @@ typedef int (*folder_action_f)(vlc_medialibrary_t*, const char*);
     }
 }
 
+- (void)monitoredFolderChanged:(NSNotification *)notification
+{
+    NSURL * const folderURL = (NSURL *)notification.object;
+    [self reloadFolderWithFileURL:folderURL];
+}
+
 - (int)appendItemToPlayQueue:(VLCMediaLibraryMediaItem *)mediaItem playImmediately:(BOOL)playImmediately
 {
     if (!_p_libraryInstance) {


=====================================
modules/gui/macosx/library/VLCLibraryModel.m
=====================================
@@ -22,6 +22,8 @@
 
 #import "VLCLibraryModel.h"
 
+#import "VLCMediaLibraryFolderObserver.h"
+
 #import "extensions/NSArray+VLCAdditions.h"
 #import "extensions/NSString+Helpers.h"
 
@@ -94,6 +96,8 @@ NSString * const VLCLibraryModelDiscoveryFailed = @"VLCLibraryModelDiscoveryFail
     dispatch_queue_t _groupCacheModificationQueue;
 }
 
+ at property (readwrite) NSArray<VLCMediaLibraryFolderObserver *> *folderObservers;
+
 @property (readwrite, atomic) NSArray *cachedAudioMedia;
 @property (readwrite, atomic) NSArray *cachedArtists;
 @property (readwrite, atomic) NSArray *cachedAlbums;
@@ -305,6 +309,8 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
             self->_initialRecentsCount = vlc_ml_count_video_history(self->_p_mediaLibrary, &queryParameters);
             self->_initialRecentAudioCount = vlc_ml_count_audio_history(self->_p_mediaLibrary, &queryParameters);
         });
+
+        [self resetCachedListOfMonitoredFolders];
     }
     return self;
 }
@@ -765,18 +771,29 @@ static void libraryCallback(void *p_data, const vlc_ml_event_t *p_event)
             return;
         }
 
-        NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:pp_entrypoints->i_nb_items];
+        NSMutableArray * const mutableArray =
+            [[NSMutableArray alloc] initWithCapacity:pp_entrypoints->i_nb_items];
+        NSMutableArray * const mutableObservers =
+            [[NSMutableArray alloc] initWithCapacity:pp_entrypoints->i_nb_items];
+
         for (size_t x = 0; x < pp_entrypoints->i_nb_items; x++) {
-            VLCMediaLibraryEntryPoint *entryPoint = [[VLCMediaLibraryEntryPoint alloc] initWithEntryPoint:&pp_entrypoints->p_items[x]];
+            VLCMediaLibraryEntryPoint * const entryPoint =
+                [[VLCMediaLibraryEntryPoint alloc] initWithEntryPoint:&pp_entrypoints->p_items[x]];
             if (entryPoint) {
                 [mutableArray addObject:entryPoint];
+
+                NSURL * const url = [NSURL URLWithString:entryPoint.MRL];
+                VLCMediaLibraryFolderObserver * const observer =
+                    [[VLCMediaLibraryFolderObserver alloc] initWithURL:url];
+                [mutableObservers addObject:observer];
             }
         }
 
         vlc_ml_folder_list_release(pp_entrypoints);
 
         dispatch_async(dispatch_get_main_queue(), ^{
-            self.cachedListOfMonitoredFolders = [mutableArray copy];
+            self.cachedListOfMonitoredFolders = mutableArray.copy;
+            self.folderObservers = mutableObservers.copy;
             [self.changeDelegate notifyChange:VLCLibraryModelListOfMonitoredFoldersUpdated withObject:self];
         });
     });


=====================================
modules/gui/macosx/library/VLCMediaLibraryFolderObserver.h
=====================================
@@ -0,0 +1,37 @@
+/*****************************************************************************
+ * VLCMediaLibraryFolderObserver.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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+extern NSString * const VLCMediaLibraryFolderFSEvent;
+
+ at interface VLCMediaLibraryFolderObserver : NSObject
+
+ at property (readonly) NSURL *url;
+
+- (instancetype)initWithURL:(NSURL *)url;
+
+ at end
+
+NS_ASSUME_NONNULL_END


=====================================
modules/gui/macosx/library/VLCMediaLibraryFolderObserver.m
=====================================
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * VLCMediaLibraryFolderObserver.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 "VLCMediaLibraryFolderObserver.h"
+
+NSString * const VLCMediaLibraryFolderFSEvent = @"VLCMediaLibraryFolderFSEvent";
+NSString * const VLCMediaLibraryFolderFSLastEventIdPrefix = @"VLCMediaLibraryFolderFSLastEventId_";
+
+ at interface VLCMediaLibraryFolderObserver ()
+
+ at property (readonly) FSEventStreamRef stream;
+ at property (readonly) CFStringRef urlPathRef;
+ at property (readonly) CFArrayRef pathsToWatch;
+ at property (readonly) NSString *defaultsKey;
+
+ at end
+
+void fsEventCallback(ConstFSEventStreamRef streamRef,
+                     void *clientCallBackInfo,
+                     size_t numEvents,
+                     void *eventPaths,
+                     const FSEventStreamEventFlags eventFlags[],
+                     const FSEventStreamEventId eventIds[])
+{
+    if (numEvents == 0) {
+        return;
+    }
+    
+    VLCMediaLibraryFolderObserver * const observer =
+        (__bridge VLCMediaLibraryFolderObserver *)clientCallBackInfo;
+    NSNumber * const eventId = @(eventIds[0]);
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [NSUserDefaults.standardUserDefaults setObject:eventId forKey:observer.defaultsKey];
+        [NSNotificationCenter.defaultCenter postNotificationName:VLCMediaLibraryFolderFSEvent
+                                                          object:observer.url];
+    });
+}
+
+ at implementation VLCMediaLibraryFolderObserver
+
+- (instancetype)initWithURL:(NSURL *)url
+{
+    self = [super init];
+    if (self) {
+        _url = url;
+        _urlPathRef = (__bridge_retained CFStringRef)url.path;
+        _pathsToWatch = CFArrayCreate(NULL, (const void **)&_urlPathRef, 1, NULL);
+        _defaultsKey =
+            [NSString stringWithFormat:@"%@%@", VLCMediaLibraryFolderFSLastEventIdPrefix, url.path];
+
+        FSEventStreamContext context = { 0, (__bridge void *)self, NULL, NULL, NULL };
+        const FSEventStreamCreateFlags createFlags =
+            kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagIgnoreSelf;
+
+        NSNumber * const lastEventIdDefaults =
+            (NSNumber *)[NSUserDefaults.standardUserDefaults objectForKey:self.defaultsKey];
+        const FSEventStreamEventId lastEventId = lastEventIdDefaults == nil
+            ? kFSEventStreamEventIdSinceNow
+            : lastEventIdDefaults.unsignedLongLongValue;
+
+        _stream = FSEventStreamCreate(kCFAllocatorDefault,
+                                      &fsEventCallback,
+                                      &context,
+                                      _pathsToWatch,
+                                      lastEventId,
+                                      3.0,
+                                      createFlags);
+
+        FSEventStreamSetDispatchQueue(_stream, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
+        NSAssert(FSEventStreamStart(_stream), @"FSEvent stream should be started for %@", url.path);
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    FSEventStreamStop(_stream);
+    FSEventStreamRelease(_stream);
+    CFRelease(_urlPathRef);
+    CFRelease(_pathsToWatch);
+}
+
+ at end


=====================================
modules/gui/macosx/views/VLCStatusNotifierView.m
=====================================
@@ -108,6 +108,10 @@ NSString * const VLCMessageTimeoutTimerUserInfoMessageKey = @"VLCMessageTimeoutT
 
 - (void)removeMessage:(NSString *)message
 {
+    if (message == nil) {
+        return;
+    }
+
     const NSInteger matchingIndex = [self.messages indexOfObjectPassingTest:^BOOL(NSString * const string, NSUInteger, BOOL *){
         return [string hasPrefix:message];
     }];



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3f9d7ff7d86fce6ba509a2310f2c0695808b7b67...b3f9613ca56f97e3f0092f6e1c923872696e1c2c

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3f9d7ff7d86fce6ba509a2310f2c0695808b7b67...b3f9613ca56f97e3f0092f6e1c923872696e1c2c
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