v1.1.0: Parallel downloads, bug fixes, history persistence

This commit is contained in:
zarzet
2026-01-01 22:09:39 +07:00
parent 9570547ff9
commit 6a1265eac3
10 changed files with 430 additions and 228 deletions
+12 -30
View File
@@ -7,21 +7,22 @@ part of 'download_item.dart';
// **************************************************************************
DownloadItem _$DownloadItemFromJson(Map<String, dynamic> json) => DownloadItem(
id: json['id'] as String,
track: Track.fromJson(json['track'] as Map<String, dynamic>),
service: json['service'] as String,
status: $enumDecodeNullable(_$DownloadStatusEnumMap, json['status']) ??
DownloadStatus.queued,
progress: (json['progress'] as num?)?.toDouble() ?? 0.0,
filePath: json['filePath'] as String?,
error: json['error'] as String?,
createdAt: DateTime.parse(json['createdAt'] as String),
);
id: json['id'] as String,
track: Track.fromJson(json['track'] as Map<String, dynamic>),
service: json['service'] as String,
status:
$enumDecodeNullable(_$DownloadStatusEnumMap, json['status']) ??
DownloadStatus.queued,
progress: (json['progress'] as num?)?.toDouble() ?? 0.0,
filePath: json['filePath'] as String?,
error: json['error'] as String?,
createdAt: DateTime.parse(json['createdAt'] as String),
);
Map<String, dynamic> _$DownloadItemToJson(DownloadItem instance) =>
<String, dynamic>{
'id': instance.id,
'track': instance.track.toJson(),
'track': instance.track,
'service': instance.service,
'status': _$DownloadStatusEnumMap[instance.status]!,
'progress': instance.progress,
@@ -37,22 +38,3 @@ const _$DownloadStatusEnumMap = {
DownloadStatus.failed: 'failed',
DownloadStatus.skipped: 'skipped',
};
K? $enumDecodeNullable<K, V>(
Map<K, V> enumValues,
Object? source, {
K? unknownValue,
}) {
if (source == null) {
return null;
}
return enumValues.entries
.singleWhere(
(e) => e.value == source,
orElse: () => throw ArgumentError(
'`$source` is not one of the supported values: '
'${enumValues.values.join(', ')}',
),
)
.key;
}
+4
View File
@@ -12,6 +12,7 @@ class AppSettings {
final bool embedLyrics;
final bool maxQualityCover;
final bool isFirstLaunch;
final int concurrentDownloads; // 1 = sequential (default), max 3
const AppSettings({
this.defaultService = 'tidal',
@@ -22,6 +23,7 @@ class AppSettings {
this.embedLyrics = true,
this.maxQualityCover = true,
this.isFirstLaunch = true,
this.concurrentDownloads = 1, // Default: sequential (off)
});
AppSettings copyWith({
@@ -33,6 +35,7 @@ class AppSettings {
bool? embedLyrics,
bool? maxQualityCover,
bool? isFirstLaunch,
int? concurrentDownloads,
}) {
return AppSettings(
defaultService: defaultService ?? this.defaultService,
@@ -43,6 +46,7 @@ class AppSettings {
embedLyrics: embedLyrics ?? this.embedLyrics,
maxQualityCover: maxQualityCover ?? this.maxQualityCover,
isFirstLaunch: isFirstLaunch ?? this.isFirstLaunch,
concurrentDownloads: concurrentDownloads ?? this.concurrentDownloads,
);
}
+11 -9
View File
@@ -7,15 +7,16 @@ part of 'settings.dart';
// **************************************************************************
AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
defaultService: json['defaultService'] as String? ?? 'tidal',
audioQuality: json['audioQuality'] as String? ?? 'LOSSLESS',
filenameFormat: json['filenameFormat'] as String? ?? '{title} - {artist}',
downloadDirectory: json['downloadDirectory'] as String? ?? '',
autoFallback: json['autoFallback'] as bool? ?? true,
embedLyrics: json['embedLyrics'] as bool? ?? true,
maxQualityCover: json['maxQualityCover'] as bool? ?? true,
isFirstLaunch: json['isFirstLaunch'] as bool? ?? true,
);
defaultService: json['defaultService'] as String? ?? 'tidal',
audioQuality: json['audioQuality'] as String? ?? 'LOSSLESS',
filenameFormat: json['filenameFormat'] as String? ?? '{title} - {artist}',
downloadDirectory: json['downloadDirectory'] as String? ?? '',
autoFallback: json['autoFallback'] as bool? ?? true,
embedLyrics: json['embedLyrics'] as bool? ?? true,
maxQualityCover: json['maxQualityCover'] as bool? ?? true,
isFirstLaunch: json['isFirstLaunch'] as bool? ?? true,
concurrentDownloads: (json['concurrentDownloads'] as num?)?.toInt() ?? 1,
);
Map<String, dynamic> _$AppSettingsToJson(AppSettings instance) =>
<String, dynamic>{
@@ -27,4 +28,5 @@ Map<String, dynamic> _$AppSettingsToJson(AppSettings instance) =>
'embedLyrics': instance.embedLyrics,
'maxQualityCover': instance.maxQualityCover,
'isFirstLaunch': instance.isFirstLaunch,
'concurrentDownloads': instance.concurrentDownloads,
};
+39 -38
View File
@@ -7,37 +7,38 @@ part of 'track.dart';
// **************************************************************************
Track _$TrackFromJson(Map<String, dynamic> json) => Track(
id: json['id'] as String,
name: json['name'] as String,
artistName: json['artistName'] as String,
albumName: json['albumName'] as String,
albumArtist: json['albumArtist'] as String?,
coverUrl: json['coverUrl'] as String?,
isrc: json['isrc'] as String?,
duration: (json['duration'] as num).toInt(),
trackNumber: (json['trackNumber'] as num?)?.toInt(),
discNumber: (json['discNumber'] as num?)?.toInt(),
releaseDate: json['releaseDate'] as String?,
availability: json['availability'] == null
? null
: ServiceAvailability.fromJson(
json['availability'] as Map<String, dynamic>),
);
id: json['id'] as String,
name: json['name'] as String,
artistName: json['artistName'] as String,
albumName: json['albumName'] as String,
albumArtist: json['albumArtist'] as String?,
coverUrl: json['coverUrl'] as String?,
isrc: json['isrc'] as String?,
duration: (json['duration'] as num).toInt(),
trackNumber: (json['trackNumber'] as num?)?.toInt(),
discNumber: (json['discNumber'] as num?)?.toInt(),
releaseDate: json['releaseDate'] as String?,
availability: json['availability'] == null
? null
: ServiceAvailability.fromJson(
json['availability'] as Map<String, dynamic>,
),
);
Map<String, dynamic> _$TrackToJson(Track instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
'artistName': instance.artistName,
'albumName': instance.albumName,
'albumArtist': instance.albumArtist,
'coverUrl': instance.coverUrl,
'isrc': instance.isrc,
'duration': instance.duration,
'trackNumber': instance.trackNumber,
'discNumber': instance.discNumber,
'releaseDate': instance.releaseDate,
'availability': instance.availability?.toJson(),
};
'id': instance.id,
'name': instance.name,
'artistName': instance.artistName,
'albumName': instance.albumName,
'albumArtist': instance.albumArtist,
'coverUrl': instance.coverUrl,
'isrc': instance.isrc,
'duration': instance.duration,
'trackNumber': instance.trackNumber,
'discNumber': instance.discNumber,
'releaseDate': instance.releaseDate,
'availability': instance.availability,
};
ServiceAvailability _$ServiceAvailabilityFromJson(Map<String, dynamic> json) =>
ServiceAvailability(
@@ -50,12 +51,12 @@ ServiceAvailability _$ServiceAvailabilityFromJson(Map<String, dynamic> json) =>
);
Map<String, dynamic> _$ServiceAvailabilityToJson(
ServiceAvailability instance) =>
<String, dynamic>{
'tidal': instance.tidal,
'qobuz': instance.qobuz,
'amazon': instance.amazon,
'tidalUrl': instance.tidalUrl,
'qobuzUrl': instance.qobuzUrl,
'amazonUrl': instance.amazonUrl,
};
ServiceAvailability instance,
) => <String, dynamic>{
'tidal': instance.tidal,
'qobuz': instance.qobuz,
'amazon': instance.amazon,
'tidalUrl': instance.tidalUrl,
'qobuzUrl': instance.qobuzUrl,
'amazonUrl': instance.amazonUrl,
};