mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-28 23:08:59 +02:00
fix(download): skip AC-4 repair for non-MP4 decrypt output
Decrypt can emit a raw FLAC stream; the AC-4 repair step fed it to the MP4 box parser, which failed with a garbage atom type whose control bytes then broke the unescaped Kotlin error JSON (FormatException in Dart). Guard the repair call by extension, sniff the box type in Go as a second layer, and build platform error JSON with JSONObject.quote.
This commit is contained in:
@@ -2677,7 +2677,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("SpotiFLAC", "readFileMetadata failed: ${e.message}", e)
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
@@ -2718,7 +2718,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("SpotiFLAC", "editFileMetadata failed: ${e.message}", e)
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
@@ -2731,7 +2731,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
Gobackend.writeM4AFreeformTags(filePath, metadataJson)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("SpotiFLAC", "writeM4AFreeformTags failed: ${e.message}", e)
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
@@ -2744,7 +2744,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
Gobackend.ensureAC4Config(filePath, sourcePath)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("SpotiFLAC", "ensureAC4Config failed: ${e.message}", e)
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
@@ -2758,7 +2758,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
Gobackend.writeAC4Metadata(filePath, metadataJson, coverPath)
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("SpotiFLAC", "writeAC4Metadata failed: ${e.message}", e)
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
@@ -2966,7 +2966,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
Gobackend.reEnrichFile(requestJson)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
@@ -3685,7 +3685,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
Gobackend.readAudioMetadataJSON(filePath)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
@@ -3759,7 +3759,7 @@ class MainActivity: FlutterFragmentActivity() {
|
||||
Gobackend.parseCueSheet(cuePath, audioDir)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
"""{"error":"${e.message?.replace("\"", "'")}"}"""
|
||||
"""{"error":${org.json.JSONObject.quote(e.message ?: "unknown")}}"""
|
||||
}
|
||||
}
|
||||
result.success(response)
|
||||
|
||||
@@ -3,6 +3,7 @@ package gobackend
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -295,6 +296,23 @@ func EnsureAC4ConfigBox(decryptedPath, sourcePath string) error {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
// A non-MP4 decrypt output (e.g. a raw FLAC stream) is not an AC-4 file;
|
||||
// bail out before the box parser reports it as a corrupt MP4. A real
|
||||
// ISO-BMFF box type is four printable ASCII bytes.
|
||||
var head [8]byte
|
||||
if _, err := f.ReadAt(head[:], 0); err != nil {
|
||||
f.Close()
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
for _, c := range head[4:8] {
|
||||
if c < 0x20 || c > 0x7e {
|
||||
f.Close()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
moovBuf, moovOffset, moovFound, err := loadTopLevelMP4Box(f, info.Size(), "moov")
|
||||
if err != nil || !moovFound {
|
||||
f.Close()
|
||||
|
||||
@@ -11,6 +11,13 @@ class _DecryptOutcome {
|
||||
const _DecryptOutcome(this.path, {this.newFileName, this.failStage});
|
||||
}
|
||||
|
||||
/// AC-4 repair only applies to MP4 containers; decrypt can also emit raw
|
||||
/// FLAC, which the native MP4 box parser would reject as corrupt.
|
||||
bool _isMp4Container(String path) {
|
||||
final lower = path.toLowerCase();
|
||||
return lower.endsWith('.m4a') || lower.endsWith('.mp4');
|
||||
}
|
||||
|
||||
extension _DownloadQueueFinalization on DownloadQueueNotifier {
|
||||
/// Builds the [DownloadHistoryItem] shared by the native-worker and inline
|
||||
/// completion paths. Fields whose source/derivation legitimately differs
|
||||
@@ -297,7 +304,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier {
|
||||
return null;
|
||||
}
|
||||
addCleanup(decryptedTempPath);
|
||||
if (repairAc4) {
|
||||
if (repairAc4 && _isMp4Container(decryptedTempPath)) {
|
||||
try {
|
||||
await PlatformBridge.ensureAC4Config(decryptedTempPath, tempPath);
|
||||
} catch (e) {
|
||||
@@ -345,10 +352,12 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier {
|
||||
failStage: DownloadQueueNotifier._decryptStageDecrypt,
|
||||
);
|
||||
}
|
||||
try {
|
||||
await PlatformBridge.ensureAC4Config(decryptedPath, filePath);
|
||||
} catch (e) {
|
||||
_log.w('AC-4 container repair skipped: $e');
|
||||
if (_isMp4Container(decryptedPath)) {
|
||||
try {
|
||||
await PlatformBridge.ensureAC4Config(decryptedPath, filePath);
|
||||
} catch (e) {
|
||||
_log.w('AC-4 container repair skipped: $e');
|
||||
}
|
||||
}
|
||||
try {
|
||||
await deleteFile(filePath);
|
||||
|
||||
Reference in New Issue
Block a user