[vlc-commits] [Git][videolan/vlc][master] 9 commits: macosx: Add NSImage extension method to get thumbnail from localUrl asynchronously

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sun May 5 17:54:36 UTC 2024



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
530a8554 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Add NSImage extension method to get thumbnail from localUrl asynchronously

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

- - - - -
ab2e9a10 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Make getting thumbnail image for input item async

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

- - - - -
8ee07689 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Enforce use of async thumbnailing

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

- - - - -
64ae7930 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Actually fetch the thumbnail in QuickLook thumbnail generator

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

- - - - -
0444ac77 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Fix check for stream in input item thumbnail generation

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

- - - - -
5dc72cd2 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Store async input item thumbnail in image cache

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

- - - - -
0a46015f by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Just use folder icon for the input node path control item

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

- - - - -
b11661f1 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Be more permissive with QL thumbnail representations

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

- - - - -
3634ea93 by Claudio Cambra at 2024-05-05T17:15:36+00:00
macosx: Set image on cache for input item thumbnail on main queue

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

- - - - -


10 changed files:

- modules/gui/macosx/Makefile.am
- modules/gui/macosx/extensions/NSImage+VLCAdditions.h
- modules/gui/macosx/extensions/NSImage+VLCAdditions.m
- modules/gui/macosx/library/VLCInputItem.h
- modules/gui/macosx/library/VLCInputItem.m
- modules/gui/macosx/library/VLCInputNodePathControlItem.m
- modules/gui/macosx/library/VLCLibraryHeroView.m
- modules/gui/macosx/library/VLCLibraryImageCache.h
- modules/gui/macosx/library/VLCLibraryImageCache.m
- modules/gui/macosx/panels/VLCInformationWindowController.m


Changes:

=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -48,6 +48,7 @@ libmacosx_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(guidir)' \
 	-Wl,-framework,ScriptingBridge \
 	-Wl,-framework,WebKit \
 	-Wl,-framework,QuickLook \
+	-Wl,-framework,QuickLookThumbnailing \
 	-Wl,-weak_framework,MediaPlayer
 
 if HAVE_SPARKLE


=====================================
modules/gui/macosx/extensions/NSImage+VLCAdditions.h
=====================================
@@ -26,6 +26,12 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface NSImage (VLCAdditions)
 
++ (void)quickLookPreviewForLocalPath:(NSString *)path 
+                            withSize:(NSSize)size 
+                   completionHandler:(void (^)(NSImage * _Nullable))completionHandler;
++ (void)quickLookPreviewForLocalURL:(NSURL *)url 
+                           withSize:(NSSize)size 
+                  completionHandler:(void (^)(NSImage * _Nullable))completionHandler;
 + (instancetype)quickLookPreviewForLocalPath:(NSString*)path withSize:(NSSize)size;
 + (instancetype)quickLookPreviewForLocalURL:(NSURL*)url withSize:(NSSize)size;
 + (instancetype)compositeImageWithImages:(NSArray<NSImage *> * const)images


=====================================
modules/gui/macosx/extensions/NSImage+VLCAdditions.m
=====================================
@@ -23,9 +23,51 @@
 #import "NSImage+VLCAdditions.h"
 
 #import <QuickLook/QuickLook.h>
+#import <QuickLookThumbnailing/QuickLookThumbnailing.h>
 
 @implementation NSImage(VLCAdditions)
 
