diff --git a/extras/package/macosx/VLC.xcodeproj/project.pbxproj b/extras/package/macosx/VLC.xcodeproj/project.pbxproj
index 4534baf5cc25c3b40aefb3c6027fca04f4f3a1cb..38aa696d996e46349108bb8d9dac310c19fa6285 100644
--- a/extras/package/macosx/VLC.xcodeproj/project.pbxproj
+++ b/extras/package/macosx/VLC.xcodeproj/project.pbxproj
@@ -139,6 +139,7 @@
 		7D903EAF224392B400917358 /* timespec_get.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D903EAE224392B400917358 /* timespec_get.c */; };
 		7D903EB6224394BE00917358 /* specific.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D903EB5224394BE00917358 /* specific.c */; };
 		7D903EB92243952100917358 /* threads.c in Sources */ = {isa = PBXBuildFile; fileRef = 7D903EB82243952100917358 /* threads.c */; };
+		7D92AF2123DDCA8D00D81EA3 /* VLCLibraryImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D92AF2023DDCA8D00D81EA3 /* VLCLibraryImageCache.m */; };
 		7D93D8FC2316C2DC001C0063 /* VLCCustomCropArWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D93D8FB2316C2DC001C0063 /* VLCCustomCropArWindowController.m */; };
 		7DB40D2A20CBCEB500F63173 /* VLCMainMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DB40D2920CBCEB500F63173 /* VLCMainMenu.m */; };
 		7DB40D2D20CBCEC200F63173 /* VLCStatusBarIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DB40D2B20CBCEC200F63173 /* VLCStatusBarIcon.m */; };
@@ -540,6 +541,8 @@
 		7D903EAE224392B400917358 /* timespec_get.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = timespec_get.c; path = ../../../../compat/timespec_get.c; sourceTree = "<group>"; };
 		7D903EB5224394BE00917358 /* specific.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = specific.c; path = ../../../src/darwin/specific.c; sourceTree = "<group>"; };
 		7D903EB82243952100917358 /* threads.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threads.c; path = ../../../../src/misc/threads.c; sourceTree = "<group>"; };
+		7D92AF1F23DDCA8D00D81EA3 /* VLCLibraryImageCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryImageCache.h; sourceTree = "<group>"; };
+		7D92AF2023DDCA8D00D81EA3 /* VLCLibraryImageCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryImageCache.m; sourceTree = "<group>"; };
 		7D93D8F92316C142001C0063 /* VLCCustomCropARPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VLCCustomCropARPanel.xib; sourceTree = "<group>"; };
 		7D93D8FA2316C2DC001C0063 /* VLCCustomCropArWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCCustomCropArWindowController.h; sourceTree = "<group>"; };
 		7D93D8FB2316C2DC001C0063 /* VLCCustomCropArWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCCustomCropArWindowController.m; sourceTree = "<group>"; };
@@ -1052,6 +1055,8 @@
 				7DFBDCAA2269E77F00B700A5 /* VLCLibraryModel.m */,
 				7DFBDCB2226CD00900B700A5 /* VLCLibraryDataTypes.h */,
 				7DFBDCB3226CD00900B700A5 /* VLCLibraryDataTypes.m */,
+				7D92AF1F23DDCA8D00D81EA3 /* VLCLibraryImageCache.h */,
+				7D92AF2023DDCA8D00D81EA3 /* VLCLibraryImageCache.m */,
 				7DFBDCAC2269ED0C00B700A5 /* VLCLibraryVideoDataSource.h */,
 				7DFBDCAD2269ED0C00B700A5 /* VLCLibraryVideoDataSource.m */,
 				7DE2F0422282C84A0040DD0A /* VLCLibraryAudioDataSource.h */,
@@ -1754,6 +1759,7 @@
 				7DE9C7DD220728420089108F /* VLCPlayerController.m in Sources */,
 				7D93D8FC2316C2DC001C0063 /* VLCCustomCropArWindowController.m in Sources */,
 				1C3113941E508C6900D4DD76 /* VLCAddonsWindowController.m in Sources */,
