mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-14 23:20:25 +02:00
Fix metadata consistency (Go->Flutter) and build optimization
- Backend: Return full metadata (Track, Disc, Year) from Tidal/Qobuz/Amazon download results - Flutter: Use backend metadata for tagging converted M4A and history entries - Fix: Duplicate convertTrack method in deezer.go - Fix: Better error message for Deezer fallback failure - Changed: Default service fallback to Tidal -> Qobuz -> Amazon - Build: Re-enabled resource shrinking and minification for release build
This commit is contained in:
@@ -13,6 +13,14 @@ enum DownloadStatus {
|
||||
skipped,
|
||||
}
|
||||
|
||||
/// Error type enum for better error handling
|
||||
enum DownloadErrorType {
|
||||
unknown,
|
||||
notFound, // Track not found on any service
|
||||
rateLimit, // Rate limited by service
|
||||
network, // Network/connection error
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class DownloadItem {
|
||||
final String id;
|
||||
@@ -20,8 +28,10 @@ class DownloadItem {
|
||||
final String service;
|
||||
final DownloadStatus status;
|
||||
final double progress;
|
||||
final double speedMBps; // Download speed in MB/s
|
||||
final String? filePath;
|
||||
final String? error;
|
||||
final DownloadErrorType? errorType;
|
||||
final DateTime createdAt;
|
||||
final String? qualityOverride; // Override quality for this specific download
|
||||
|
||||
@@ -31,8 +41,10 @@ class DownloadItem {
|
||||
required this.service,
|
||||
this.status = DownloadStatus.queued,
|
||||
this.progress = 0.0,
|
||||
this.speedMBps = 0.0,
|
||||
this.filePath,
|
||||
this.error,
|
||||
this.errorType,
|
||||
required this.createdAt,
|
||||
this.qualityOverride,
|
||||
});
|
||||
@@ -43,8 +55,10 @@ class DownloadItem {
|
||||
String? service,
|
||||
DownloadStatus? status,
|
||||
double? progress,
|
||||
double? speedMBps,
|
||||
String? filePath,
|
||||
String? error,
|
||||
DownloadErrorType? errorType,
|
||||
DateTime? createdAt,
|
||||
String? qualityOverride,
|
||||
}) {
|
||||
@@ -54,13 +68,31 @@ class DownloadItem {
|
||||
service: service ?? this.service,
|
||||
status: status ?? this.status,
|
||||
progress: progress ?? this.progress,
|
||||
speedMBps: speedMBps ?? this.speedMBps,
|
||||
filePath: filePath ?? this.filePath,
|
||||
error: error ?? this.error,
|
||||
errorType: errorType ?? this.errorType,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
qualityOverride: qualityOverride ?? this.qualityOverride,
|
||||
);
|
||||
}
|
||||
|
||||
/// Get user-friendly error message based on error type
|
||||
String get errorMessage {
|
||||
if (error == null) return '';
|
||||
|
||||
switch (errorType) {
|
||||
case DownloadErrorType.notFound:
|
||||
return 'Song not found on any service';
|
||||
case DownloadErrorType.rateLimit:
|
||||
return 'Rate limit reached, try again later';
|
||||
case DownloadErrorType.network:
|
||||
return 'Connection failed, check your internet';
|
||||
default:
|
||||
return error ?? 'An error occurred';
|
||||
}
|
||||
}
|
||||
|
||||
factory DownloadItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$DownloadItemFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$DownloadItemToJson(this);
|
||||
|
||||
@@ -14,8 +14,10 @@ DownloadItem _$DownloadItemFromJson(Map<String, dynamic> json) => DownloadItem(
|
||||
$enumDecodeNullable(_$DownloadStatusEnumMap, json['status']) ??
|
||||
DownloadStatus.queued,
|
||||
progress: (json['progress'] as num?)?.toDouble() ?? 0.0,
|
||||
speedMBps: (json['speedMBps'] as num?)?.toDouble() ?? 0.0,
|
||||
filePath: json['filePath'] as String?,
|
||||
error: json['error'] as String?,
|
||||
errorType: $enumDecodeNullable(_$DownloadErrorTypeEnumMap, json['errorType']),
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
qualityOverride: json['qualityOverride'] as String?,
|
||||
);
|
||||
@@ -27,8 +29,10 @@ Map<String, dynamic> _$DownloadItemToJson(DownloadItem instance) =>
|
||||
'service': instance.service,
|
||||
'status': _$DownloadStatusEnumMap[instance.status]!,
|
||||
'progress': instance.progress,
|
||||
'speedMBps': instance.speedMBps,
|
||||
'filePath': instance.filePath,
|
||||
'error': instance.error,
|
||||
'errorType': _$DownloadErrorTypeEnumMap[instance.errorType],
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'qualityOverride': instance.qualityOverride,
|
||||
};
|
||||
@@ -41,3 +45,10 @@ const _$DownloadStatusEnumMap = {
|
||||
DownloadStatus.failed: 'failed',
|
||||
DownloadStatus.skipped: 'skipped',
|
||||
};
|
||||
|
||||
const _$DownloadErrorTypeEnumMap = {
|
||||
DownloadErrorType.unknown: 'unknown',
|
||||
DownloadErrorType.notFound: 'notFound',
|
||||
DownloadErrorType.rateLimit: 'rateLimit',
|
||||
DownloadErrorType.network: 'network',
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'settings.dart';
|
||||
// **************************************************************************
|
||||
|
||||
AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
|
||||
defaultService: json['defaultService'] as String? ?? 'tidal',
|
||||
defaultService: json['defaultService'] as String? ?? 'qobuz',
|
||||
audioQuality: json['audioQuality'] as String? ?? 'LOSSLESS',
|
||||
filenameFormat: json['filenameFormat'] as String? ?? '{title} - {artist}',
|
||||
downloadDirectory: json['downloadDirectory'] as String? ?? '',
|
||||
|
||||
@@ -16,6 +16,7 @@ class Track {
|
||||
final int? trackNumber;
|
||||
final int? discNumber;
|
||||
final String? releaseDate;
|
||||
final String? deezerId;
|
||||
final ServiceAvailability? availability;
|
||||
|
||||
const Track({
|
||||
@@ -30,6 +31,7 @@ class Track {
|
||||
this.trackNumber,
|
||||
this.discNumber,
|
||||
this.releaseDate,
|
||||
this.deezerId,
|
||||
this.availability,
|
||||
});
|
||||
|
||||
@@ -42,17 +44,23 @@ class ServiceAvailability {
|
||||
final bool tidal;
|
||||
final bool qobuz;
|
||||
final bool amazon;
|
||||
final bool deezer;
|
||||
final String? tidalUrl;
|
||||
final String? qobuzUrl;
|
||||
final String? amazonUrl;
|
||||
final String? deezerUrl;
|
||||
final String? deezerId;
|
||||
|
||||
const ServiceAvailability({
|
||||
this.tidal = false,
|
||||
this.qobuz = false,
|
||||
this.amazon = false,
|
||||
this.deezer = false,
|
||||
this.tidalUrl,
|
||||
this.qobuzUrl,
|
||||
this.amazonUrl,
|
||||
this.deezerUrl,
|
||||
this.deezerId,
|
||||
});
|
||||
|
||||
factory ServiceAvailability.fromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -18,6 +18,7 @@ Track _$TrackFromJson(Map<String, dynamic> json) => Track(
|
||||
trackNumber: (json['trackNumber'] as num?)?.toInt(),
|
||||
discNumber: (json['discNumber'] as num?)?.toInt(),
|
||||
releaseDate: json['releaseDate'] as String?,
|
||||
deezerId: json['deezerId'] as String?,
|
||||
availability: json['availability'] == null
|
||||
? null
|
||||
: ServiceAvailability.fromJson(
|
||||
@@ -37,6 +38,7 @@ Map<String, dynamic> _$TrackToJson(Track instance) => <String, dynamic>{
|
||||
'trackNumber': instance.trackNumber,
|
||||
'discNumber': instance.discNumber,
|
||||
'releaseDate': instance.releaseDate,
|
||||
'deezerId': instance.deezerId,
|
||||
'availability': instance.availability,
|
||||
};
|
||||
|
||||
@@ -45,9 +47,12 @@ ServiceAvailability _$ServiceAvailabilityFromJson(Map<String, dynamic> json) =>
|
||||
tidal: json['tidal'] as bool? ?? false,
|
||||
qobuz: json['qobuz'] as bool? ?? false,
|
||||
amazon: json['amazon'] as bool? ?? false,
|
||||
deezer: json['deezer'] as bool? ?? false,
|
||||
tidalUrl: json['tidalUrl'] as String?,
|
||||
qobuzUrl: json['qobuzUrl'] as String?,
|
||||
amazonUrl: json['amazonUrl'] as String?,
|
||||
deezerUrl: json['deezerUrl'] as String?,
|
||||
deezerId: json['deezerId'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ServiceAvailabilityToJson(
|
||||
@@ -56,7 +61,10 @@ Map<String, dynamic> _$ServiceAvailabilityToJson(
|
||||
'tidal': instance.tidal,
|
||||
'qobuz': instance.qobuz,
|
||||
'amazon': instance.amazon,
|
||||
'deezer': instance.deezer,
|
||||
'tidalUrl': instance.tidalUrl,
|
||||
'qobuzUrl': instance.qobuzUrl,
|
||||
'amazonUrl': instance.amazonUrl,
|
||||
'deezerUrl': instance.deezerUrl,
|
||||
'deezerId': instance.deezerId,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user