2024-03-01 18:22:57 +00:00
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:dio/dio.dart';
|
|
|
|
import 'package:freezer/api/deezer.dart';
|
|
|
|
import 'package:freezer/api/deezer_audio.dart';
|
|
|
|
import 'package:freezer/api/download.dart';
|
2023-10-20 23:12:33 +00:00
|
|
|
import 'package:freezer/api/download_manager/service_interface.dart';
|
2024-03-01 18:22:57 +00:00
|
|
|
import 'package:freezer/main.dart';
|
2024-02-12 02:37:26 +00:00
|
|
|
import 'package:freezer/settings.dart';
|
2024-03-01 18:22:57 +00:00
|
|
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
|
|
import 'package:path/path.dart';
|
|
|
|
|
|
|
|
part 'download_service.freezed.dart';
|
|
|
|
part 'download_service.g.dart';
|
2023-10-19 12:34:22 +00:00
|
|
|
|
|
|
|
class DownloadService {
|
|
|
|
static const NOTIFICATION_ID = 6969;
|
|
|
|
static const NOTIFICATION_CHANNEL_ID = "freezerdownloads";
|
|
|
|
|
2023-10-28 12:42:06 +00:00
|
|
|
final ServiceInterface service;
|
2023-10-19 12:34:22 +00:00
|
|
|
DownloadService(this.service);
|
|
|
|
|
2024-02-12 02:37:26 +00:00
|
|
|
AudioQuality? _preferredQuality;
|
|
|
|
AudioQuality? _downloadQuality;
|
|
|
|
bool useGetURL = false;
|
|
|
|
|
2024-03-01 18:22:57 +00:00
|
|
|
late String downloadFilename;
|
|
|
|
|
|
|
|
late DeezerAPI _deezerAPI;
|
|
|
|
|
|
|
|
void run() async {
|
|
|
|
service.on('addDownloads', (event) {
|
|
|
|
downloadTrack(DownloadInfo.fromJson(event!));
|
|
|
|
});
|
|
|
|
service.on('updateQuality', (event) {
|
2024-02-12 02:37:26 +00:00
|
|
|
_preferredQuality = AudioQuality.values[event!['q']!];
|
|
|
|
});
|
2024-03-01 18:22:57 +00:00
|
|
|
service.on('updateCapabilities', (event) {
|
2024-02-12 02:37:26 +00:00
|
|
|
final bool canStreamHQ = event!['canStreamHQ'];
|
|
|
|
final bool canStreamLossless = event['canStreamLossless'];
|
|
|
|
|
|
|
|
if (canStreamHQ || canStreamLossless) useGetURL = true;
|
|
|
|
|
|
|
|
_downloadQuality = settings.maxQualityFor(
|
|
|
|
_preferredQuality!, canStreamHQ, canStreamLossless);
|
|
|
|
});
|
2024-03-01 18:22:57 +00:00
|
|
|
service.on('updateSettings', (event) {
|
|
|
|
if (event!['downloadFilename'] != null) {
|
|
|
|
downloadFilename = event['downloadFilename'];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event['deezerLanguage'] != null) {
|
|
|
|
_deezerAPI.deezerLanguage = event['deezerLanguage'];
|
|
|
|
}
|
|
|
|
if (event['deezerCountry'] != null) {
|
|
|
|
_deezerAPI.deezerCountry = event['deezerCountry'];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
_deezerAPI = DeezerAPI(await getCookieJar());
|
|
|
|
await _deezerAPI.authorize();
|
|
|
|
|
|
|
|
service.send('ready');
|
2024-02-12 02:37:26 +00:00
|
|
|
}
|
|
|
|
|
2024-03-01 18:22:57 +00:00
|
|
|
void downloadTrack(DownloadInfo info) async {
|
|
|
|
final trackId = info.trackId;
|
|
|
|
final deezerAudio = DeezerAudio(
|
|
|
|
deezerAPI: _deezerAPI,
|
|
|
|
quality: _downloadQuality ?? AudioQuality.MP3_128,
|
|
|
|
trackId: trackId);
|
|
|
|
|
|
|
|
final track = await _deezerAPI.track(trackId);
|
|
|
|
final file = File(join(
|
|
|
|
info.path!,
|
|
|
|
downloadFilename
|
|
|
|
.replaceAll('%artist%', track.artists?.join(',') ?? '')
|
|
|
|
.replaceAll('%title%', track.title!)));
|
|
|
|
print('downloading to ${file.path}');
|
|
|
|
final Uri uri;
|
|
|
|
if (useGetURL) {
|
|
|
|
// TODO: use pipe API to get track token!
|
|
|
|
final res = await deezerAudio.getUrl(
|
|
|
|
track.trackToken!, track.trackTokenExpiration!);
|
|
|
|
uri = res!.$1;
|
|
|
|
} else {
|
|
|
|
final res = await deezerAudio.fallback(
|
|
|
|
md5origin: track.playbackDetails![0],
|
|
|
|
mediaVersion: track.playbackDetails![1]);
|
|
|
|
uri = res.uri;
|
|
|
|
}
|
|
|
|
|
|
|
|
// download url and decrypt
|
|
|
|
final req = await _deezerAPI.dio.get(uri.toString(),
|
|
|
|
options: Options(responseType: ResponseType.bytes));
|
|
|
|
final stream =
|
|
|
|
DeezerAudio.decryptionStream(req.data, start: 0, trackId: trackId);
|
|
|
|
final fWrite = file.openWrite();
|
|
|
|
await stream.pipe(fWrite);
|
|
|
|
print('download complete!');
|
2023-10-28 12:42:06 +00:00
|
|
|
}
|
2023-10-19 12:34:22 +00:00
|
|
|
}
|
2024-03-01 18:22:57 +00:00
|
|
|
|
|
|
|
@freezed
|
|
|
|
class DownloadInfo with _$DownloadInfo {
|
|
|
|
const factory DownloadInfo({required String trackId, String? path}) =
|
|
|
|
_DownloadInfo;
|
|
|
|
factory DownloadInfo.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$DownloadInfoFromJson(json);
|
|
|
|
}
|