freezer/lib/api/download_manager/download_manager.dart

184 lines
5.4 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/widgets.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/definitions.dart' as d;
import 'package:freezer/api/download_manager/database.dart';
import 'package:freezer/api/download_manager/download_service.dart';
import 'package:freezer/api/download_manager/service_interface.dart';
import 'package:freezer/api/paths.dart';
import 'package:freezer/main.dart';
import 'package:freezer/settings.dart';
import 'package:freezer/translations.i18n.dart';
import 'package:isar/isar.dart';
import '../download.dart' as dl;
class DownloadManager {
//implements dl.DownloadManager {
late Isar _isar;
Isolate? _isolate;
ServiceInterface? _service;
bool _started = false;
Future<void> startDebug() async {
if (_started) return;
_started = true;
await configure();
await startService();
await Future.delayed(const Duration(milliseconds: 500));
}
Future<bool> configure() async {
_isar = await Isar.open(
[
// collections
TrackSchema,
AlbumSchema,
ArtistSchema,
PlaylistSchema,
],
directory: await Paths.dataDirectory(),
name: 'offline',
);
if (Platform.isAndroid || Platform.isIOS) {
return FlutterBackgroundService().configure(
iosConfiguration: IosConfiguration(), // fuck ios
androidConfiguration: AndroidConfiguration(
onStart: _startNative,
isForegroundMode: false,
autoStart: false,
autoStartOnBoot: false,
foregroundServiceNotificationId: DownloadService.NOTIFICATION_ID,
notificationChannelId: DownloadService.NOTIFICATION_CHANNEL_ID,
initialNotificationTitle: 'Freezer'.i18n,
initialNotificationContent: 'Starting download service...'.i18n,
));
}
// will run in foreground instead, in a separate isolate
return Future.value(true);
}
Future<bool> startService() async {
if (Platform.isAndroid) {
final didStart = await FlutterBackgroundService().startService();
if (!didStart) return false;
} else {
// UI -> Service communication
final receivePort = ReceivePort();
_isolate = await Isolate.spawn(_startIsolate, receivePort.sendPort);
_service = ServiceInterface(receivePort: receivePort);
_service!.on('sendPort', (args) {
_service!.sendPort = args!['s'];
_service!.no('sendPort');
});
}
final completer = Completer<void>();
on('ready', (args) => completer.complete());
await completer.future;
invoke('updateSettings', {
'downloadFilename': settings.downloadFilename,
'deezerLanguage': settings.deezerLanguage,
'deezerCountry': settings.deezerCountry,
});
return true;
}
void kill() {
_isolate?.kill();
}
void invoke(String method, [Map<String, dynamic>? args]) {
if (_service != null) {
_service!.send(method, args);
return;
}
FlutterBackgroundService().invoke(method, args);
}
void on(String method, ListenerCallback listener) {
if (_service != null) {
_service!.on(method, listener);
return;
}
FlutterBackgroundService().on(method).listen(listener);
}
Future<bool> checkOffline(String trackId) async {
final c =
await _isar.tracks.where().isarIdEqualTo(int.parse(trackId)).count();
return c > 0;
}
Future<bool> addOfflineTrack(d.Track track, AudioQuality downloadQuality,
{bool private = true}) async {
//Permission
if (!private && !(await dl.DownloadManager.checkCanDownload())) {
return false;
}
if (downloadQuality == AudioQuality.ASK) {
throw Exception('Invalid quality.');
}
if (private) {
if (track.artists == null ||
track.artists!.isEmpty ||
track.album == null) {
track = await DeezerAPI.instance.track(track.id);
}
// cache album art
cacheManager.getSingleFile(track.albumArt!.thumb);
cacheManager.getSingleFile(track.albumArt!.full);
await _isar.writeTxn(() async {
await _isar.tracks.put(Track.from(track));
if (track.album != null) {
await _isar.albums.put(Album.from(track.album!));
}
if (track.artists != null) {
await _isar.artists
.putAll(track.artists!.map(Artist.from).toList(growable: false));
}
});
}
// logic for downloading the track
invoke(
'addDownloads',
DownloadInfo(
trackId: track.id,
path: private ? null : settings.downloadPath,
).toJson());
return true;
}
static void _startNative(ServiceInstance service) =>
_startService(ServiceInterface(service: service));
static void _startIsolate(SendPort sendPort) async {
final receivePort = ReceivePort();
final service = ServiceInterface(receivePort: receivePort)
..sendPort = sendPort;
service.send('sendPort', {'s': receivePort.sendPort});
return _startService(service);
}
static void _startService(ServiceInterface service) =>
DownloadService(service).run();
}