musikr: introduce cover cleanup
Helps reduce overall memory use.
This commit is contained in:
parent
7b35ba840b
commit
8b58f357cb
4 changed files with 40 additions and 15 deletions
|
@ -432,7 +432,7 @@ constructor(
|
||||||
// Old cover revisions may be lying around, even during a normal refresh due
|
// Old cover revisions may be lying around, even during a normal refresh due
|
||||||
// to really lucky cancellations. Clean those up now that it's impossible for
|
// to really lucky cancellations. Clean those up now that it's impossible for
|
||||||
// the rest of the app to be using them.
|
// the rest of the app to be using them.
|
||||||
covers.cleanup(context)
|
covers.cleanup(newLibrary)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun emitIndexingProgress(progress: IndexingProgress) {
|
private suspend fun emitIndexingProgress(progress: IndexingProgress) {
|
||||||
|
|
|
@ -19,9 +19,11 @@
|
||||||
package org.oxycblt.auxio.music
|
package org.oxycblt.auxio.music
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import java.io.File
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.oxycblt.musikr.Library
|
||||||
import org.oxycblt.musikr.cover.Cover
|
import org.oxycblt.musikr.cover.Cover
|
||||||
import org.oxycblt.musikr.cover.CoverFiles
|
import org.oxycblt.musikr.cover.CoverFiles
|
||||||
import org.oxycblt.musikr.cover.CoverFormat
|
import org.oxycblt.musikr.cover.CoverFormat
|
||||||
|
@ -29,8 +31,11 @@ import org.oxycblt.musikr.cover.CoverParams
|
||||||
import org.oxycblt.musikr.cover.MutableStoredCovers
|
import org.oxycblt.musikr.cover.MutableStoredCovers
|
||||||
import org.oxycblt.musikr.cover.StoredCovers
|
import org.oxycblt.musikr.cover.StoredCovers
|
||||||
|
|
||||||
class RevisionedCovers(private val revision: UUID, private val inner: MutableStoredCovers) :
|
class RevisionedCovers(
|
||||||
MutableStoredCovers {
|
private val rootDir: File,
|
||||||
|
private val revision: UUID,
|
||||||
|
private val inner: MutableStoredCovers
|
||||||
|
) : MutableStoredCovers {
|
||||||
override suspend fun obtain(id: String): RevisionedCover? {
|
override suspend fun obtain(id: String): RevisionedCover? {
|
||||||
val (coverId, coverRevision) = parse(id) ?: return null
|
val (coverId, coverRevision) = parse(id) ?: return null
|
||||||
if (coverRevision != revision) return null
|
if (coverRevision != revision) return null
|
||||||
|
@ -41,24 +46,27 @@ class RevisionedCovers(private val revision: UUID, private val inner: MutableSto
|
||||||
return inner.write(data)?.let { RevisionedCover(revision, it) }
|
return inner.write(data)?.let { RevisionedCover(revision, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun cleanup(context: Context) =
|
override suspend fun cleanup(assuming: Library) {
|
||||||
|
inner.cleanup(assuming)
|
||||||
|
|
||||||
|
// Destroy old revisions no longer being used.
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val exclude = revision.toString()
|
val exclude = revision.toString()
|
||||||
context.filesDir
|
rootDir.listFiles { file -> file.name != exclude }?.forEach { it.deleteRecursively() }
|
||||||
.resolve("covers")
|
|
||||||
.listFiles { file -> file.name != exclude }
|
|
||||||
?.forEach { it.deleteRecursively() }
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
suspend fun at(context: Context, revision: UUID): RevisionedCovers {
|
suspend fun at(context: Context, revision: UUID): RevisionedCovers {
|
||||||
val dir =
|
val rootDir: File
|
||||||
withContext(Dispatchers.IO) {
|
val revisionDir: File
|
||||||
context.filesDir.resolve("covers/${revision}").apply { mkdirs() }
|
withContext(Dispatchers.IO) {
|
||||||
}
|
rootDir = context.filesDir.resolve("covers").apply { mkdirs() }
|
||||||
return RevisionedCovers(
|
revisionDir = rootDir.resolve(revision.toString()).apply { mkdirs() }
|
||||||
revision,
|
}
|
||||||
StoredCovers.from(CoverFiles.at(dir), CoverFormat.jpeg(CoverParams.of(750, 80))))
|
val files = CoverFiles.at(revisionDir)
|
||||||
|
val format = CoverFormat.jpeg(CoverParams.of(750, 80))
|
||||||
|
return RevisionedCovers(rootDir, revision, StoredCovers.from(files, format))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parse(id: String): Pair<String, UUID>? {
|
private fun parse(id: String): Pair<String, UUID>? {
|
||||||
|
|
|
@ -32,6 +32,8 @@ interface CoverFiles {
|
||||||
|
|
||||||
suspend fun write(name: String, block: suspend (OutputStream) -> Unit): CoverFile?
|
suspend fun write(name: String, block: suspend (OutputStream) -> Unit): CoverFile?
|
||||||
|
|
||||||
|
suspend fun deleteWhere(block: (String) -> Boolean)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
suspend fun at(dir: File): CoverFiles {
|
suspend fun at(dir: File): CoverFiles {
|
||||||
withContext(Dispatchers.IO) { check(dir.exists() && dir.isDirectory) }
|
withContext(Dispatchers.IO) { check(dir.exists() && dir.isDirectory) }
|
||||||
|
@ -83,6 +85,12 @@ private class CoverFilesImpl(private val dir: File) : CoverFiles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteWhere(block: (String) -> Boolean) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
dir.listFiles { file -> block(file.name) }?.forEach { it.deleteRecursively() }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CoverFileImpl(private val file: File) : CoverFile {
|
private class CoverFileImpl(private val file: File) : CoverFile {
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr.cover
|
package org.oxycblt.musikr.cover
|
||||||
|
|
||||||
|
import org.oxycblt.musikr.Library
|
||||||
|
|
||||||
interface StoredCovers {
|
interface StoredCovers {
|
||||||
suspend fun obtain(id: String): Cover?
|
suspend fun obtain(id: String): Cover?
|
||||||
|
|
||||||
|
@ -32,6 +34,8 @@ interface StoredCovers {
|
||||||
|
|
||||||
interface MutableStoredCovers : StoredCovers {
|
interface MutableStoredCovers : StoredCovers {
|
||||||
suspend fun write(data: ByteArray): Cover?
|
suspend fun write(data: ByteArray): Cover?
|
||||||
|
|
||||||
|
suspend fun cleanup(assuming: Library)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileStoredCovers(
|
private class FileStoredCovers(
|
||||||
|
@ -49,6 +53,11 @@ private class FileStoredCovers(
|
||||||
?.let { FileCover(id, it) }
|
?.let { FileCover(id, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun cleanup(assuming: Library) {
|
||||||
|
val used = assuming.songs.mapNotNullTo(mutableSetOf()) { it.cover?.id?.let(::getFileName) }
|
||||||
|
coverFiles.deleteWhere { it !in used }
|
||||||
|
}
|
||||||
|
|
||||||
private fun getFileName(id: String) = "$id.${coverFormat.extension}"
|
private fun getFileName(id: String) = "$id.${coverFormat.extension}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue