Skip to content

Commit 790699a

Browse files
committed
perf: Set max size for thumbnail GIF and render original when within limit
1 parent 34bf19e commit 790699a

2 files changed

Lines changed: 63 additions & 15 deletions

File tree

FileBrowserClient/FileBrowserClient/Configuration/Constants.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ struct Constants {
3131
static let thumbnailQuality: CGFloat = 0.1
3232
static let thumbnailRetryLimit: Int = 5
3333
static let maxThumbnailSize: CGFloat = 320
34+
// 10 MB is the max GIF filesize that can be rendered as a thumbnail
35+
static let maxGIFThumbnailBytes: Int = 10_000_000
3436

3537
// MediaPlayerView:
3638
// Minimum duration (in secs) of a video to be considered resume worthy

FileBrowserClient/FileBrowserClient/DataManagers/RemoteThumbnail.swift

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -336,27 +336,73 @@ struct RemoteThumbnail: View {
336336
return
337337
}
338338
autoreleasepool {
339-
if advancedSettings.cacheThumbnail {
340-
if let img = downsampleImage(data: data, maxDimension: Constants.maxThumbnailSize),
341-
let compressed = img.jpegData(compressionQuality: Constants.thumbnailQuality),
342-
advancedSettings.cacheThumbnail {
343-
FileCache.shared.store(
344-
for: serverURL,
345-
data: compressed,
346-
path: file.path,
347-
modified: file.modified,
348-
fileID: "thumb"
339+
if isGIF {
340+
// Cap GIF size to avoid memory bombs
341+
if data.count < Constants.maxGIFThumbnailBytes {
342+
if advancedSettings.cacheThumbnail {
343+
FileCache.shared.store(
344+
for: serverURL,
345+
data: data,
346+
path: file.path,
347+
modified: file.modified,
348+
fileID: "thumb"
349+
)
350+
}
351+
GlobalThumbnailLoader.shared.finish(
352+
filePath: file.path,
353+
image: nil,
354+
gifData: data,
355+
failed: false
349356
)
357+
} else {
358+
// Treat large GIF as static image
359+
if let img = downsampleImage(
360+
data: data,
361+
maxDimension: Constants.maxThumbnailSize
362+
) {
363+
GlobalThumbnailLoader.shared.finish(
364+
filePath: file.path,
365+
image: img,
366+
gifData: nil,
367+
failed: false
368+
)
369+
}
350370
}
371+
return
351372
}
352-
if isGIF {
353-
GlobalThumbnailLoader.shared.finish(filePath: file.path, image: nil, gifData: data, failed: false)
354-
} else if let img = downsampleImage(data: data, maxDimension: Constants.maxThumbnailSize) {
355-
GlobalThumbnailLoader.shared.finish(filePath: file.path, image: img, gifData: nil, failed: false)
356-
} else {
373+
374+
// Static image handling
375+
guard let img = downsampleImage(
376+
data: data,
377+
maxDimension: Constants.maxThumbnailSize
378+
) else {
357379
let errMsg = "Failed to downsample image thumbnail for file: \(file.name)"
358380
retryHandler(retryCount: retryCount, errMsg: errMsg)
381+
return
359382
}
383+
384+
// Cache downsampled thumbnail
385+
if advancedSettings.cacheThumbnail,
386+
let compressed = img.jpegData(
387+
compressionQuality: Constants.thumbnailQuality
388+
) {
389+
390+
FileCache.shared.store(
391+
for: serverURL,
392+
data: compressed,
393+
path: file.path,
394+
modified: file.modified,
395+
fileID: "thumb"
396+
)
397+
}
398+
399+
// Render UI
400+
GlobalThumbnailLoader.shared.finish(
401+
filePath: file.path,
402+
image: img,
403+
gifData: nil,
404+
failed: false
405+
)
360406
}
361407
}.resume()
362408
}

0 commit comments

Comments
 (0)