Skip to content

Commit 2b5e93f

Browse files
authored
Prevent high-res track album art from causing memory overload (#278)
Guards artwork extraction and resize logic from loading very high-res album artwork that can potentially cause memory overload and crash. This is follow up to fix made in #268. Fixes #273
1 parent 623e71f commit 2b5e93f

3 files changed

Lines changed: 33 additions & 5 deletions

File tree

Core/MetadataExtractor.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,21 @@ class MetadataExtractor {
573573

574574
let rawData = firstPicture.imageData
575575

576+
if rawData.count > AlbumArtFormat.maxArtworkSize {
577+
let context = source.map { " for \($0)" } ?? ""
578+
Logger.warning("Skipping oversized embedded artwork\(context) (\(rawData.count) bytes)")
579+
return
580+
}
581+
576582
// Check cache for previously compressed identical artwork
577583
if let cache = artworkCache, let cached = await cache.get(for: rawData) {
578584
metadata.artworkData = cached
579585
return
580586
}
581587

582-
let compressed = ImageUtils.compressImage(from: rawData, source: source) ?? rawData
588+
// If compression fails, leave artworkData nil rather than persisting undecodable bytes
589+
// that would re-fail on every later read (sidebar, now-playing, color extraction).
590+
guard let compressed = ImageUtils.compressImage(from: rawData, source: source) else { return }
583591
metadata.artworkData = compressed
584592

585593
// Store in cache for subsequent tracks with identical artwork

Utilities/Constants.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ enum AlbumArtFormat {
149149
static let priorityKeywords = ["cover", "folder", "album", "artwork", "front"]
150150

151151
static let maxArtworkSize: Int = 20 * 1024 * 1024
152+
static let maxArtworkPixelDimension: Int = 8000
152153

153154
static func isSupported(_ fileExtension: String) -> Bool {
154155
supportedExtensions.contains(fileExtension.lowercased())

Utilities/ImageUtils.swift

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,26 @@ enum ImageUtils {
2222
return compressImageIntel(from: imageData, maxDimension: maxDimension, quality: quality, source: source)
2323
#else
2424
guard let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil),
25-
let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
25+
let props = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any],
26+
let srcWidth = props[kCGImagePropertyPixelWidth] as? CGFloat,
27+
let srcHeight = props[kCGImagePropertyPixelHeight] as? CGFloat else {
2628
let context = source.map { " from \($0)" } ?? ""
27-
Logger.warning("Failed to create image\(context) (\(imageData.count) bytes)")
29+
Logger.warning("Failed to read image properties\(context) (\(imageData.count) bytes)")
2830
return nil
2931
}
3032

31-
let srcWidth = CGFloat(cgImage.width)
32-
let srcHeight = CGFloat(cgImage.height)
33+
let pixelLimit = CGFloat(AlbumArtFormat.maxArtworkPixelDimension)
34+
if srcWidth > pixelLimit || srcHeight > pixelLimit {
35+
let context = source.map { " from \($0)" } ?? ""
36+
Logger.warning("Skipping oversized artwork \(Int(srcWidth))x\(Int(srcHeight))\(context)")
37+
return nil
38+
}
39+
40+
guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
41+
let context = source.map { " from \($0)" } ?? ""
42+
Logger.warning("Failed to decode image\(context) (\(imageData.count) bytes)")
43+
return nil
44+
}
3345

3446
var destWidth = srcWidth
3547
var destHeight = srcHeight
@@ -434,6 +446,13 @@ enum ImageUtils {
434446
return nil
435447
}
436448

449+
let pixelLimit = CGFloat(AlbumArtFormat.maxArtworkPixelDimension)
450+
if width > pixelLimit || height > pixelLimit {
451+
let context = source.map { " from \($0)" } ?? ""
452+
Logger.warning("Skipping oversized artwork \(Int(width))x\(Int(height))\(context)")
453+
return nil
454+
}
455+
437456
var destWidth = width
438457
var destHeight = height
439458
if width > maxDimension || height > maxDimension {

0 commit comments

Comments
 (0)