musikr: separate cover files/format

This commit is contained in:
Alexander Capehart 2024-12-26 19:29:57 -05:00
parent b8178056f5
commit 80c97cbea1
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
4 changed files with 32 additions and 27 deletions

View file

@ -47,7 +47,7 @@ class RevisionedCovers(private val revision: UUID, private val inner: MutableSto
context.filesDir.resolve("covers/${revision}").apply { mkdirs() }
}
return RevisionedCovers(
revision, StoredCovers.from(CoverFiles.at(dir, CoverFormat.jpeg())))
revision, StoredCovers.from(CoverFiles.at(dir), CoverFormat.jpeg()))
}
suspend fun cleanup(context: Context, exclude: UUID) =

View file

@ -21,20 +21,21 @@ package org.oxycblt.musikr.cover
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
interface CoverFiles {
suspend fun find(id: String): CoverFile?
suspend fun find(name: String): CoverFile?
suspend fun write(id: String, data: ByteArray): CoverFile?
suspend fun write(name: String, block: suspend (OutputStream) -> Unit): CoverFile?
companion object {
suspend fun at(dir: File, format: CoverFormat): CoverFiles {
suspend fun at(dir: File): CoverFiles {
withContext(Dispatchers.IO) { check(dir.exists() && dir.isDirectory) }
return CoverFilesImpl(dir, format)
return CoverFilesImpl(dir)
}
}
}
@ -43,8 +44,7 @@ interface CoverFile {
suspend fun open(): InputStream?
}
private class CoverFilesImpl(private val dir: File, private val coverFormat: CoverFormat) :
CoverFiles {
private class CoverFilesImpl(private val dir: File) : CoverFiles {
private val fileMutexes = mutableMapOf<String, Mutex>()
private val mapMutex = Mutex()
@ -52,25 +52,25 @@ private class CoverFilesImpl(private val dir: File, private val coverFormat: Cov
return mapMutex.withLock { fileMutexes.getOrPut(file) { Mutex() } }
}
override suspend fun find(id: String): CoverFile? =
override suspend fun find(name: String): CoverFile? =
withContext(Dispatchers.IO) {
try {
File(dir, getTargetFilePath(id)).takeIf { it.exists() }?.let { CoverFileImpl(it) }
File(dir, name).takeIf { it.exists() }?.let { CoverFileImpl(it) }
} catch (e: IOException) {
null
}
}
override suspend fun write(id: String, data: ByteArray): CoverFile? {
val fileMutex = getMutexForFile(id)
override suspend fun write(name: String, block: suspend (OutputStream) -> Unit): CoverFile? {
val fileMutex = getMutexForFile(name)
return fileMutex.withLock {
val targetFile = File(dir, getTargetFilePath(id))
val targetFile = File(dir, name)
if (!targetFile.exists()) {
withContext(Dispatchers.IO) {
val tempFile = File(dir, getTempFilePath(id))
val tempFile = File(dir, "$name.tmp")
try {
tempFile.outputStream().use { coverFormat.transcodeInto(data, it) }
block(tempFile.outputStream())
tempFile.renameTo(targetFile)
CoverFileImpl(targetFile)
} catch (e: IOException) {
@ -83,10 +83,6 @@ private class CoverFilesImpl(private val dir: File, private val coverFormat: Cov
}
}
}
private fun getTargetFilePath(name: String) = "cover_${name}.${coverFormat.extension}"
private fun getTempFilePath(name: String) = "${getTargetFilePath(name)}.tmp"
}
private class CoverFileImpl(private val file: File) : CoverFile {

View file

@ -20,7 +20,7 @@ package org.oxycblt.musikr.cover
import java.security.MessageDigest
internal interface CoverIdentifier {
interface CoverIdentifier {
suspend fun identify(data: ByteArray): String
companion object {

View file

@ -22,8 +22,11 @@ interface StoredCovers {
suspend fun obtain(id: String): Cover?
companion object {
fun from(files: CoverFiles): MutableStoredCovers =
FileStoredCovers(CoverIdentifier.md5(), files)
fun from(
coverFiles: CoverFiles,
coverFormat: CoverFormat,
identifier: CoverIdentifier = CoverIdentifier.md5()
): MutableStoredCovers = FileStoredCovers(coverFiles, coverFormat, identifier)
}
}
@ -32,15 +35,21 @@ interface MutableStoredCovers : StoredCovers {
}
private class FileStoredCovers(
private val coverFiles: CoverFiles,
private val coverFormat: CoverFormat,
private val coverIdentifier: CoverIdentifier,
private val coverFiles: CoverFiles
) : StoredCovers, MutableStoredCovers {
override suspend fun obtain(id: String) = coverFiles.find(id)?.let { FileCover(id, it) }
override suspend fun obtain(id: String) =
coverFiles.find(getFileName(id))?.let { FileCover(id, it) }
override suspend fun write(data: ByteArray) =
coverIdentifier.identify(data).let { id ->
coverFiles.write(id, data)?.let { FileCover(id, it) }
}
override suspend fun write(data: ByteArray): Cover? {
val id = coverIdentifier.identify(data)
return coverFiles
.write(getFileName(id)) { coverFormat.transcodeInto(data, it) }
?.let { FileCover(id, it) }
}
private fun getFileName(id: String) = "$id.${coverFormat.extension}"
}
private class FileCover(override val id: String, private val coverFile: CoverFile) : Cover {