+				7D92AF2123DDCA8D00D81EA3 /* VLCLibraryImageCache.m in Sources */,
 				7D2E0EDE20CD206F0033A221 /* VLCVideoWindowCommon.m in Sources */,
 				1C3113961E508C6900D4DD76 /* applescript.m in Sources */,
 				1C3113981E508C6900D4DD76 /* VLCAudioEffectsWindowController.m in Sources */,
diff --git a/modules/gui/macosx/Makefile.am b/modules/gui/macosx/Makefile.am
index 53d36083f9231f7e49d4276d99ec2247cc0c6245..f273ccb7cf286882ba073337bbec63b78eabb6ee 100644
--- a/modules/gui/macosx/Makefile.am
+++ b/modules/gui/macosx/Makefile.am
@@ -64,6 +64,8 @@ libmacosx_plugin_la_SOURCES = \
 	gui/macosx/library/VLCLibraryController.m \
 	gui/macosx/library/VLCLibraryDataTypes.h \
 	gui/macosx/library/VLCLibraryDataTypes.m \
+	gui/macosx/library/VLCLibraryImageCache.h \
+	gui/macosx/library/VLCLibraryImageCache.m \
 	gui/macosx/library/VLCLibraryInformationPanel.h \
 	gui/macosx/library/VLCLibraryInformationPanel.m \
 	gui/macosx/library/VLCLibraryMenuController.h \
diff --git a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m
index 4fc79b3ae87fa099fd5a6e146e2db5613232fe92..c02442c77642049a21e520fc678fe71b248f0d2c 100644
--- a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m
+++ b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m
@@ -213,12 +213,7 @@ const CGFloat LayoutSpacer;
 
     VLCMediaLibraryMediaItem *mediaItem = _tracks[row];
 
-    NSImage *image;
-    if (mediaItem.smallArtworkGenerated) {
-        if (mediaItem.smallArtworkMRL.length > 0) {
-            image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:mediaItem.smallArtworkMRL]];
-        }
-    }
+    NSImage *image = mediaItem.smallArtworkImage;
     if (!image) {
         image = [NSImage imageNamed: @"noart.png"];
     }
diff --git a/modules/gui/macosx/library/VLCLibraryAudioDataSource.m b/modules/gui/macosx/library/VLCLibraryAudioDataSource.m
index 6512f276855f420ae7d7036738b0810835208cae..d711d96d52bcfe38c891d76943206884b7d3bf13 100644
--- a/modules/gui/macosx/library/VLCLibraryAudioDataSource.m
+++ b/modules/gui/macosx/library/VLCLibraryAudioDataSource.m
@@ -189,12 +189,7 @@ static NSString *VLCAudioLibraryCellIdentifier = @"VLCAudioLibraryCellIdentifier
         {
             VLCMediaLibraryMediaItem *mediaItem = _displayedCollection[row];
 
-            NSImage *image;
-            if (mediaItem.smallArtworkGenerated) {
-                if (mediaItem.smallArtworkMRL.length > 0) {
-                    image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:mediaItem.smallArtworkMRL]];
-                }
-            }
+            NSImage *image = mediaItem.smallArtworkImage;
             if (!image) {
                 image = [NSImage imageNamed: @"noart.png"];
             }
diff --git a/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m b/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m
index ac97cfe2111d17e8fdf451b06e0241f770c3db77..f6487a17d9b89918b67986fa4626dcdac3a9755f 100644
--- a/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m
+++ b/modules/gui/macosx/library/VLCLibraryCollectionViewItem.m
@@ -185,14 +185,7 @@ const CGFloat VLCLibraryCollectionViewItemMaximumDisplayedProgress = 0.95;
 
 - (NSImage *)imageForMedia
 {
-    NSImage *image;
-    if (_representedMediaItem.smallArtworkGenerated) {
-        image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:_representedMediaItem.smallArtworkMRL]];
-    } else {
-        if (_representedMediaItem.mediaType != VLC_ML_MEDIA_TYPE_AUDIO) {
-            [_libraryController attemptToGenerateThumbnailForMediaItem:_representedMediaItem];
-        }
-    }
+    NSImage *image = _representedMediaItem.smallArtworkImage;
     if (!image) {
         image = [NSImage imageNamed: @"noart.png"];
     }
