mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-11 18:28:56 +02:00
feat: add resolve API with SongLink fallback, fix multi-artist tags (#288), and cleanup
Resolve API (api.zarz.moe): - Refactor songlink.go: Spotify URLs use resolve API, non-Spotify uses SongLink API - Add SongLink fallback when resolve API fails for Spotify (two-layer resilience) - Remove dead code: page parser, XOR-obfuscated keys, legacy helpers Multi-artist tag fix (#288): - Add RewriteSplitArtistTags() in Go to rewrite ARTIST/ALBUMARTIST as split Vorbis comments - Wire method channel handler in Android (MainActivity.kt) and iOS (AppDelegate.swift) - Add PlatformBridge.rewriteSplitArtistTags() in Dart - Call native FLAC rewriter after FFmpeg embed when split_vorbis mode is active - Extract deezerTrackArtistDisplay() helper to use Contributors in album/playlist tracks Code cleanup: - Remove unused imports, dead code, and redundant comments across Go and Dart - Fix build: remove stale getQobuzDebugKey() reference in deezer_download.go
This commit is contained in:
1 parent
7c4309955e
commit
d034144e9c
63 files changed
+425
-1044
No files matched your search
@@ -47,26 +47,20 @@ bool isValidIosWritablePath(String path) {
|
||||
if (path.isEmpty) return false;
|
||||
if (!path.startsWith('/')) return false;
|
||||
|
||||
// Check if it's the container root (without Documents/, tmp/, etc.)
|
||||
if (_iosContainerRootPattern.hasMatch(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for iCloud Drive paths
|
||||
if (path.contains('Mobile Documents') ||
|
||||
path.contains('CloudDocs') ||
|
||||
path.contains('com~apple~CloudDocs')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reject stale paths where an old sandbox container path has been embedded
|
||||
// inside the current Documents directory.
|
||||
if (_iosNestedLegacyDocumentsPattern.hasMatch(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure path contains a valid subdirectory (Documents, tmp, Library, etc.)
|
||||
// This handles cases where FilePicker returns container root
|
||||
final containerPattern = RegExp(
|
||||
r'/var/mobile/Containers/Data/Application/[A-F0-9\-]+',
|
||||
caseSensitive: false,
|
||||
@@ -74,7 +68,6 @@ bool isValidIosWritablePath(String path) {
|
||||
final match = containerPattern.firstMatch(path);
|
||||
if (match != null) {
|
||||
final remainingPath = path.substring(match.end);
|
||||
// Valid paths should have something after the UUID
|
||||
if (remainingPath.isEmpty || remainingPath == '/') {
|
||||
return false;
|
||||
}
|
||||
@@ -111,13 +104,10 @@ Future<String> validateOrFixIosPath(
|
||||
candidates.add(trimmed);
|
||||
}
|
||||
|
||||
// Some pickers can return absolute iOS paths without the leading slash.
|
||||
if (_iosContainerPathWithoutLeadingSlashPattern.hasMatch(trimmed)) {
|
||||
candidates.add('/$trimmed');
|
||||
}
|
||||
|
||||
// Recover legacy relative iOS path format:
|
||||
// Data/Application/<UUID>/Documents/<subdir>
|
||||
final legacyRelativeMatch = _iosLegacyRelativeDocumentsPattern.firstMatch(
|
||||
trimmed,
|
||||
);
|
||||
@@ -127,7 +117,6 @@ Future<String> validateOrFixIosPath(
|
||||
);
|
||||
}
|
||||
|
||||
// Generic salvage for relative paths containing `Documents/...`.
|
||||
if (!trimmed.startsWith('/')) {
|
||||
final documentsMarker = 'Documents/';
|
||||
final index = trimmed.indexOf(documentsMarker);
|
||||
@@ -143,7 +132,6 @@ Future<String> validateOrFixIosPath(
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to app Documents directory
|
||||
final musicDir = Directory('${docDir.path}/$subfolder');
|
||||
if (!await musicDir.exists()) {
|
||||
await musicDir.create(recursive: true);
|
||||
@@ -185,7 +173,6 @@ IosPathValidationResult validateIosPath(String path) {
|
||||
);
|
||||
}
|
||||
|
||||
// Check if it's the container root
|
||||
if (_iosContainerRootPattern.hasMatch(path)) {
|
||||
return const IosPathValidationResult(
|
||||
isValid: false,
|
||||
@@ -194,7 +181,6 @@ IosPathValidationResult validateIosPath(String path) {
|
||||
);
|
||||
}
|
||||
|
||||
// Check for iCloud Drive paths
|
||||
if (path.contains('Mobile Documents') ||
|
||||
path.contains('CloudDocs') ||
|
||||
path.contains('com~apple~CloudDocs')) {
|
||||
@@ -213,7 +199,6 @@ IosPathValidationResult validateIosPath(String path) {
|
||||
);
|
||||
}
|
||||
|
||||
// Check for container root without subdirectory
|
||||
final containerPattern = RegExp(
|
||||
r'/var/mobile/Containers/Data/Application/[A-F0-9\-]+',
|
||||
caseSensitive: false,
|
||||
@@ -263,7 +248,6 @@ String stripCueTrackSuffix(String path) {
|
||||
|
||||
Future<bool> fileExists(String? path) async {
|
||||
if (path == null || path.isEmpty) return false;
|
||||
// For CUE virtual paths, check if the base .cue file exists
|
||||
final realPath = isCueVirtualPath(path) ? stripCueTrackSuffix(path) : path;
|
||||
if (isContentUri(realPath)) {
|
||||
return PlatformBridge.safExists(realPath);
|
||||
@@ -288,7 +272,6 @@ Future<void> deleteFile(String? path) async {
|
||||
|
||||
Future<FileAccessStat?> fileStat(String? path) async {
|
||||
if (path == null || path.isEmpty) return null;
|
||||
// For CUE virtual paths, stat the base .cue file
|
||||
final realPath = isCueVirtualPath(path) ? stripCueTrackSuffix(path) : path;
|
||||
if (isContentUri(realPath)) {
|
||||
final stat = await PlatformBridge.safStat(realPath);
|
||||
|
||||
Reference in new issue
Block a user