mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-02 06:46:22 +01:00
Enhance original file instead of cached preview
This commit is contained in:
parent
192fe923a2
commit
6d0f612c7b
7 changed files with 123 additions and 40 deletions
|
@ -2,9 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/cache_manager_util.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/help_utils.dart';
|
||||
|
@ -64,10 +62,15 @@ class EnhanceHandler {
|
|||
return;
|
||||
}
|
||||
_log.info("[call] Selected: ${selected.name}");
|
||||
final imageUri = await _getFileUri();
|
||||
switch (selected) {
|
||||
case _Algorithm.zeroDce:
|
||||
await ImageProcessor.zeroDce(imageUri.toString(), file.filename);
|
||||
await ImageProcessor.zeroDce(
|
||||
"${account.url}/${file.path}",
|
||||
file.filename,
|
||||
headers: {
|
||||
"Authorization": Api.getAuthorizationHeaderValue(account),
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -104,22 +107,6 @@ class EnhanceHandler {
|
|||
),
|
||||
];
|
||||
|
||||
Future<Uri> _getFileUri() async {
|
||||
final f = await LargeImageCacheManager.inst.getSingleFile(
|
||||
api_util.getFilePreviewUrl(
|
||||
account,
|
||||
file,
|
||||
width: k.photoLargeSize,
|
||||
height: k.photoLargeSize,
|
||||
a: true,
|
||||
),
|
||||
headers: {
|
||||
"Authorization": Api.getAuthorizationHeaderValue(account),
|
||||
},
|
||||
);
|
||||
return f.absolute.uri;
|
||||
}
|
||||
|
||||
final Account account;
|
||||
final File file;
|
||||
|
||||
|
|
|
@ -5,11 +5,9 @@ import android.net.Uri
|
|||
interface MessageEvent
|
||||
|
||||
data class ImageProcessorCompletedEvent(
|
||||
val image: Uri,
|
||||
val result: Uri,
|
||||
) : MessageEvent
|
||||
|
||||
data class ImageProcessorFailedEvent(
|
||||
val image: Uri,
|
||||
val exception: Throwable,
|
||||
) : MessageEvent
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
package com.nkming.nc_photos.plugin
|
||||
|
||||
class PermissionException(message: String) : Exception(message)
|
||||
|
||||
class HttpException(statusCode: Int, message: String): Exception(message)
|
||||
|
|
|
@ -20,7 +20,8 @@ class ImageProcessorChannelHandler(context: Context) :
|
|||
"zeroDce" -> {
|
||||
try {
|
||||
zeroDce(
|
||||
call.argument("image")!!,
|
||||
call.argument("fileUrl")!!,
|
||||
call.argument("headers"),
|
||||
call.argument("filename")!!,
|
||||
result
|
||||
)
|
||||
|
@ -42,14 +43,18 @@ class ImageProcessorChannelHandler(context: Context) :
|
|||
}
|
||||
|
||||
private fun zeroDce(
|
||||
image: String, filename: String, result: MethodChannel.Result
|
||||
fileUrl: String, headers: Map<String, String>?, filename: String,
|
||||
result: MethodChannel.Result
|
||||
) {
|
||||
val intent = Intent(context, ImageProcessorService::class.java).apply {
|
||||
putExtra(
|
||||
ImageProcessorService.EXTRA_METHOD,
|
||||
ImageProcessorService.METHOD_ZERO_DCE
|
||||
)
|
||||
putExtra(ImageProcessorService.EXTRA_IMAGE, image)
|
||||
putExtra(ImageProcessorService.EXTRA_FILE_URL, fileUrl)
|
||||
putExtra(
|
||||
ImageProcessorService.EXTRA_HEADERS,
|
||||
headers?.let { HashMap(it) })
|
||||
putExtra(ImageProcessorService.EXTRA_FILENAME, filename)
|
||||
}
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
|
|
|
@ -18,12 +18,16 @@ import androidx.core.app.NotificationChannelCompat
|
|||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.nkming.nc_photos.plugin.image_processor.ZeroDce
|
||||
import java.io.File
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
class ImageProcessorService : Service() {
|
||||
companion object {
|
||||
const val EXTRA_METHOD = "method"
|
||||
const val METHOD_ZERO_DCE = "zero-dce"
|
||||
const val EXTRA_IMAGE = "image"
|
||||
const val EXTRA_FILE_URL = "fileUrl"
|
||||
const val EXTRA_HEADERS = "headers"
|
||||
const val EXTRA_FILENAME = "filename"
|
||||
|
||||
private const val NOTIFICATION_ID =
|
||||
|
@ -45,6 +49,7 @@ class ImageProcessorService : Service() {
|
|||
super.onCreate()
|
||||
wakeLock.acquire()
|
||||
createNotificationChannel()
|
||||
cleanUp()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -55,7 +60,7 @@ class ImageProcessorService : Service() {
|
|||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
assert(intent.hasExtra(EXTRA_METHOD))
|
||||
assert(intent.hasExtra(EXTRA_IMAGE))
|
||||
assert(intent.hasExtra(EXTRA_FILE_URL))
|
||||
if (!isForeground) {
|
||||
try {
|
||||
startForeground(NOTIFICATION_ID, buildNotification())
|
||||
|
@ -73,19 +78,23 @@ class ImageProcessorService : Service() {
|
|||
Log.e(TAG, "Unknown method: $method")
|
||||
// we can't call stopSelf here as it'll stop the service even if
|
||||
// there are commands running in the bg
|
||||
addCommand(
|
||||
ImageProcessorCommand(startId, "null", Uri.EMPTY, "")
|
||||
)
|
||||
addCommand(ImageProcessorCommand(startId, "null", "", null, ""))
|
||||
}
|
||||
}
|
||||
return START_REDELIVER_INTENT
|
||||
}
|
||||
|
||||
private fun onZeroDce(startId: Int, extras: Bundle) {
|
||||
val imageUri = Uri.parse(extras.getString(EXTRA_IMAGE)!!)
|
||||
val fileUrl = extras.getString(EXTRA_FILE_URL)!!
|
||||
|
||||
@Suppress("Unchecked_cast")
|
||||
val headers =
|
||||
extras.getSerializable(EXTRA_HEADERS) as HashMap<String, String>?
|
||||
val filename = extras.getString(EXTRA_FILENAME)!!
|
||||
addCommand(
|
||||
ImageProcessorCommand(startId, METHOD_ZERO_DCE, imageUri, filename)
|
||||
ImageProcessorCommand(
|
||||
startId, METHOD_ZERO_DCE, fileUrl, headers, filename
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -186,6 +195,17 @@ class ImageProcessorService : Service() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up temp files in case the service ended prematurely last time
|
||||
*/
|
||||
private fun cleanUp() {
|
||||
try {
|
||||
getTempDir(this).deleteRecursively()
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "[cleanUp] Failed while cleanUp", e)
|
||||
}
|
||||
}
|
||||
|
||||
private var isForeground = false
|
||||
private val cmds = mutableListOf<ImageProcessorCommand>()
|
||||
private var cmdTask: ImageProcessorCommandTask? = null
|
||||
|
@ -205,7 +225,8 @@ class ImageProcessorService : Service() {
|
|||
private data class ImageProcessorCommand(
|
||||
val startId: Int,
|
||||
val method: String,
|
||||
val uri: Uri,
|
||||
val fileUrl: String,
|
||||
val headers: Map<String, String>?,
|
||||
val filename: String,
|
||||
val args: Map<String, Any> = mapOf(),
|
||||
)
|
||||
|
@ -222,18 +243,62 @@ private open class ImageProcessorCommandTask(context: Context) :
|
|||
): MessageEvent {
|
||||
val cmd = params[0]!!
|
||||
return try {
|
||||
val outUri = handleCommand(cmd)
|
||||
ImageProcessorCompletedEvent(outUri)
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "[doInBackground] Failed while handleCommand", e)
|
||||
ImageProcessorFailedEvent(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCommand(cmd: ImageProcessorCommand): Uri {
|
||||
val file = downloadFile(cmd.fileUrl, cmd.headers)
|
||||
return try {
|
||||
val fileUri = Uri.fromFile(file)
|
||||
val output = when (cmd.method) {
|
||||
ImageProcessorService.METHOD_ZERO_DCE -> ZeroDce(context).infer(
|
||||
cmd.uri
|
||||
fileUri
|
||||
)
|
||||
else -> throw IllegalArgumentException(
|
||||
"Unknown method: ${cmd.method}"
|
||||
)
|
||||
}
|
||||
val uri = saveBitmap(output, cmd.filename)
|
||||
ImageProcessorCompletedEvent(cmd.uri, uri)
|
||||
} catch (e: Throwable) {
|
||||
ImageProcessorFailedEvent(cmd.uri, e)
|
||||
saveBitmap(output, cmd.filename)
|
||||
} finally {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadFile(
|
||||
fileUrl: String, headers: Map<String, String>?
|
||||
): File {
|
||||
Log.i(TAG, "[downloadFile] $fileUrl")
|
||||
return (URL(fileUrl).openConnection() as HttpURLConnection).apply {
|
||||
requestMethod = "GET"
|
||||
instanceFollowRedirects = true
|
||||
connectTimeout = 8000
|
||||
readTimeout = 15000
|
||||
for (entry in (headers ?: mapOf()).entries) {
|
||||
setRequestProperty(entry.key, entry.value)
|
||||
}
|
||||
}.use {
|
||||
val responseCode = it.responseCode
|
||||
if (responseCode / 100 == 2) {
|
||||
val file =
|
||||
File.createTempFile("img", null, getTempDir(context))
|
||||
file.outputStream().use { oStream ->
|
||||
it.inputStream.copyTo(oStream)
|
||||
}
|
||||
file
|
||||
} else {
|
||||
Log.e(
|
||||
TAG,
|
||||
"[downloadFile] Failed downloading file: HTTP$responseCode"
|
||||
)
|
||||
throw HttpException(
|
||||
responseCode, "Failed downloading file (HTTP$responseCode)"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,3 +313,14 @@ private open class ImageProcessorCommandTask(context: Context) :
|
|||
@SuppressLint("StaticFieldLeak")
|
||||
private val context = context
|
||||
}
|
||||
|
||||
private fun getTempDir(context: Context): File {
|
||||
val f = File(context.cacheDir, "imageProcessor")
|
||||
if (!f.exists()) {
|
||||
f.mkdirs()
|
||||
} else if (!f.isDirectory) {
|
||||
f.delete()
|
||||
f.mkdirs()
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.nkming.nc_photos.plugin
|
|||
|
||||
import android.app.PendingIntent
|
||||
import android.os.Build
|
||||
import java.net.HttpURLConnection
|
||||
|
||||
fun getPendingIntentFlagImmutable(): Int {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
|
@ -12,3 +13,12 @@ fun getPendingIntentFlagMutable(): Int {
|
|||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
PendingIntent.FLAG_MUTABLE else 0
|
||||
}
|
||||
|
||||
inline fun <T> HttpURLConnection.use(block: (HttpURLConnection) -> T): T {
|
||||
try {
|
||||
connect()
|
||||
return block(this)
|
||||
} finally {
|
||||
disconnect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,14 @@ import 'package:flutter/services.dart';
|
|||
import 'package:nc_photos_plugin/src/k.dart' as k;
|
||||
|
||||
class ImageProcessor {
|
||||
static Future<void> zeroDce(String image, String filename) =>
|
||||
static Future<void> zeroDce(
|
||||
String fileUrl,
|
||||
String filename, {
|
||||
Map<String, String>? headers,
|
||||
}) =>
|
||||
_methodChannel.invokeMethod("zeroDce", <String, dynamic>{
|
||||
"image": image,
|
||||
"fileUrl": fileUrl,
|
||||
"headers": headers,
|
||||
"filename": filename,
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue