musikr: separate cover files/format
This commit is contained in:
parent
b8178056f5
commit
80c97cbea1
4 changed files with 32 additions and 27 deletions
|
@ -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) =
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue