Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Core/MetadataExtractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -573,13 +573,21 @@ class MetadataExtractor {

let rawData = firstPicture.imageData

if rawData.count > AlbumArtFormat.maxArtworkSize {
let context = source.map { " for \($0)" } ?? ""
Logger.warning("Skipping oversized embedded artwork\(context) (\(rawData.count) bytes)")
return
}

// Check cache for previously compressed identical artwork
if let cache = artworkCache, let cached = await cache.get(for: rawData) {
metadata.artworkData = cached
return
}

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

// Store in cache for subsequent tracks with identical artwork
Expand Down
1 change: 1 addition & 0 deletions Utilities/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ enum AlbumArtFormat {
static let priorityKeywords = ["cover", "folder", "album", "artwork", "front"]

static let maxArtworkSize: Int = 20 * 1024 * 1024
static let maxArtworkPixelDimension: Int = 8000

static func isSupported(_ fileExtension: String) -> Bool {
supportedExtensions.contains(fileExtension.lowercased())
Expand Down
27 changes: 23 additions & 4 deletions Utilities/ImageUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,26 @@ enum ImageUtils {
return compressImageIntel(from: imageData, maxDimension: maxDimension, quality: quality, source: source)
#else
guard let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil),
let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
let props = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any],
let srcWidth = props[kCGImagePropertyPixelWidth] as? CGFloat,
let srcHeight = props[kCGImagePropertyPixelHeight] as? CGFloat else {
let context = source.map { " from \($0)" } ?? ""
Logger.warning("Failed to create image\(context) (\(imageData.count) bytes)")
Logger.warning("Failed to read image properties\(context) (\(imageData.count) bytes)")
return nil
}

let srcWidth = CGFloat(cgImage.width)
let srcHeight = CGFloat(cgImage.height)
let pixelLimit = CGFloat(AlbumArtFormat.maxArtworkPixelDimension)
if srcWidth > pixelLimit || srcHeight > pixelLimit {
let context = source.map { " from \($0)" } ?? ""
Logger.warning("Skipping oversized artwork \(Int(srcWidth))x\(Int(srcHeight))\(context)")
return nil
}

guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
let context = source.map { " from \($0)" } ?? ""
Logger.warning("Failed to decode image\(context) (\(imageData.count) bytes)")
return nil
}

var destWidth = srcWidth
var destHeight = srcHeight
Expand Down Expand Up @@ -434,6 +446,13 @@ enum ImageUtils {
return nil
}

let pixelLimit = CGFloat(AlbumArtFormat.maxArtworkPixelDimension)
if width > pixelLimit || height > pixelLimit {
let context = source.map { " from \($0)" } ?? ""
Logger.warning("Skipping oversized artwork \(Int(width))x\(Int(height))\(context)")
return nil
}

var destWidth = width
var destHeight = height
if width > maxDimension || height > maxDimension {
Expand Down
Loading