++ (void)quickLookPreviewForLocalPath:(NSString *)path 
+                            withSize:(NSSize)size 
+                   completionHandler:(void (^)(NSImage *))completionHandler
+{
+    NSURL * const pathUrl = [NSURL fileURLWithPath:path];
+    [self quickLookPreviewForLocalURL:pathUrl withSize:size completionHandler:completionHandler];
+}
+
++ (void)quickLookPreviewForLocalURL:(NSURL *)url 
+                           withSize:(NSSize)size 
+                  completionHandler:(void (^)(NSImage *))completionHandler
+{
+    if (@available(macOS 10.15, *)) {
+        const QLThumbnailGenerationRequestRepresentationTypes type = 
+            QLThumbnailGenerationRequestRepresentationTypeAll;
+        QLThumbnailGenerator * const generator = QLThumbnailGenerator.sharedGenerator;
+        QLThumbnailGenerationRequest * const request = 
+            [[QLThumbnailGenerationRequest alloc] initWithFileAtURL:url 
+                                                               size:size 
+                                                              scale:1. 
+                                                representationTypes:type];
+        [generator generateBestRepresentationForRequest:request 
+                                      completionHandler:^(QLThumbnailRepresentation * const thumbnail, 
+                                                          NSError * const error) {
+            if (error != nil) {
+                NSLog(@"Error generating thumbnail: %@", error);
+                completionHandler(nil);
+                return;
+            }
+            completionHandler(thumbnail.NSImage);
+        }];
+    } else {
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
+            NSImage * const image = [self quickLookPreviewForLocalURL:url withSize:size];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                completionHandler(image);
+            });
+        });
+    }
+}
+
 + (instancetype)quickLookPreviewForLocalPath:(NSString *)path withSize:(NSSize)size
 {
     NSURL *pathUrl = [NSURL fileURLWithPath:path];


=====================================
modules/gui/macosx/library/VLCInputItem.h
=====================================
@@ -81,7 +81,8 @@ extern NSString *VLCInputItemPreparsingSucceeded;
 - (int)preparseInputItem;
 - (int)writeMetadataToFile;
 
-- (NSImage*)thumbnailWithSize:(NSSize)size;
+- (void)thumbnailWithSize:(NSSize)size 
+        completionHandler:(void (^)(NSImage * _Nullable))completionHandler;
 
 @end
 


=====================================
modules/gui/macosx/library/VLCInputItem.m
=====================================
@@ -606,32 +606,46 @@ static const struct vlc_metadata_cbs preparseCallbacks = {
     return input_item_WriteMeta(VLC_OBJECT(getIntf()), _vlcInputItem);
 }
 
-- (NSImage*)thumbnailWithSize:(NSSize)size
+- (void)thumbnailWithSize:(NSSize)size completionHandler:(void(^)(NSImage * image))completionHandler
 {
-    NSImage *image;
-    if (!self.isStream && _vlcInputItem != NULL) {
-        char *psz_url = input_item_GetURI(_vlcInputItem);
-        if (psz_url) {
-            char *psz_path = vlc_uri2path(psz_url);
-            if (psz_path) {
-                NSString *path = toNSStr(psz_path);
-                free(psz_path);
-                image = [NSImage quickLookPreviewForLocalPath:path
-                                                     withSize:size];
-
-                if (!image) {
-                    image = [NSWorkspace.sharedWorkspace iconForFile:path];
-                    image.size = size;
-                }
-            }
-            free(psz_url);
-        }
+    if (self.isStream || _vlcInputItem == NULL) {
+        completionHandler(nil);
+        return;
     }
 
-    if (!image) {
-        image = [NSImage imageNamed: @"noart.png"];
+    char * const psz_url = input_item_GetURI(_vlcInputItem);
+    if (psz_url == NULL) {
+        completionHandler(nil);
+        return;
+    }
+
+    char * const psz_path = vlc_uri2path(psz_url);
+    free(psz_url);
+    if (psz_path == NULL) {
+        completionHandler(nil);
+        return;
     }
-    return image;
+
+    NSString * const path = toNSStr(psz_path);
+    free(psz_path);
+
+    [NSImage quickLookPreviewForLocalPath:path 
+                                 withSize:size
+                        completionHandler:^(NSImage * image) {
+        if (image) {
+            completionHandler(image);
+            return;
+        }
+
+        NSImage * const workspaceImage = [NSWorkspace.sharedWorkspace iconForFile:path];
+        if (workspaceImage) {
+            image.size = size;
+            completionHandler(image);
+            return;
+        }
+
+        completionHandler(nil);
+    }];
 }
 
 - (void)moveToTrash


=====================================
modules/gui/macosx/library/VLCInputNodePathControlItem.m
=====================================
@@ -36,8 +36,7 @@
         VLCInputItem * const inputItem = inputNode.inputItem;
         self.title = inputItem.name;
 
-        self.image = [VLCLibraryImageCache thumbnailForInputItem:inputItem];;
-
+        self.image = [NSImage imageNamed:NSImageNameFolder];
         // HACK: We have no way when we get the clicked item from the path control
         // of knowing specifically which input node this path item corresponds to,
         // as the path control returns a copy for clickedPathItem that is not of


=====================================
modules/gui/macosx/library/VLCLibraryHeroView.m
=====================================
@@ -54,9 +54,11 @@
 {
     NSAssert(self.representedItem != nil, @"Should not update nil represented item!");
     const id<VLCMediaLibraryItemProtocol> actualItem = self.representedItem.item;
-    self.largeImageView.image = [VLCLibraryImageCache thumbnailForLibraryItem:actualItem];
     self.titleTextField.stringValue = actualItem.displayString;
     self.detailTextField.stringValue = actualItem.primaryDetailString;
+    [VLCLibraryImageCache thumbnailForLibraryItem:actualItem withCompletion:^(NSImage * const image) {
+        self.largeImageView.image = image;
+    }];
 }
 
 - (void)setRepresentedItem:(VLCLibraryRepresentedItem *)representedItem


=====================================
modules/gui/macosx/library/VLCLibraryImageCache.h
=====================================
@@ -30,10 +30,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface VLCLibraryImageCache : NSObject
 
-+ (nullable NSImage *)thumbnailForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem;
-+ (nullable NSImage *)thumbnailForInputItem:(VLCInputItem*)inputItem;
-+ (nullable NSImage *)thumbnailForPlaylistItem:(VLCPlaylistItem*)playlistItem;
-
 + (void)thumbnailForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
                  withCompletion:(void(^)(const NSImage *))completionHandler;
 + (void)thumbnailForInputItem:(VLCInputItem *)inputItem


=====================================
modules/gui/macosx/library/VLCLibraryImageCache.m
=====================================
@@ -119,50 +119,58 @@ const NSUInteger kVLCCompositeImageDefaultCompositedGridItemCount = 4;
                                     kVLCDefaultThumbnailPosition);
 }
 
-+ (NSImage *)thumbnailForInputItem:(VLCInputItem *)inputItem
++ (void)thumbnailForInputItem:(VLCInputItem *)inputItem 
+               withCompletion:(nonnull void (^)(const NSImage * _Nonnull))completionHandler
 {
-    return [VLCLibraryImageCache.sharedImageCache imageForInputItem:inputItem];
+    [VLCLibraryImageCache.sharedImageCache imageForInputItem:inputItem withCompletion:completionHandler];
 }
 
-- (NSImage *)imageForInputItem:(VLCInputItem *)inputItem
+- (void)imageForInputItem:(VLCInputItem *)inputItem 
+           withCompletion:(nonnull void (^)(const NSImage * _Nonnull))completionHandler
 {
-    NSImage *cachedImage = [_imageCache objectForKey:inputItem.MRL];
+    NSImage * const cachedImage = [_imageCache objectForKey:inputItem.MRL];
     if (cachedImage) {
-        return cachedImage;
+        completionHandler(cachedImage);
+        return;
     }
-    return [self generateImageForInputItem:inputItem];
+    [self generateImageForInputItem:inputItem withCompletion:completionHandler];
 }
 
-- (NSImage *)generateImageForInputItem:(VLCInputItem *)inputItem
+- (void)generateImageForInputItem:(VLCInputItem *)inputItem 
+                   withCompletion:(void(^)(const NSImage *))completionHandler
 {
-    NSImage *image;
     NSURL * const artworkURL = inputItem.artworkURL;
+    NSImage * const image = [[NSImage alloc] initWithContentsOfURL:artworkURL];
     const NSSize imageSize = NSMakeSize(kVLCDesiredThumbnailWidth, kVLCDesiredThumbnailHeight);
 
-    if (artworkURL) {
-        image = [[NSImage alloc] initWithContentsOfURL:artworkURL];
-    }
-
-    if (image == nil) {
-        image = [inputItem thumbnailWithSize:imageSize];
-    }
-
     if (image) {
+        image.size = imageSize;
         [_imageCache setObject:image forKey:inputItem.MRL];
-    } else { // If nothing so far worked, then fall back on default image
-        image = [NSImage imageNamed:@"noart.png"];
+        completionHandler(image);
+    } else {
+        [inputItem thumbnailWithSize:imageSize completionHandler:^(NSImage * const image) {
+            dispatch_async(dispatch_get_main_queue(), ^{
+                if (image) {
+                    [_imageCache setObject:image forKey:inputItem.MRL];
+                    completionHandler(image);
+                } else {
+                    NSLog(@"Failed to generate thumbnail for input item %@", inputItem.MRL);
+                    completionHandler([NSImage imageNamed:@"noart.png"]);
+                }
+            });
+        }];
     }
-
-    return image;
 }
 
-+ (NSImage *)thumbnailForPlaylistItem:(VLCPlaylistItem *)playlistItem
++ (void)thumbnailForPlaylistItem:(VLCPlaylistItem *)playlistItem 
+                  withCompletion:(nonnull void (^)(const NSImage * _Nonnull))completionHandler
 {
-    return [VLCLibraryImageCache.sharedImageCache imageForInputItem:playlistItem.inputItem];
+    return [VLCLibraryImageCache.sharedImageCache imageForInputItem:playlistItem.inputItem 
+                                                     withCompletion:completionHandler];
 }
 
 + (void)thumbnailForLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
-               withCompletion:(void(^)(const NSImage *))completionHandler
+                 withCompletion:(void(^)(const NSImage *))completionHandler
 {
     if ([libraryItem isKindOfClass:VLCAbstractMediaLibraryAudioGroup.class] && ![libraryItem isKindOfClass:VLCMediaLibraryAlbum.class]) {
 
@@ -200,21 +208,4 @@ const NSUInteger kVLCCompositeImageDefaultCompositedGridItemCount = 4;
     }
 }
 
-+ (void)thumbnailForInputItem:(VLCInputItem *)inputItem
-               withCompletion:(void(^)(const NSImage *))completionHandler
-{
-    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
-        NSImage * const image = [VLCLibraryImageCache thumbnailForInputItem:inputItem];
-        dispatch_async(dispatch_get_main_queue(), ^{
-            completionHandler(image);
-        });
-    });
-}
-
-+ (void)thumbnailForPlaylistItem:(VLCPlaylistItem *)playlistItem
-               withCompletion:(void(^)(const NSImage *))completionHandler
-{
-    [self thumbnailForInputItem:playlistItem.inputItem withCompletion:completionHandler];
-}
-
 @end


