mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-26 22:11:03 +02:00
fix(native-worker): never delete a published download over bookkeeping failures
The finalizer's catch-all cleanup deleted the finished output for any exception, including ones thrown after the file was already published to SAF. A failed history insert (SQLITE_BUSY against the concurrently open Flutter connection, disk full, or a schema-version mismatch that would throw on every download), an external .lrc write error, or a quality-metadata refresh failure destroyed a complete, valid track and forced a full re-download. - Track outputPublished and skip cleanupFailedFinalizationOutput once the file reached its final destination - Make the history insert, external LRC write, and quality metadata refresh non-fatal (log + history_written:false instead of failing the item)
This commit is contained in:
@@ -185,6 +185,10 @@ object NativeDownloadFinalizer {
|
||||
),
|
||||
)
|
||||
|
||||
// Once the output has been published to its final destination the audio
|
||||
// is complete; failures past that point are bookkeeping and must never
|
||||
// trigger the destructive cleanup below.
|
||||
var outputPublished = result.optBoolean("already_exists", false)
|
||||
try {
|
||||
var qualityMetadataRefreshed = false
|
||||
if (!result.optBoolean("already_exists", false)) {
|
||||
@@ -198,7 +202,11 @@ object NativeDownloadFinalizer {
|
||||
checkCancelled(shouldCancel)
|
||||
finalizeMetadata(context, effectiveInput, state)
|
||||
checkCancelled(shouldCancel)
|
||||
writeExternalLrc(context, effectiveInput, state)
|
||||
try {
|
||||
writeExternalLrc(context, effectiveInput, state)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.w(TAG, "External LRC write failed (non-fatal): ${e.message}")
|
||||
}
|
||||
checkCancelled(shouldCancel)
|
||||
runPostProcessing(context, effectiveInput, state, shouldCancel)
|
||||
checkCancelled(shouldCancel)
|
||||
@@ -206,22 +214,37 @@ object NativeDownloadFinalizer {
|
||||
if (replayGain != null) result.put("replaygain", replayGain)
|
||||
checkCancelled(shouldCancel)
|
||||
if (isDeferredSafPublish(effectiveInput)) {
|
||||
refreshFinalAudioQualityMetadata(context, result, state)
|
||||
try {
|
||||
refreshFinalAudioQualityMetadata(context, result, state)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.w(TAG, "Quality metadata refresh failed (non-fatal): ${e.message}")
|
||||
}
|
||||
qualityMetadataRefreshed = true
|
||||
publishDeferredSafOutput(context, effectiveInput, state)
|
||||
} else {
|
||||
promoteStagedSafOutputIfNeeded(context, effectiveInput, state)
|
||||
}
|
||||
outputPublished = true
|
||||
}
|
||||
checkCancelled(shouldCancel)
|
||||
if (!qualityMetadataRefreshed) {
|
||||
refreshFinalAudioQualityMetadata(context, result, state)
|
||||
try {
|
||||
refreshFinalAudioQualityMetadata(context, result, state)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.w(TAG, "Quality metadata refresh failed (non-fatal): ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
val saveDownloadHistory = parseObject(settingsJson)
|
||||
.optBoolean("save_download_history", true)
|
||||
val history = if (saveDownloadHistory) {
|
||||
buildHistoryRow(effectiveInput, state).also { upsertHistory(context, it) }
|
||||
try {
|
||||
buildHistoryRow(effectiveInput, state).also { upsertHistory(context, it) }
|
||||
} catch (e: Exception) {
|
||||
// History is bookkeeping; never fail (and never delete) a
|
||||
// finished download because the insert failed.
|
||||
android.util.Log.w(TAG, "History write failed (non-fatal): ${e.message}")
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -233,13 +256,17 @@ object NativeDownloadFinalizer {
|
||||
result.put("history_written", history != null)
|
||||
if (history != null) result.put("history_item", historyToJson(history))
|
||||
} catch (e: CancellationException) {
|
||||
cleanupFailedFinalizationOutput(context, result, initialPath, state.filePath)
|
||||
if (!outputPublished) {
|
||||
cleanupFailedFinalizationOutput(context, result, initialPath, state.filePath)
|
||||
}
|
||||
result.put("success", false)
|
||||
result.put("error", "Native finalization cancelled")
|
||||
result.put("error_type", "cancelled")
|
||||
result.put("native_finalized", false)
|
||||
} catch (e: Exception) {
|
||||
cleanupFailedFinalizationOutput(context, result, initialPath, state.filePath)
|
||||
if (!outputPublished) {
|
||||
cleanupFailedFinalizationOutput(context, result, initialPath, state.filePath)
|
||||
}
|
||||
result.put("success", false)
|
||||
result.put("error", "Native finalization failed: ${e.message}")
|
||||
result.put("error_type", "unknown")
|
||||
|
||||
Reference in New Issue
Block a user