Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ fun Progress.toWorkData(): Data =
.putInt(KEY_TOTAL, total)
.build()

fun Progress.Companion.fromWorkData(data: Data): Progress =
Progress(
current = data.getInt(KEY_CURRENT, 0),
total = data.getInt(KEY_TOTAL, -1),
fun Progress.Companion.fromWorkData(data: Data): Progress? {
with(data.keyValueMap) {
if (isEmpty()) return null
if (!containsKey(KEY_CURRENT)) return null
if (!containsKey(KEY_TOTAL)) return null
}

return Progress(
current = data.getInt(KEY_CURRENT, Progress.INDETERMINATE.current),
total = data.getInt(KEY_TOTAL, Progress.INDETERMINATE.total),
)
}

fun Progress.Companion.fromWorkInfo(workInfo: WorkInfo?): Progress? =
when (workInfo?.state) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@ import androidx.sqlite.driver.bundled.SQLITE_OPEN_CREATE
import androidx.sqlite.driver.bundled.SQLITE_OPEN_READWRITE
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import co.touchlab.kermit.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.io.files.SystemFileSystem
import xyz.sevive.arcaeaoffline.core.Progress
import xyz.sevive.arcaeaoffline.core.ocr.ImageHashesDatabaseBuilder
import xyz.sevive.arcaeaoffline.data.OcrDependencyPaths
import xyz.sevive.arcaeaoffline.helpers.ArcaeaPackageHelper
import xyz.sevive.arcaeaoffline.helpers.toWorkData
import kotlin.time.Duration.Companion.milliseconds

class ImageHashesDatabaseBuilderJob(
context: Context,
Expand All @@ -27,45 +33,55 @@ class ImageHashesDatabaseBuilderJob(
companion object {
const val NAME = "ImageHashesDatabaseBuilderJob"
private const val LOG_TAG = "ImageHashesDbBuilderJob"
const val KEY_PROGRESS = "progress"
const val KEY_PROGRESS_TOTAL = "progressTotal"
}

private val progressFlow = MutableStateFlow(Progress.INDETERMINATE)

override suspend fun doWork(): Result {
val ocrDependencyPaths = OcrDependencyPaths()
val path = ocrDependencyPaths.imageHashesDatabaseFile
val arcaeaPackageHelper = ArcaeaPackageHelper(applicationContext)

val collectScope = CoroutineScope(Dispatchers.Default)

try {
withContext(Dispatchers.IO) {
if (SystemFileSystem.exists(path)) SystemFileSystem.delete(path)
SystemFileSystem.createDirectories(path.parent ?: error("$path has no parent"))
}

BundledSQLiteDriver()
.open(
path.toString(),
SQLITE_OPEN_READWRITE.or(SQLITE_OPEN_CREATE),
).use { conn ->
val builder = ImageHashesDatabaseBuilder(conn)

collectScope.launch {
builder.buildProgress.collectLatest {
setProgress(
workDataOf(KEY_PROGRESS to it?.first, KEY_PROGRESS_TOTAL to it?.second),
)
}
return coroutineScope {
@OptIn(FlowPreview::class)
val progressPublishJob =
launch {
progressFlow
.sample(100.milliseconds)
.collect { setProgress(it.toWorkData()) }
}

arcaeaPackageHelper.fillHashesDatabaseBuilderTasks(builder)
builder.build(hashSize = 16, highFreqFactor = 4)
arcaeaPackageHelper.buildHashesDatabaseCleanUp()
withContext(Dispatchers.IO) {
if (SystemFileSystem.exists(path)) SystemFileSystem.delete(path)
SystemFileSystem.createDirectories(path.parent ?: error("$path has no parent"))
}

return Result.success()
BundledSQLiteDriver()
.open(
path.toString(),
SQLITE_OPEN_READWRITE.or(SQLITE_OPEN_CREATE),
).use { conn ->
val builder = ImageHashesDatabaseBuilder(conn)

val progressUpdateJob =
launch {
builder.buildProgress.collect { progressFlow.value = it ?: Progress.INDETERMINATE }
}

arcaeaPackageHelper.fillHashesDatabaseBuilderTasks(builder)
builder.build(hashSize = 16, highFreqFactor = 4)
arcaeaPackageHelper.buildHashesDatabaseCleanUp()

progressUpdateJob.cancelAndJoin()
}

progressPublishJob.cancelAndJoin()
Result.success()
}
} catch (e: Exception) {
if (e is CancellationException) throw e

logger.e(e) { "Error building image hashes database" }
return Result.failure()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class OcrQueueProcessingJob(
;

companion object {
fun fromInt(value: Int) = entries.firstOrNull { it.value == value } ?: NORMAL
fun fromInt(value: Int) = entries.firstOrNull { it.value == value }
}
}

Expand All @@ -75,9 +75,16 @@ class OcrQueueProcessingJob(
val parallelCount: Int,
)

private fun parseRunMode(): RunMode {
val runModeInput = inputData.getInt(DATA_RUN_MODE, 0)
val result = RunMode.fromInt(runModeInput)
if (result == null) logger.w { "Invalid RunMode $runModeInput, falling back to ${RunMode.NORMAL}" }
return result ?: RunMode.NORMAL
}

private fun getWorkOptions(): WorkOptions =
WorkOptions(
runMode = RunMode.fromInt(inputData.getInt(DATA_RUN_MODE, RunMode.NORMAL.value)),
runMode = parseRunMode(),
parallelCount =
inputData
.getInt(
Expand Down
Loading
Loading