mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-27 21:30:23 +02:00
fix(download): normalize MP4 audio extension by codec (#512)
This commit is contained in:
@@ -542,7 +542,12 @@ object NativeDownloadFinalizer {
|
||||
if (successPath != null) break
|
||||
}
|
||||
|
||||
val decryptedPath = successPath ?: throw IllegalStateException("decrypt failed: $lastOutput")
|
||||
val rawDecryptedPath = successPath ?: throw IllegalStateException("decrypt failed: $lastOutput")
|
||||
val decryptedPath = normalizeDecryptedIsoBmffAudioPath(
|
||||
rawDecryptedPath,
|
||||
state,
|
||||
shouldCancel,
|
||||
)
|
||||
replaceStatePath(context, input, state, decryptedPath, deleteOld = true)
|
||||
} finally {
|
||||
if (successPath == null) {
|
||||
@@ -554,6 +559,37 @@ object NativeDownloadFinalizer {
|
||||
}
|
||||
}
|
||||
|
||||
private fun normalizeDecryptedIsoBmffAudioPath(
|
||||
path: String,
|
||||
state: FinalizeState,
|
||||
shouldCancel: () -> Boolean,
|
||||
): String {
|
||||
if (!isMP4ContainerFile(path)) return path
|
||||
|
||||
val probedCodec = probePrimaryAudioCodec(path, shouldCancel)
|
||||
val codec = normalizeAudioCodec(probedCodec.ifBlank { state.audioCodec.orEmpty() })
|
||||
val desiredExt = NativeFinalizationPolicy.isoBmffAudioExtension(codec)
|
||||
state.audioCodec = codec
|
||||
if (path.lowercase(Locale.ROOT).endsWith(desiredExt)) return path
|
||||
|
||||
val target = File(buildOutputPath(path, desiredExt))
|
||||
if (target.exists()) {
|
||||
Log.w(TAG, "Cannot normalize ISO-BMFF audio extension; ${target.name} already exists")
|
||||
return path
|
||||
}
|
||||
val source = File(path)
|
||||
if (!source.renameTo(target)) {
|
||||
Log.w(TAG, "Failed to normalize ISO-BMFF audio extension to ${target.name}")
|
||||
return path
|
||||
}
|
||||
Log.i(
|
||||
TAG,
|
||||
"ISO-BMFF audio renamed: ${source.name} -> ${target.name} " +
|
||||
"(codec=${codec.orEmpty().ifBlank { "unknown" }})",
|
||||
)
|
||||
return target.absolutePath
|
||||
}
|
||||
|
||||
private fun finalizeHighConversion(
|
||||
context: Context,
|
||||
input: FinalizeInput,
|
||||
@@ -814,10 +850,10 @@ object NativeDownloadFinalizer {
|
||||
if (!currentFile.name.lowercase(Locale.ROOT).endsWith(".flac")) return
|
||||
|
||||
val newExt = when {
|
||||
codec == "aac" && isMP4ContainerFile(localInput) -> ".m4a"
|
||||
isMP4ContainerFile(localInput) ->
|
||||
NativeFinalizationPolicy.isoBmffAudioExtension(codec)
|
||||
codec == "mp3" -> ".mp3"
|
||||
codec == "opus" -> ".opus"
|
||||
isMP4ContainerFile(localInput) -> ".m4a"
|
||||
else -> return
|
||||
}
|
||||
val renamed = File(
|
||||
|
||||
@@ -68,6 +68,15 @@ internal object NativeFinalizationPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio-only ISO-BMFF files conventionally use .m4a. AC-4 remains .mp4
|
||||
* because its passthrough and standards-repair path depends on that
|
||||
* container identity.
|
||||
*/
|
||||
fun isoBmffAudioExtension(audioCodec: String?): String {
|
||||
return if (normalizeAudioCodec(audioCodec) == "ac4") ".mp4" else ".m4a"
|
||||
}
|
||||
|
||||
fun audioFormatForCodec(codec: String?): String? {
|
||||
return when (normalizeAudioCodec(codec)) {
|
||||
"flac" -> "FLAC"
|
||||
|
||||
@@ -76,6 +76,26 @@ class NativeFinalizationPolicyTest {
|
||||
assertFalse(NativeFinalizationPolicy.isLosslessAudioCodec("aac"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isoBmffAudioUsesM4aExceptForAc4Passthrough() {
|
||||
assertEquals(
|
||||
".m4a",
|
||||
NativeFinalizationPolicy.isoBmffAudioExtension("opus"),
|
||||
)
|
||||
assertEquals(
|
||||
".m4a",
|
||||
NativeFinalizationPolicy.isoBmffAudioExtension("ec-3"),
|
||||
)
|
||||
assertEquals(
|
||||
".m4a",
|
||||
NativeFinalizationPolicy.isoBmffAudioExtension("aac"),
|
||||
)
|
||||
assertEquals(
|
||||
".mp4",
|
||||
NativeFinalizationPolicy.isoBmffAudioExtension("ac-4"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayQualityUsesMeasuredLosslessSpecifications() {
|
||||
assertEquals(
|
||||
|
||||
Reference in New Issue
Block a user