diff --git a/modules/gui/macosx/library/VLCLibraryController.h b/modules/gui/macosx/library/VLCLibraryController.h
index 95a19981f1385b852647b7815f2edd8157259b65..cb18fe436f675f6f9ab3cef4f518eab9c0fab11c 100644
--- a/modules/gui/macosx/library/VLCLibraryController.h
+++ b/modules/gui/macosx/library/VLCLibraryController.h
@@ -36,7 +36,6 @@ NS_ASSUME_NONNULL_BEGIN
 - (int)appendItemToPlaylist:(VLCMediaLibraryMediaItem *)mediaItem playImmediately:(BOOL)playImmediately;
 - (int)appendItemsToPlaylist:(NSArray <VLCMediaLibraryMediaItem *> *)mediaItemArray playFirstItemImmediately:(BOOL)playFirstItemImmediately;
 - (void)showItemInFinder:(VLCMediaLibraryMediaItem *)mediaItem;
-- (int)attemptToGenerateThumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem;
 
 - (int)addFolderWithFileURL:(NSURL *)fileURL;
 - (int)banFolderWithFileURL:(NSURL *)fileURL;
diff --git a/modules/gui/macosx/library/VLCLibraryController.m b/modules/gui/macosx/library/VLCLibraryController.m
index a0e955019bc62e20c6c56455641221501ebb8355..b20b48ade82de0c7676b0bb7bc1667b065c041be 100644
--- a/modules/gui/macosx/library/VLCLibraryController.m
+++ b/modules/gui/macosx/library/VLCLibraryController.m
@@ -30,10 +30,6 @@
 
 #import <vlc_media_library.h>
 
-uint32_t kVLCDesiredThumbnailWidth = 512;
-uint32_t kVLCDesiredThumbnailHeight = 320;
-float kVLCDefaultThumbnailPosition = .15;
-
 @interface VLCLibraryController()
 {
     vlc_medialibrary_t *_p_libraryInstance;
@@ -140,19 +136,6 @@ float kVLCDefaultThumbnailPosition = .15;
     }
 }
 
-- (int)attemptToGenerateThumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem
-{
-    if (!_p_libraryInstance) {
-        return VLC_ENOOBJ;
-    }
-    return vlc_ml_media_generate_thumbnail(_p_libraryInstance,
-                                           mediaItem.libraryID,
-                                           VLC_ML_THUMBNAIL_SMALL,
-                                           kVLCDesiredThumbnailWidth,
-                                           kVLCDesiredThumbnailHeight,
-                                           kVLCDefaultThumbnailPosition);
-}
-
 #pragma mark - folder management
 
 - (int)addFolderWithFileURL:(NSURL *)fileURL
diff --git a/modules/gui/macosx/library/VLCLibraryDataTypes.h b/modules/gui/macosx/library/VLCLibraryDataTypes.h
index 01f67a81c12c0a2581da69653ea2aa8fcadfd1c5..bada8467f8a56ca543a18f4f38b257d36f741718 100644
--- a/modules/gui/macosx/library/VLCLibraryDataTypes.h
+++ b/modules/gui/macosx/library/VLCLibraryDataTypes.h
@@ -175,6 +175,7 @@ extern const long long int VLCMediaLibraryMediaItemDurationDenominator;
 @property (readonly) NSString *title;
 
 @property (readonly, nullable) NSString *smallArtworkMRL;
+@property (readonly, nullable) NSImage *smallArtworkImage;
 
 @property (readonly) BOOL smallArtworkGenerated;
 @property (readonly) BOOL favorited;
