Skip to content

Commit

Permalink
Allow using "original" to get the original image file, add support fo…
Browse files Browse the repository at this point in the history
…r jpeg files (downscaling not supported yet)
  • Loading branch information
MrPowerGamerBR committed Oct 14, 2024
1 parent 989c1f8 commit 333cf86
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,12 @@ class EtherealGambi(val config: EtherealGambiConfig) {
when (imageType) {
ImageType.PNG -> PNGImageInfo(this, data)
ImageType.GIF -> GIFImageInfo(this, data)
ImageType.JPEG -> JPEGImageInfo(this, data)
}
}
}

fun getVariantFromFileName(fileNameWithUnknownVariant: String): VariantResult {
fun getVariantFromFileName(pathWithoutFile: String, fileNameWithUnknownVariant: String): VariantResult {
val nameWithoutExtension = fileNameWithUnknownVariant.substringBeforeLast(".")
val extension = fileNameWithUnknownVariant.substringAfterLast(".")
.lowercase()
Expand All @@ -162,8 +163,13 @@ class EtherealGambi(val config: EtherealGambiConfig) {
val variantResult = GetFileRoute.variantRegex.findAll(nameWithoutExtension)
.lastOrNull()

if (variantResult != null && variantResult.groupValues[1] == "original") {
val fileNameWithoutTheVariant = nameWithoutExtension.replace("@original", "")
return OriginalFile("$pathWithoutFile/$fileNameWithoutTheVariant.$extension")
}

// GIF doesn't support scaling down... yet
if (variantResult != null && imageType != ImageType.GIF) {
if (variantResult != null && imageType != ImageType.GIF && imageType != ImageType.JPEG) {
val variantPreset = scaleDownToWidthVariantsPresets.firstOrNull { it.name == variantResult.groupValues[1] } ?: return VariantNotFound

val fileNameWithoutTheVariant = nameWithoutExtension.replace("@${variantPreset.name}", "")
Expand All @@ -176,9 +182,11 @@ class EtherealGambi(val config: EtherealGambiConfig) {
fileNameWithoutTheVariant
)
} else {
val fileNameWithoutTheVariant = nameWithoutExtension.replace(GetFileRoute.variantRegex, "")

return VariantFound(
DefaultImageVariant(imageType),
nameWithoutExtension
fileNameWithoutTheVariant
)
}
}
Expand All @@ -204,6 +212,7 @@ class EtherealGambi(val config: EtherealGambiConfig) {
it.key to when (it.value.imageType) {
ImageType.PNG -> PNGImageInfo(this, it.value)
ImageType.GIF -> GIFImageInfo(this, it.value)
ImageType.JPEG -> JPEGImageInfo(this, it.value)
}
}
.toMap()
Expand Down Expand Up @@ -237,4 +246,5 @@ class EtherealGambi(val config: EtherealGambiConfig) {
val variant: ImageVariant,
val nameWithoutExtension: String,
) : VariantResult()
data class OriginalFile(val path: String) : VariantResult()
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import io.ktor.server.response.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.perfectdreams.etherealgambi.backend.EtherealGambi
import net.perfectdreams.etherealgambi.backend.utils.SimpleImageInfo
import net.perfectdreams.sequins.ktor.BaseRoute
import java.io.File

Expand All @@ -28,44 +29,62 @@ class GetFileRoute(val m: EtherealGambi) : BaseRoute("/{file...}") {

val fileWithUnknownVariant = completePath.last()
val pathWithoutFile = completePath.dropLast(1)
val variantResult = m.getVariantFromFileName(fileWithUnknownVariant)
val variantResult = m.getVariantFromFileName(pathWithoutFile.joinToString("/"), fileWithUnknownVariant)

when (variantResult) {
is EtherealGambi.UnsupportedVariantImageFormat -> {
// Couldn't figure out an image from the URL... so let's check if we can serve the file "raw"!
val file = File(m.files, completePath.joinToString("/"))
if (file.exists())
call.respondBytes(file.readBytes())
else
call.respondText("File not found", status = HttpStatusCode.NotFound)
return
}
is EtherealGambi.VariantNotFound -> {
call.respondText("Unknown Variant", status = HttpStatusCode.BadRequest)
return
}
is EtherealGambi.VariantFound -> {
val (variant, fileNameWithoutVariant) = variantResult
when (variantResult) {
is EtherealGambi.UnsupportedVariantImageFormat -> {
// Couldn't figure out an image from the URL... so let's check if we can serve the file "raw"!
val file = File(m.files, completePath.joinToString("/"))
if (file.exists())
call.respondBytes(file.readBytes())
else
call.respondText("File not found", status = HttpStatusCode.NotFound)
return
}
is EtherealGambi.VariantNotFound -> {
call.respondText("Unknown Variant", status = HttpStatusCode.BadRequest)
return
}
is EtherealGambi.VariantFound -> {
val (variant, fileNameWithoutVariant) = variantResult

val path = (pathWithoutFile + fileNameWithoutVariant).joinToString("/")
val path = (pathWithoutFile + fileNameWithoutVariant).joinToString("/")

withContext(Dispatchers.IO) {
val imageInfo = m.createImageInfoForImage(path)
withContext(Dispatchers.IO) {
val imageInfo = m.createImageInfoForImage(path)

if (imageInfo == null) {
call.respondText("Image not found", status = HttpStatusCode.NotFound)
return@withContext
}
if (imageInfo == null) {
call.respondText("Image not found", status = HttpStatusCode.NotFound)
return@withContext
}

val file = imageInfo.createImageVariant(variant)
val file = imageInfo.createImageVariant(variant)

call.respondBytes(
file.readBytes(),
variant.imageType.contentType
)
}
call.respondBytes(
file.readBytes(),
variant.imageType.contentType
)
}
}

// Gets the original file
is EtherealGambi.OriginalFile -> {
val path = variantResult.path

val image = File(m.files, path).readBytes()
val simpleImageInfo = SimpleImageInfo(image)

call.respondBytes(
image,
contentType = when (simpleImageInfo.mimeType) {
"image/png" -> ContentType.Image.PNG
"image/gif" -> ContentType.Image.GIF
"image/jpeg" -> ContentType.Image.JPEG
else -> error("Unsupported mime type ${simpleImageInfo.mimeType}")
}
)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.perfectdreams.etherealgambi.backend.utils

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.perfectdreams.etherealgambi.backend.EtherealGambi
import net.perfectdreams.etherealgambi.data.DefaultImageVariant
import net.perfectdreams.etherealgambi.data.DefaultImageVariantPreset
import net.perfectdreams.etherealgambi.data.ImageInfoData
import net.perfectdreams.etherealgambi.data.ImageType
import net.perfectdreams.etherealgambi.data.ImageVariant
import net.perfectdreams.etherealgambi.data.ImageVariantInfoData
import net.perfectdreams.etherealgambi.data.ScaleDownToWidthImageVariant
import java.io.File

class JPEGImageInfo(
m: EtherealGambi,
data: ImageInfoData
) : ImageInfo(m, data) {
override fun getValidVariantPresets() = listOf(DefaultImageVariantPreset)

override suspend fun createImageVariant(
variant: ImageVariant
): File {
return mutexes.getOrPut(variant) { Mutex() }.withLock {
val storedVariant = data.variants.firstOrNull { it.variantAttributes == variant }

if (storedVariant != null) {
val storedVariantFile = File(m.generatedFiles, storedVariant.path)
if (storedVariant.optimizationVersion == EtherealGambi.OPTIMIZATION_VERSION && storedVariantFile.exists())
return@withLock storedVariantFile

data.variants.removeIf { it.variantAttributes == variant }
}

val originalImageFile = File(m.files, path)
val generatedImageFile = File(m.generatedFiles, "$folder/${fileName.name}${variant.variantWithPrefix()}.${variant.imageType.extension}")

when (variant) {
is DefaultImageVariant -> {
generatedImageFile.parentFile.mkdirs()

generatedImageFile.writeBytes(originalImageFile.readBytes())

// TODO: Use jpegoptim to optimize the GIF!

data.variants.add(
ImageVariantInfoData(
EtherealGambi.OPTIMIZATION_VERSION,
path,
variant,
generatedImageFile.length()
)
)

return@withLock generatedImageFile
}
is ScaleDownToWidthImageVariant -> error("JPEG does not support scale down variations yet!")
}
}
}
}

0 comments on commit 333cf86

Please sign in to comment.