=====================================
modules/gui/macosx/panels/VLCInformationWindowController.m
=====================================
@@ -265,7 +265,10 @@ _##field##TextField.delegate = self
 - (void)setRepresentedInputItem:(VLCInputItem *)representedInputItem
 {
     _representedInputItems = (representedInputItem == nil) ? @[] : @[representedInputItem];
-    _artwork = [VLCLibraryImageCache thumbnailForInputItem:representedInputItem];
+    [VLCLibraryImageCache thumbnailForInputItem:representedInputItem 
+                                 withCompletion:^(NSImage * const image) {
+        self->_artwork = image;
+    }];
     [self updateRepresentation];
 }
 
@@ -278,9 +281,12 @@ _##field##TextField.delegate = self
 
     _representedInputItems = [inputItems copy];
 
-    // HACK: Input items retrieved via an audio group do not acquire an artwork URL.
-    // To show something in the information window, set the small artwork from the audio group.
-    _artwork = [VLCLibraryImageCache thumbnailForLibraryItem:representedMediaLibraryAudioGroup];
+    [VLCLibraryImageCache thumbnailForLibraryItem:representedMediaLibraryAudioGroup 
+                                   withCompletion:^(NSImage * const image) {
+        // HACK: Input items retrieved via an audio group do not acquire an artwork URL.
+        // To show something in the information window, set the small artwork from the audio group.
+        self->_artwork = image;
+    }];
 
     [self updateRepresentation];
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/bcd5247fb75f7c56ce02e4adaf927bbfcd61fce1...3634ea930bf00008e18ba203f85188529897b1b8

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/bcd5247fb75f7c56ce02e4adaf927bbfcd61fce1...3634ea930bf00008e18ba203f85188529897b1b8
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