diff --git a/modules/gui/macosx/library/VLCLibraryDataTypes.m b/modules/gui/macosx/library/VLCLibraryDataTypes.m
index d29094da13c17b90bf37da5671cda44065006078..57555e0879670b430711cf6c2aac6cac8cd28e40 100644
--- a/modules/gui/macosx/library/VLCLibraryDataTypes.m
+++ b/modules/gui/macosx/library/VLCLibraryDataTypes.m
@@ -25,6 +25,7 @@
 #import "main/VLCMain.h"
 #import "extensions/NSString+Helpers.h"
 #import "library/VLCInputItem.h"
+#import "library/VLCLibraryImageCache.h"
 
 #import <vlc_url.h>
 
@@ -461,6 +462,11 @@ NSString *VLCMediaLibraryMediaItemLibraryID = @"VLCMediaLibraryMediaItemLibraryI
     }
 }
 
+- (NSImage *)smallArtworkImage
+{
+    return [VLCLibraryImageCache thumbnailForMediaItem:self];
+}
+
 - (VLCInputItem *)inputItem
 {
     input_item_t *p_inputItem = vlc_ml_get_input_item(_p_mediaLibrary, _libraryID);
diff --git a/modules/gui/macosx/library/VLCLibraryImageCache.h b/modules/gui/macosx/library/VLCLibraryImageCache.h
new file mode 100644
index 0000000000000000000000000000000000000000..18e6cef04d859b017c2c48fa7f53640dd5dc7495
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryImageCache.h
@@ -0,0 +1,36 @@
+/*****************************************************************************
+* VLCLibraryImageCache.h: MacOS X interface module
+*****************************************************************************
+* Copyright (C) 2020 VLC authors and VideoLAN
+*
+* Authors: Felix Paul Kühne <fkuehne # videolan -dot- org>
+*
+* 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>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class VLCMediaLibraryMediaItem;
+
+@interface VLCLibraryImageCache : NSObject
+
++ (NSImage *)thumbnailForMediaItemWithID:(int64_t)libraryID;
++ (NSImage *)thumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/modules/gui/macosx/library/VLCLibraryImageCache.m b/modules/gui/macosx/library/VLCLibraryImageCache.m
new file mode 100644
index 0000000000000000000000000000000000000000..29255f2873cd6ad15b5baacd7666ccfbbe1a430f
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryImageCache.m
@@ -0,0 +1,123 @@
+/*****************************************************************************
+* VLCLibraryImageCache.m: MacOS X interface module
+*****************************************************************************
+* Copyright (C) 2020 VLC authors and VideoLAN
+*
+* Authors: Felix Paul Kühne <fkuehne # videolan -dot- org>
+*
+* 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 "VLCLibraryImageCache.h"
+#import "library/VLCLibraryDataTypes.h"
+#import "main/VLCMain.h"
+
+NSUInteger kVLCMaximumImageCacheSize = 50;
+uint32_t kVLCDesiredThumbnailWidth = 512;
+uint32_t kVLCDesiredThumbnailHeight = 320;
+float kVLCDefaultThumbnailPosition = .15;
+
+
+@interface VLCLibraryImageCache()
+{
+    NSCache *_imageCache;
+    vlc_medialibrary_t *_p_libraryInstance;
+}
+
+@end
+
+@implementation VLCLibraryImageCache
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        _imageCache = [[NSCache alloc] init];
+        _imageCache.countLimit = kVLCMaximumImageCacheSize;
+    }
+    return self;
+}
+
++ (instancetype)sharedImageCache
+{
+    static dispatch_once_t onceToken;
+    static VLCLibraryImageCache *sharedImageCache;
+    dispatch_once(&onceToken, ^{
+        sharedImageCache = [[VLCLibraryImageCache alloc] init];
+    });
+    return sharedImageCache;
+}
+
++ (NSImage *)thumbnailForMediaItemWithID:(int64_t)libraryID
+{
+    return [[VLCLibraryImageCache sharedImageCache] imageForMediaItemWithID:libraryID];
+}
+
++ (NSImage *)thumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem
+{
+    return [[VLCLibraryImageCache sharedImageCache] imageForMediaItem:mediaItem];
+}
+
+- (NSImage *)imageForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem
+{
+    NSImage *cachedImage = [_imageCache objectForKey:@(mediaItem.libraryID)];
+    if (cachedImage) {
+        return cachedImage;
+    }
+    return [self smallThumbnailForMediaItem:mediaItem];
+}
+
+- (NSImage *)imageForMediaItemWithID:(int64_t)libraryID
+{
+    NSNumber *libraryIDnumber = @(libraryID);
+    NSImage *cachedImage = [_imageCache objectForKey:libraryIDnumber];
+    if (cachedImage) {
+        return cachedImage;
+    }
+    VLCMediaLibraryMediaItem *mediaItem = [VLCMediaLibraryMediaItem mediaItemForLibraryID:libraryID];
+    return [self smallThumbnailForMediaItem:mediaItem];
+}
+
+- (NSImage *)smallThumbnailForMediaItem:(VLCMediaLibraryMediaItem *)mediaItem
+{
+    NSImage *image;
+    if (mediaItem.smallArtworkGenerated) {
+        image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:mediaItem.smallArtworkMRL]];
+    } else {
+        if (mediaItem.mediaType != VLC_ML_MEDIA_TYPE_AUDIO) {
+            [self generateThumbnailForMediaItem:mediaItem.libraryID];
+        }
+    }
+    if (image) {
+        [_imageCache setObject:image forKey:@(mediaItem.libraryID)];
+    }
+    return image;
+}
+
+- (void)generateThumbnailForMediaItem:(int64_t)mediaID
+{
+    if (!_p_libraryInstance) {
+        _p_libraryInstance = vlc_ml_instance_get(getIntf());
+    }
+    vlc_ml_media_generate_thumbnail(_p_libraryInstance,
+                                    mediaID,
+                                    VLC_ML_THUMBNAIL_SMALL,
+                                    kVLCDesiredThumbnailWidth,
+                                    kVLCDesiredThumbnailHeight,
+                                    kVLCDefaultThumbnailPosition);
+}
+
+@end
diff --git a/modules/gui/macosx/library/VLCLibraryInformationPanel.m b/modules/gui/macosx/library/VLCLibraryInformationPanel.m
index 793105f6d330beec4d01c31d64fac635c77a03ab..54b0e0a3e62f1dd5b652b25b12c118ecf89cd599 100644
--- a/modules/gui/macosx/library/VLCLibraryInformationPanel.m
+++ b/modules/gui/macosx/library/VLCLibraryInformationPanel.m
@@ -92,9 +92,7 @@
 
     self.multiLineTextLabel.stringValue = textContent;
     self.window.title = _representedMediaItem.title;
-    if (_representedMediaItem.smallArtworkMRL) {
-        self.imageView.image = [[NSImage alloc] initWithContentsOfURL:[NSURL URLWithString:_representedMediaItem.smallArtworkMRL]];
-    }
+    self.imageView.image = _representedMediaItem.smallArtworkImage;
 }
 
 @end
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2ca218a6210d911a8696ba506e811db12b3a03d1..21287b2f21a1a073c011cd2db067bc7543d9be3b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -471,6 +471,8 @@ modules/gui/macosx/library/VLCLibraryController.h
 modules/gui/macosx/library/VLCLibraryController.m
 modules/gui/macosx/library/VLCLibraryDataTypes.h
 modules/gui/macosx/library/VLCLibraryDataTypes.m
+modules/gui/macosx/library/VLCLibraryImageCache.h
+modules/gui/macosx/library/VLCLibraryImageCache.m
 modules/gui/macosx/library/VLCLibraryInformationPanel.h
 modules/gui/macosx/library/VLCLibraryInformationPanel.m
 modules/gui/macosx/library/VLCLibraryMenuController.h