Skip to content

Commit

Permalink
Generates thumbnails for uploaded images (fixes #371) (#380)
Browse files Browse the repository at this point in the history
* generates thumbnails for uploaded images (fixes #371)

* remove useless code
  • Loading branch information
cohoe authored Feb 12, 2025
1 parent 3ac38be commit 2d081fd
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 4 deletions.
9 changes: 6 additions & 3 deletions Sources/swiftarr/Controllers/AdminController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,7 @@ struct AdminController: APIRouteCollection {
var imageImportError: String?
do {
if let userImage = userToImport.userImage {
copiedUserImage = try await copyImage(userImage, verifyOnly: verifyOnly)
copiedUserImage = try await copyImage(userImage, verifyOnly: verifyOnly, on: req)
}
}
catch {
Expand Down Expand Up @@ -1071,7 +1071,7 @@ struct AdminController: APIRouteCollection {
var copiedUserImage: String?
do {
if let image = performerData.photo.filename {
copiedUserImage = try await copyImage(image, verifyOnly: verifyOnly)
copiedUserImage = try await copyImage(image, verifyOnly: verifyOnly, on: req)
}
}
catch {
Expand Down Expand Up @@ -1132,7 +1132,7 @@ struct AdminController: APIRouteCollection {
}

// Copy an image from the uploaded data bundle to the expected location on the filesystem.
func copyImage(_ image: String, verifyOnly: Bool) async throws -> String {
func copyImage(_ image: String, verifyOnly: Bool, on req: Request) async throws -> String {
let archiveSource = try uploadUserDirPath().appendingPathComponent("Twitarr_userfile/userImages", isDirectory: true)
.appendingPathComponent(image)
let serverImageDestDir = Settings.shared.userImagesRootPath.appendingPathComponent(ImageSizeGroup.full.rawValue)
Expand All @@ -1149,6 +1149,9 @@ struct AdminController: APIRouteCollection {
}
try FileManager.default.copyItem(at: archiveSource, to: serverImageDest)
}
if !verifyOnly {
try await regenerateThumbnail(for: serverImageDest, on: req)
}
return image
}

Expand Down
42 changes: 41 additions & 1 deletion Sources/swiftarr/Protocols/ImageHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,47 @@ extension APIRouteCollection {
return fullPath.lastPathComponent
}.get()
}


// Generate a thumbnail for the given image (by full path). Currently used in `copyImage`
// as part of the bulk user import process. Could stand to be deduped with `processImage`
// above some day since most of the code came from there.
func regenerateThumbnail(for imageSource: URL, on req: Request) async throws {
let imageName = imageSource.lastPathComponent
return try await req.application.threadPool.runIfActive(eventLoop: req.eventLoop) {
let data = try Data(contentsOf: imageSource)

let imageTypes: [ImportableFormat] = [.jpg, .png, .gif, .webp, .tiff, .bmp, .wbmp]
var foundType: ImportableFormat? = nil
var foundImage: GDImage?
for type in imageTypes {
foundImage = try? GDImage(data: data, as: type)
if foundImage != nil{
foundType = type
break
}
}
guard let image = foundImage, let imageType = foundType else {
throw GDError.invalidImage(reason: "No matching raster formatter for given image found")
}

let destinationDir = Settings.shared.userImagesRootPath
.appendingPathComponent(ImageSizeGroup.thumbnail.rawValue)
.appendingPathComponent(String(imageName.prefix(2)))
// Testing this requires wiping out the thumbnail directory.
if (!FileManager.default.fileExists(atPath: destinationDir.path)) {
try FileManager.default.createDirectory(at: destinationDir, withIntermediateDirectories: true)
}
let thumbPath = destinationDir.appendingPathComponent(imageName)

guard let thumbnail = image.resizedTo(height: Settings.shared.imageThumbnailSize) else {
throw Abort(.internalServerError, reason: "Error generating thumbnail image")
}

let thumbnailData = try thumbnail.export(as: ExportableFormat(imageType))
try thumbnailData.write(to: thumbPath)
}.get()
}

/// Archives an image that is no longer needed other than for accountability tracking, by
/// removing the full-sized image and moving the thumbnail into the `archive/` subdirectory
/// of the provided base image directory.
Expand Down

0 comments on commit 2d081fd

Please sign in to comment.