mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-11 23:04:33 -05:00
Remote Image Processing (#1684)
* Update ServerConfig.kt * Update ConversionUtil.kt * Update Page.kt * Update ServerConfig.kt fixed deletions caused by ide * Update ServerConfig.kt * Update ServerConfig.kt * Cleanup * Post-processing terminology * More comments * Lint * Add known image mimes * Fix weird mime set/get * Implement different downloadConversions and serveConversions * Lint * Improve Post-Processing massivly * Fix thumbnail build * Use Array for headers * Actually fix headers * Actually fix headers 2 * Manually parse DownloadConversion * Cleanup parse * Fix write * Update TypeName * Optimize imports * Remove header type * Fix build --------- Co-authored-by: Syer10 <syer10@users.noreply.github.com>
This commit is contained in:
@@ -1,35 +1,28 @@
|
||||
package suwayomi.tachidesk.util
|
||||
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import libcore.net.MimeUtils
|
||||
import okhttp3.Headers
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import suwayomi.tachidesk.graphql.types.DownloadConversion
|
||||
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Files
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.getValue
|
||||
|
||||
object ConversionUtil {
|
||||
val logger = KotlinLogging.logger {}
|
||||
|
||||
public fun readImage(image: File): BufferedImage? {
|
||||
val readers = ImageIO.getImageReadersBySuffix(image.extension)
|
||||
image.inputStream().use {
|
||||
ImageIO.createImageInputStream(it).use { inputStream ->
|
||||
for (reader in readers) {
|
||||
try {
|
||||
reader.setInput(inputStream)
|
||||
return reader.read(0)
|
||||
} catch (e: Throwable) {
|
||||
logger.debug(e) { "Reader ${reader.javaClass.name} not suitable" }
|
||||
} finally {
|
||||
reader.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info { "No suitable image converter found for ${image.name}" }
|
||||
return null
|
||||
}
|
||||
|
||||
public fun readImage(
|
||||
fun readImage(
|
||||
image: InputStream,
|
||||
mimeType: String,
|
||||
): BufferedImage? {
|
||||
@@ -49,4 +42,100 @@ object ConversionUtil {
|
||||
logger.info { "No suitable image converter found for $mimeType" }
|
||||
return null
|
||||
}
|
||||
|
||||
private val networkService: NetworkHelper by injectLazy()
|
||||
|
||||
/**
|
||||
* Send image to external HTTP service for post-processing
|
||||
* Returns the processed image stream or null if failed
|
||||
*/
|
||||
suspend fun imageHttpPostProcess(
|
||||
imageFile: File,
|
||||
conversion: DownloadConversion,
|
||||
mimeType: String,
|
||||
): InputStream? =
|
||||
try {
|
||||
logger.debug { "Sending ${imageFile.name} to HTTP converter: ${conversion.target}" }
|
||||
|
||||
val requestBody =
|
||||
MultipartBody
|
||||
.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart(
|
||||
"image",
|
||||
imageFile.name,
|
||||
imageFile.asRequestBody(mimeType.toMediaType()),
|
||||
).build()
|
||||
|
||||
val client =
|
||||
networkService.client
|
||||
.newBuilder()
|
||||
.apply {
|
||||
if (conversion.callTimeout != null) {
|
||||
callTimeout(conversion.callTimeout!!)
|
||||
}
|
||||
if (conversion.connectTimeout != null) {
|
||||
connectTimeout(conversion.connectTimeout!!)
|
||||
}
|
||||
}.build()
|
||||
|
||||
val response =
|
||||
client
|
||||
.newCall(
|
||||
POST(
|
||||
conversion.target,
|
||||
body = requestBody,
|
||||
headers =
|
||||
Headers
|
||||
.Builder()
|
||||
.apply {
|
||||
conversion.headers?.forEach {
|
||||
set(it.key, it.value)
|
||||
}
|
||||
}.build(),
|
||||
),
|
||||
).await()
|
||||
logger.debug { "HTTP conversion successful for ${imageFile.name}" }
|
||||
response.body.byteStream()
|
||||
} catch (e: Exception) {
|
||||
logger.warn(e) { "HTTP conversion failed for ${imageFile.name}" }
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload that takes InputStream and mimeType, creates temp file for HTTP upload
|
||||
*/
|
||||
suspend fun imageHttpPostProcess(
|
||||
inputStream: InputStream,
|
||||
mimeType: String,
|
||||
conversion: DownloadConversion,
|
||||
): InputStream? =
|
||||
try {
|
||||
// Create temporary file from input stream
|
||||
val extension =
|
||||
MimeUtils.guessExtensionFromMimeType(mimeType)
|
||||
?: mimeType.substringAfter('/')
|
||||
|
||||
val tempFile = Files.createTempFile("conversion", ".$extension").toFile()
|
||||
tempFile.outputStream().use { output ->
|
||||
inputStream.copyTo(output)
|
||||
}
|
||||
|
||||
// Convert using file method
|
||||
val result = imageHttpPostProcess(tempFile, conversion, mimeType)
|
||||
|
||||
// Clean up temp file
|
||||
tempFile.delete()
|
||||
|
||||
result
|
||||
} catch (e: Exception) {
|
||||
logger.warn(e) { "Failed to create temp file for HTTP converter" }
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a DownloadConversion target is an HTTP URL
|
||||
*/
|
||||
fun isHttpPostProcess(conversion: DownloadConversion): Boolean =
|
||||
conversion.target.startsWith("http://") || conversion.target.startsWith("https://")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user