fix(download): detect mislabeled MP4 before native metadata writes

This commit is contained in:
zarzet
2026-09-28 12:13:17 +07:00
parent c5a262b865
commit 4c2c356763
3 changed files with 65 additions and 10 deletions
@@ -0,0 +1,58 @@
package com.zarz.spotiflac
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import java.io.File
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class NativeDownloadContainerTest {
@Test
fun misnamedMp4IsTaggedAndPublishedAsM4aWithoutOverwritingExistingAudio() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val root = File(context.cacheDir, "container-test-${System.nanoTime()}").apply { mkdirs() }
try {
val input = File(root, "track.flac")
val existing = File(root, "track.m4a").apply { writeText("existing download") }
val fixture = NativeDownloadFinalizer.runFFmpegArguments(arrayOf(
"-v", "error", "-f", "lavfi", "-i", "sine=frequency=997:sample_rate=44100",
"-t", "0.2", "-c:a", "aac", "-f", "mp4", input.path,
))
assertTrue(fixture.second, fixture.first)
assertTrue(isMP4ContainerFile(input.path))
val request = JSONObject()
.put("contract_version", 1).put("item_id", "example-track")
.put("service", "example-provider").put("track_name", "Container test")
.put("artist_name", "Example artist").put("album_name", "Example album")
.put("quality", "LOSSLESS").put("storage_mode", "app")
.put("output_ext", ".flac").put("embed_metadata", true)
val result = NativeDownloadFinalizer.finalize(
context, "example-track", request.toString(), "{}",
JSONObject().put("success", true).put("file_path", input.path)
.put("file_name", input.name),
"{\"save_download_history\":false}",
)
assertTrue(result.toString(), result.getBoolean("success"))
assertTrue(result.getBoolean("native_finalized"))
val output = File(result.getString("file_path"))
assertEquals("m4a", output.extension)
assertTrue(output.length() > 0)
assertTrue(isMP4ContainerFile(output.path))
assertFalse(input.exists())
assertEquals("existing download", existing.readText())
val probe = NativeDownloadFinalizer.runFFmpegArguments(arrayOf(
"-hide_banner", "-i", output.path, "-map", "0:a:0", "-f", "null", "-",
))
assertTrue(probe.second, probe.first)
assertTrue(probe.second, probe.second.contains("Container test"))
assertTrue(probe.second, probe.second.contains("Audio: aac"))
} finally {
root.deleteRecursively()
}
}
}
@@ -760,6 +760,7 @@ object NativeDownloadFinalizer {
if (!forceContainerConversion && requestedDecryptionExt.isNotBlank() && requestedDecryptionExt != ".flac") return
val mayNeedContainerConversion = forceContainerConversion ||
looksLikeM4a(state.filePath, state.fileName) ||
isMP4ContainerFile(state.filePath) ||
state.filePath.startsWith("content://")
if (!mayNeedContainerConversion) return
@@ -768,6 +769,7 @@ object NativeDownloadFinalizer {
val output = buildOutputPath(localInput, ".flac")
val stagedOutput = stagedConversionPath(output)
var adoptedOutput = false
var createdOutput = false
try {
val codec = probePrimaryAudioCodec(localInput, shouldCancel)
val isAlreadyNativeFlac = codec == "flac" && isNativeFlacFile(localInput)
@@ -794,6 +796,7 @@ object NativeDownloadFinalizer {
if (!promoteStagedConversion(stagedOutput, output)) {
throw IllegalStateException("failed to publish native FLAC output")
}
createdOutput = true
output
}
embedBasicMetadata(context, nativeFlacOutput, input, "flac")
@@ -811,6 +814,7 @@ object NativeDownloadFinalizer {
if (!promoteStagedConversion(stagedOutput, output)) {
throw IllegalStateException("failed to publish container conversion output")
}
createdOutput = true
// Keep metadata failures before adoption so the source survives
// and the unsuccessful output is removed by the local cleanup.
embedBasicMetadata(context, output, input, "flac")
@@ -819,7 +823,7 @@ object NativeDownloadFinalizer {
} finally {
if (!adoptedOutput) {
File(stagedOutput).delete()
File(output).delete()
if (createdOutput && output != localInput) File(output).delete()
}
if (deleteLocalInput) File(localInput).delete()
}
@@ -846,14 +850,7 @@ object NativeDownloadFinalizer {
codec == "opus" -> ".opus"
else -> return
}
val renamed = File(
currentFile.parentFile,
currentFile.name.dropLast(".flac".length) + newExt,
)
if (renamed.exists() && !renamed.delete()) {
Log.w(TAG, "Cannot adopt container extension; ${renamed.name} already exists")
return
}
val renamed = File(uniqueAutoConversionOutputPath(currentFile.path, newExt))
if (!currentFile.renameTo(renamed)) {
Log.w(TAG, "Failed to rename preserved container to ${renamed.name}")
return
@@ -35,7 +35,7 @@ import kotlin.math.pow
// FFmpeg execution, probing, and container helpers for NativeDownloadFinalizer.
internal fun NativeDownloadFinalizer.isMP4ContainerFile(path: String): Boolean {
internal fun isMP4ContainerFile(path: String): Boolean {
return try {
File(path).inputStream().use { stream ->
val header = ByteArray(12)