work on download backend + try fix ios
This commit is contained in:
parent
f88e43cad9
commit
1257a81e90
|
@ -538,9 +538,8 @@ class DownloadManager {
|
|||
results.playlists!.add((await getPlaylist(playlist['id'])));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
} //Sanitize filename
|
||||
|
||||
//Sanitize filename
|
||||
String sanitize(String input) {
|
||||
RegExp sanitize = RegExp(r'[\/\\\?\%\*\:\|\"\<\>]');
|
||||
return input.replaceAll(sanitize, '');
|
||||
|
@ -575,8 +574,10 @@ class DownloadManager {
|
|||
}
|
||||
}
|
||||
//Final path
|
||||
path = p.join(path!,
|
||||
isSingleton ? settings.singletonFilename : settings.downloadFilename);
|
||||
path = p.join(
|
||||
path!,
|
||||
isSingleton ? settings.singletonFilename : settings.downloadFilename,
|
||||
);
|
||||
//Playlist track number variable (not accessible in service)
|
||||
if (playlistTrackNumber != null) {
|
||||
path = path.replaceAll(
|
||||
|
|
|
@ -1,9 +1,27 @@
|
|||
import 'package:freezer/api/definitions.dart' as d;
|
||||
import 'package:freezer/api/definitions.dart' show AlbumType;
|
||||
import 'package:freezer/api/paths.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
class Database {
|
||||
static Future<Isar> open() async => Isar.open(
|
||||
[
|
||||
// collections
|
||||
TrackSchema,
|
||||
AlbumSchema,
|
||||
ArtistSchema,
|
||||
PlaylistSchema,
|
||||
],
|
||||
directory: await Paths.dataDirectory(),
|
||||
name: 'offline',
|
||||
);
|
||||
|
||||
static String sanitize(String input) =>
|
||||
input.replaceAll(RegExp(r'[\/\\\?\%\*\:\|\"\<\>]'), '');
|
||||
}
|
||||
|
||||
@collection
|
||||
class Track {
|
||||
Id get isarId => int.parse(id);
|
|
@ -6,15 +6,16 @@ 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/download/database.dart';
|
||||
import 'package:freezer/api/download/service.dart';
|
||||
import 'package:freezer/api/download/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;
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class DownloadManager {
|
||||
//implements dl.DownloadManager {
|
||||
|
@ -36,17 +37,7 @@ class DownloadManager {
|
|||
}
|
||||
|
||||
Future<bool> configure() async {
|
||||
_isar = await Isar.open(
|
||||
[
|
||||
// collections
|
||||
TrackSchema,
|
||||
AlbumSchema,
|
||||
ArtistSchema,
|
||||
PlaylistSchema,
|
||||
],
|
||||
directory: await Paths.dataDirectory(),
|
||||
name: 'offline',
|
||||
);
|
||||
_isar = await Database.open();
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
return FlutterBackgroundService().configure(
|
||||
iosConfiguration: IosConfiguration(), // fuck ios
|
||||
|
@ -123,16 +114,16 @@ class DownloadManager {
|
|||
}
|
||||
|
||||
Future<bool> addOfflineTrack(d.Track track, AudioQuality downloadQuality,
|
||||
{bool private = true}) async {
|
||||
{bool offline = true, bool isSingleton = false}) async {
|
||||
//Permission
|
||||
if (!private && !(await dl.DownloadManager.checkCanDownload())) {
|
||||
if (!offline && !(await dl.DownloadManager.checkCanDownload())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (downloadQuality == AudioQuality.ASK) {
|
||||
throw Exception('Invalid quality.');
|
||||
}
|
||||
if (private) {
|
||||
if (offline) {
|
||||
if (track.artists == null ||
|
||||
track.artists!.isEmpty ||
|
||||
track.album == null) {
|
||||
|
@ -142,18 +133,6 @@ class DownloadManager {
|
|||
// 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
|
||||
|
@ -161,12 +140,52 @@ class DownloadManager {
|
|||
'addDownloads',
|
||||
DownloadInfo(
|
||||
trackId: track.id,
|
||||
path: private ? null : settings.downloadPath,
|
||||
path: offline ? null : settings.downloadPath,
|
||||
).toJson());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Generate track download path
|
||||
String? _generatePath(Track track, bool offline,
|
||||
{String? playlistName,
|
||||
int? playlistTrackNumber,
|
||||
bool isSingleton = false}) {
|
||||
var path = settings.downloadPath!;
|
||||
|
||||
if (settings.playlistFolder && playlistName != null) {
|
||||
path = p.join(path, Database.sanitize(playlistName));
|
||||
}
|
||||
|
||||
if (settings.artistFolder) path = p.join(path, '%albumArtist%');
|
||||
|
||||
//Album folder / with disk number
|
||||
if (settings.albumFolder) {
|
||||
if (settings.albumDiscFolder) {
|
||||
path = p.join(path, '%album% - Disk ${track.diskNumber ?? 1}');
|
||||
} else {
|
||||
path = p.join(path, '%album%');
|
||||
}
|
||||
}
|
||||
//Final path
|
||||
path = p.join(
|
||||
path,
|
||||
isSingleton ? settings.singletonFilename : settings.downloadFilename,
|
||||
);
|
||||
//Playlist track number variable (not accessible in service)
|
||||
if (playlistTrackNumber != null) {
|
||||
path = path.replaceAll(
|
||||
'%playlistTrackNumber%', playlistTrackNumber.toString());
|
||||
path = path.replaceAll('%0playlistTrackNumber%',
|
||||
playlistTrackNumber.toString().padLeft(2, '0'));
|
||||
} else {
|
||||
path = path.replaceAll('%playlistTrackNumber%', '');
|
||||
path = path.replaceAll('%0playlistTrackNumber%', '');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static void _startNative(ServiceInstance service) =>
|
||||
_startService(ServiceInterface(service: service));
|
||||
|
|
@ -3,15 +3,18 @@ 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/definitions.dart' as d;
|
||||
import 'package:freezer/api/download.dart';
|
||||
import 'package:freezer/api/download_manager/service_interface.dart';
|
||||
import 'package:freezer/api/download/database.dart';
|
||||
import 'package:freezer/api/download/service_interface.dart';
|
||||
import 'package:freezer/main.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
part 'download_service.freezed.dart';
|
||||
part 'download_service.g.dart';
|
||||
part 'service.freezed.dart';
|
||||
part 'service.g.dart';
|
||||
|
||||
class DownloadService {
|
||||
static const NOTIFICATION_ID = 6969;
|
||||
|
@ -28,6 +31,8 @@ class DownloadService {
|
|||
|
||||
late DeezerAPI _deezerAPI;
|
||||
|
||||
late final Isar _isar;
|
||||
|
||||
void run() async {
|
||||
service.on('addDownloads', (event) {
|
||||
downloadTrack(DownloadInfo.fromJson(event!));
|
||||
|
@ -57,25 +62,24 @@ class DownloadService {
|
|||
}
|
||||
});
|
||||
|
||||
_isar = await Database.open();
|
||||
_deezerAPI = DeezerAPI(await getCookieJar());
|
||||
await _deezerAPI.authorize();
|
||||
|
||||
service.send('ready');
|
||||
}
|
||||
|
||||
void downloadTrack(DownloadInfo info) async {
|
||||
await _deezerAPI.authorize();
|
||||
|
||||
final trackId = info.trackId;
|
||||
final deezerAudio = DeezerAudio(
|
||||
deezerAPI: _deezerAPI,
|
||||
quality: _downloadQuality ?? AudioQuality.MP3_128,
|
||||
trackId: trackId);
|
||||
|
||||
bool offline = info.path == null;
|
||||
|
||||
final track = await _deezerAPI.track(trackId);
|
||||
final file = File(join(
|
||||
info.path!,
|
||||
downloadFilename
|
||||
.replaceAll('%artist%', track.artists?.join(',') ?? '')
|
||||
.replaceAll('%title%', track.title!)));
|
||||
final file = File(fillVariables(track, info.path!));
|
||||
print('downloading to ${file.path}');
|
||||
final Uri uri;
|
||||
if (useGetURL) {
|
||||
|
@ -90,6 +94,18 @@ class DownloadService {
|
|||
uri = res.uri;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
||||
// download url and decrypt
|
||||
final req = await _deezerAPI.dio.get(uri.toString(),
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
|
@ -99,6 +115,26 @@ class DownloadService {
|
|||
await stream.pipe(fWrite);
|
||||
print('download complete!');
|
||||
}
|
||||
|
||||
String pathForOffline(d.Track t) {
|
||||
// TODO: implement
|
||||
throw Exception();
|
||||
}
|
||||
|
||||
String fillVariables(d.Track t, String path) {
|
||||
return path
|
||||
.replaceAll('%title%', Database.sanitize(t.title!))
|
||||
.replaceAll('%album%', t.album!.title!)
|
||||
.replaceAll('%artist%', t.artists?[0].name ?? '')
|
||||
.replaceAll('%albumArtist%',
|
||||
Database.sanitize(t.album?.artistString ?? t.artistString))
|
||||
.replaceAll('%artists%', t.artistString)
|
||||
.replaceAll('%feats%',
|
||||
t.artists?.sublist(1).map((e) => e.name!).join(', ') ?? '')
|
||||
.replaceAll('%trackNumber%', t.trackNumber!.toString())
|
||||
.replaceAll('%0trackNumber%', t.trackNumber.toString().padLeft(2, '0'))
|
||||
.replaceAll('%year%', '0'); // TODO: find year
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
|
@ -47,6 +47,8 @@ class Paths {
|
|||
.path;
|
||||
}
|
||||
return target.path;
|
||||
case TargetPlatform.iOS:
|
||||
return (await getApplicationSupportDirectory()).path;
|
||||
default:
|
||||
return (await getApplicationDocumentsDirectory()).path;
|
||||
}
|
||||
|
|
|
@ -131,6 +131,7 @@ void main() async {
|
|||
GetIt.instance.registerSingleton<CookieJar>(await getCookieJar());
|
||||
final deezerAPI =
|
||||
GetIt.instance.registerSingleton<DeezerAPI>(DeezerAPI(cookieJar));
|
||||
|
||||
deezerAPI.deezerCountry = settings.deezerCountry;
|
||||
deezerAPI.deezerLanguage = settings.deezerLanguage;
|
||||
deezerAPI.favoritesPlaylistId = cache.favoritesPlaylistId;
|
||||
|
@ -141,7 +142,7 @@ void main() async {
|
|||
'${record.level.name}: ${record.time}: [${record.loggerName}] ${record.message}');
|
||||
});
|
||||
|
||||
if (kDebugMode || true) {
|
||||
if (kDebugMode) {
|
||||
Logger.root.level = Level.ALL;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import 'package:freezer/ui/cached_image.dart';
|
|||
import 'package:numberpicker/numberpicker.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:freezer/api/download_manager/download_manager.dart' as newDl;
|
||||
|
||||
class SliverTrackPersistentHeader extends SliverPersistentHeaderDelegate {
|
||||
final Track track;
|
||||
|
@ -297,12 +296,12 @@ class MenuSheet {
|
|||
Text('Download'.i18n),
|
||||
icon: const Icon(Icons.file_download),
|
||||
onTap: () async {
|
||||
final dl = newDl.DownloadManager();
|
||||
await dl.startDebug();
|
||||
dl.addOfflineTrack(t, settings.downloadQuality, private: false);
|
||||
//if (await downloadManager.addOfflineTrack(t,
|
||||
// private: false, context: context, isSingleton: true) !=
|
||||
// false) showDownloadStartedToast();
|
||||
// final dl = newDl.DownloadManager();
|
||||
// await dl.startDebug();
|
||||
// dl.addOfflineTrack(t, settings.downloadQuality, private: false);
|
||||
if (await downloadManager.addOfflineTrack(t,
|
||||
private: false, context: context, isSingleton: true) !=
|
||||
false) showDownloadStartedToast();
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1028,8 +1028,7 @@ class _FilenameTemplateDialogState extends State<FilenameTemplateDialog> {
|
|||
TextButton(
|
||||
child: Text('Reset'.i18n),
|
||||
onPressed: () {
|
||||
_controller!.value =
|
||||
_controller!.value.copyWith(text: '%artist% - %title%');
|
||||
_controller!.text = '%artist% - %title%';
|
||||
_new = '%artist% - %title%';
|
||||
},
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue