mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-21 15:02:21 +02:00
refactor(android): share the SAF scan percent and CUE helpers
The two SAF scans repeated the same progress arithmetic and CUE steps. Only the runs that are identical are shared: - safScanProgressPct(done, total) is the Int/Int to Double * 100.0 expression all six percent sites used. The incremental scan keeps its zero-total guard (100.0), the full scan's call sites keep relying on a non-zero total as before. - reportScanned() replaces the two identical scanned/error/percent updates in scanSafTree. The CUE-referenced audio skip keeps its own update, which never set errorCount. - reportProcessed() moves above the changed-CUE loop, whose inline copy of its body it now replaces. - resolveCueSheetAudio() covers the FILE line extraction plus sibling lookup that both changed-CUE loops and the unchanged-CUE loop ran. - scanSafCueTracks() covers the audio name, timestamp, cover key and scanCueFromUri steps. The scans fall back to different timestamps when the audio's own cannot be read, so the fallback is a lambda and still runs only on that failure. Cancellation checks, checkpoint writes, flushes, the copy/delete of the temporary CUE file and every counter update stay in place and order; progress values and the scan output are unchanged.
This commit is contained in:
1 parent
eeda955363
commit
e80ae1b1da
2 files changed
+105
-93
No files matched your search
@@ -100,6 +100,10 @@ internal fun MainActivity.safScanStopRequested(cancelled: () -> Boolean = { fals
|
||||
return safScanCancel || cancelled()
|
||||
}
|
||||
|
||||
/** The `progress_pct` value for [done] of [total] items; callers guard zero. */
|
||||
private fun safScanProgressPct(done: Int, total: Int): Double =
|
||||
done.toDouble() / total.toDouble() * 100.0
|
||||
|
||||
internal fun MainActivity.scanSafTree(
|
||||
treeUriStr: String,
|
||||
ndjsonOutputPath: String? = null,
|
||||
@@ -231,7 +235,7 @@ internal fun MainActivity.scanSafTree(
|
||||
it.totalFiles = totalItems
|
||||
if (totalItems > 0 && earlyCheckpoint.isNotEmpty()) {
|
||||
it.scannedFiles = earlyCheckpoint.size.coerceAtMost(totalItems)
|
||||
it.progressPct = it.scannedFiles.toDouble() / totalItems.toDouble() * 100.0
|
||||
it.progressPct = safScanProgressPct(it.scannedFiles, totalItems)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +305,15 @@ internal fun MainActivity.scanSafTree(
|
||||
var scanned = checkpoint.size.coerceAtMost(totalItems)
|
||||
var errors = traversalErrors
|
||||
|
||||
fun reportScanned() {
|
||||
val pct = safScanProgressPct(scanned, totalItems)
|
||||
updateSafScanProgress {
|
||||
it.scannedFiles = scanned
|
||||
it.errorCount = errors
|
||||
it.progressPct = pct
|
||||
}
|
||||
}
|
||||
|
||||
val cueReferencedAudioUris = mutableSetOf<String>()
|
||||
|
||||
for (cue in cueFiles) {
|
||||
@@ -327,13 +340,11 @@ internal fun MainActivity.scanSafTree(
|
||||
continue
|
||||
}
|
||||
|
||||
val audioFileName = extractCueAudioFileName(tempCuePath)
|
||||
|
||||
val audioDoc = resolveCueAudioSibling(
|
||||
parentDir = parentDir,
|
||||
cueName = cueName,
|
||||
audioFileName = audioFileName,
|
||||
childLookupCache = safChildLookupCache,
|
||||
val audioDoc = resolveCueSheetAudio(
|
||||
tempCuePath,
|
||||
parentDir,
|
||||
cueName,
|
||||
safChildLookupCache,
|
||||
)
|
||||
|
||||
if (audioDoc == null) {
|
||||
@@ -349,26 +360,12 @@ internal fun MainActivity.scanSafTree(
|
||||
continue
|
||||
}
|
||||
|
||||
val audioName = try { audioDoc.name ?: "audio.flac" } catch (_: Exception) { "audio.flac" }
|
||||
val audioLastModified = try { audioDoc.lastModified() } catch (_: Exception) { cueDoc.lastModified() }
|
||||
val coverCacheKey = buildLibraryCoverCacheKey(
|
||||
audioDoc.uri.toString(),
|
||||
audioLastModified,
|
||||
)
|
||||
|
||||
val cueLastModified = cue.lastModified
|
||||
|
||||
val cueResultsJson = scanCueFromUri(
|
||||
coreBackend,
|
||||
val cueArray = scanSafCueTracks(
|
||||
tempCuePath,
|
||||
audioDoc.uri,
|
||||
audioName,
|
||||
cueDoc.uri.toString(),
|
||||
cueLastModified,
|
||||
coverCacheKey,
|
||||
)
|
||||
|
||||
val cueArray = JSONArray(cueResultsJson)
|
||||
audioDoc,
|
||||
cueUri,
|
||||
cue.lastModified,
|
||||
) { cueDoc.lastModified() }
|
||||
for (j in 0 until cueArray.length()) {
|
||||
putResult(cueArray.getJSONObject(j))
|
||||
}
|
||||
@@ -383,12 +380,7 @@ internal fun MainActivity.scanSafTree(
|
||||
}
|
||||
|
||||
scanned++
|
||||
val pct = scanned.toDouble() / totalItems.toDouble() * 100.0
|
||||
updateSafScanProgress {
|
||||
it.scannedFiles = scanned
|
||||
it.errorCount = errors
|
||||
it.progressPct = pct
|
||||
}
|
||||
reportScanned()
|
||||
}
|
||||
|
||||
data class SafAudioScanOutcome(
|
||||
@@ -417,7 +409,7 @@ internal fun MainActivity.scanSafTree(
|
||||
if (cueReferencedAudioUris.contains(stableUri)) {
|
||||
recordCheckpoint(stableUri, lastModified)
|
||||
scanned++
|
||||
val pct = scanned.toDouble() / totalItems.toDouble() * 100.0
|
||||
val pct = safScanProgressPct(scanned, totalItems)
|
||||
updateSafScanProgress {
|
||||
it.scannedFiles = scanned
|
||||
it.progressPct = pct
|
||||
@@ -485,12 +477,7 @@ internal fun MainActivity.scanSafTree(
|
||||
}
|
||||
|
||||
scanned++
|
||||
val pct = scanned.toDouble() / totalItems.toDouble() * 100.0
|
||||
updateSafScanProgress {
|
||||
it.scannedFiles = scanned
|
||||
it.errorCount = errors
|
||||
it.progressPct = pct
|
||||
}
|
||||
reportScanned()
|
||||
}
|
||||
if (!completed) {
|
||||
ndjsonWriter?.close()
|
||||
@@ -717,6 +704,20 @@ internal fun MainActivity.scanSafTreeIncremental(
|
||||
var scanned = 0
|
||||
var errors = traversalErrors
|
||||
|
||||
fun reportProcessed() {
|
||||
val processed = skippedCount + scanned
|
||||
val pct = if (totalFiles > 0) {
|
||||
safScanProgressPct(processed, totalFiles)
|
||||
} else {
|
||||
100.0
|
||||
}
|
||||
updateSafScanProgress {
|
||||
it.scannedFiles = processed
|
||||
it.errorCount = errors
|
||||
it.progressPct = pct
|
||||
}
|
||||
}
|
||||
|
||||
val cueReferencedAudioUris = mutableSetOf<String>()
|
||||
|
||||
for ((cueDoc, parentDir, cueName, cueLastModified) in cueFilesToScan) {
|
||||
@@ -744,13 +745,11 @@ internal fun MainActivity.scanSafTreeIncremental(
|
||||
continue
|
||||
}
|
||||
|
||||
val audioFileName = extractCueAudioFileName(tempCuePath)
|
||||
|
||||
val audioDoc = resolveCueAudioSibling(
|
||||
parentDir = parentDir,
|
||||
cueName = cueName,
|
||||
audioFileName = audioFileName,
|
||||
childLookupCache = safChildLookupCache,
|
||||
val audioDoc = resolveCueSheetAudio(
|
||||
tempCuePath,
|
||||
parentDir,
|
||||
cueName,
|
||||
safChildLookupCache,
|
||||
)
|
||||
|
||||
if (audioDoc == null) {
|
||||
@@ -762,24 +761,12 @@ internal fun MainActivity.scanSafTreeIncremental(
|
||||
|
||||
cueReferencedAudioUris.add(audioDoc.uri.toString())
|
||||
|
||||
val audioName = try { audioDoc.name ?: "audio.flac" } catch (_: Exception) { "audio.flac" }
|
||||
val audioLastModified = try { audioDoc.lastModified() } catch (_: Exception) { cueLastModified }
|
||||
val coverCacheKey = buildLibraryCoverCacheKey(
|
||||
audioDoc.uri.toString(),
|
||||
audioLastModified,
|
||||
)
|
||||
|
||||
val cueResultsJson = scanCueFromUri(
|
||||
coreBackend,
|
||||
val cueArray = scanSafCueTracks(
|
||||
tempCuePath,
|
||||
audioDoc.uri,
|
||||
audioName,
|
||||
audioDoc,
|
||||
cueDoc.uri.toString(),
|
||||
cueLastModified,
|
||||
coverCacheKey,
|
||||
)
|
||||
|
||||
val cueArray = JSONArray(cueResultsJson)
|
||||
) { cueLastModified }
|
||||
for (j in 0 until cueArray.length()) {
|
||||
val trackObj = cueArray.getJSONObject(j)
|
||||
putFile(trackObj)
|
||||
@@ -797,17 +784,7 @@ internal fun MainActivity.scanSafTreeIncremental(
|
||||
}
|
||||
|
||||
scanned++
|
||||
val processed = skippedCount + scanned
|
||||
val pct = if (totalFiles > 0) {
|
||||
processed.toDouble() / totalFiles.toDouble() * 100.0
|
||||
} else {
|
||||
100.0
|
||||
}
|
||||
updateSafScanProgress {
|
||||
it.scannedFiles = processed
|
||||
it.errorCount = errors
|
||||
it.progressPct = pct
|
||||
}
|
||||
reportProcessed()
|
||||
}
|
||||
|
||||
for ((cueDoc, parentDir, cueName) in unchangedCueFiles) {
|
||||
@@ -815,12 +792,11 @@ internal fun MainActivity.scanSafTreeIncremental(
|
||||
try {
|
||||
tempCue = copyUriToTemp(coreBackend, cueDoc.uri, ".cue", cueName)
|
||||
if (tempCue != null) {
|
||||
val audioFileName = extractCueAudioFileName(tempCue)
|
||||
val audioDoc = resolveCueAudioSibling(
|
||||
parentDir = parentDir,
|
||||
cueName = cueName,
|
||||
audioFileName = audioFileName,
|
||||
childLookupCache = safChildLookupCache,
|
||||
val audioDoc = resolveCueSheetAudio(
|
||||
tempCue,
|
||||
parentDir,
|
||||
cueName,
|
||||
safChildLookupCache,
|
||||
)
|
||||
if (audioDoc != null) {
|
||||
cueReferencedAudioUris.add(audioDoc.uri.toString())
|
||||
@@ -845,20 +821,6 @@ internal fun MainActivity.scanSafTreeIncremental(
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
fun reportProcessed() {
|
||||
val processed = skippedCount + scanned
|
||||
val pct = if (totalFiles > 0) {
|
||||
processed.toDouble() / totalFiles.toDouble() * 100.0
|
||||
} else {
|
||||
100.0
|
||||
}
|
||||
updateSafScanProgress {
|
||||
it.scannedFiles = processed
|
||||
it.errorCount = errors
|
||||
it.progressPct = pct
|
||||
}
|
||||
}
|
||||
|
||||
val pendingAudio = mutableListOf<ChangedAudio>()
|
||||
for (audio in audioFiles) {
|
||||
if (safScanStopRequested(cancelled)) return cancelledIncrementalResult()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.zarz.spotiflac
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import org.json.JSONArray
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
|
||||
@@ -101,3 +102,52 @@ internal fun MainActivity.resolveCueAudioSibling(
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/** Resolves the audio document named by a CUE sheet copied to [tempCuePath]. */
|
||||
internal fun MainActivity.resolveCueSheetAudio(
|
||||
tempCuePath: String,
|
||||
parentDir: DocumentFile,
|
||||
cueName: String,
|
||||
childLookupCache: MutableMap<String, Map<String, DocumentFile>>,
|
||||
): DocumentFile? {
|
||||
val audioFileName = extractCueAudioFileName(tempCuePath)
|
||||
|
||||
return resolveCueAudioSibling(
|
||||
parentDir = parentDir,
|
||||
cueName = cueName,
|
||||
audioFileName = audioFileName,
|
||||
childLookupCache = childLookupCache,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the virtual track rows of a CUE sheet against its resolved audio.
|
||||
* [audioLastModifiedFallback] runs only when the audio's own timestamp
|
||||
* cannot be read; the two scans fall back to different values.
|
||||
*/
|
||||
internal fun MainActivity.scanSafCueTracks(
|
||||
tempCuePath: String,
|
||||
audioDoc: DocumentFile,
|
||||
cueUri: String,
|
||||
cueLastModified: Long,
|
||||
audioLastModifiedFallback: () -> Long,
|
||||
): JSONArray {
|
||||
val audioName = try { audioDoc.name ?: "audio.flac" } catch (_: Exception) { "audio.flac" }
|
||||
val audioLastModified = try { audioDoc.lastModified() } catch (_: Exception) { audioLastModifiedFallback() }
|
||||
val coverCacheKey = buildLibraryCoverCacheKey(
|
||||
audioDoc.uri.toString(),
|
||||
audioLastModified,
|
||||
)
|
||||
|
||||
val cueResultsJson = scanCueFromUri(
|
||||
coreBackend,
|
||||
tempCuePath,
|
||||
audioDoc.uri,
|
||||
audioName,
|
||||
cueUri,
|
||||
cueLastModified,
|
||||
coverCacheKey,
|
||||
)
|
||||
|
||||
return JSONArray(cueResultsJson)
|
||||
}
|
||||
Reference in new issue
Block a user