use get_it + work on the new download backend + migrate to isar for cookie store
This commit is contained in:
parent
475787f433
commit
f88e43cad9
|
@ -52,6 +52,7 @@ android/app/.cxx
|
|||
/build/
|
||||
.gradle/
|
||||
*.g.dart
|
||||
*.freezed.dart
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
int? trackTokenExpiration,
|
||||
this.onStreamObtained,
|
||||
}) : _deezerAudio = DeezerAudio(
|
||||
deezerAPI: deezerAPI,
|
||||
deezerAPI: DeezerAPI.instance,
|
||||
quality: getQuality.call(),
|
||||
trackId: trackId,
|
||||
) {
|
||||
|
@ -78,7 +78,7 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
}
|
||||
|
||||
_logger.fine("authorizing...");
|
||||
if (!await deezerAPI.authorize()) {
|
||||
if (!await DeezerAPI.instance.authorize()) {
|
||||
_logger.severe("authorization failed! cannot continue!");
|
||||
throw Exception("Authorization failed!");
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ class DeezerAudioSource extends StreamAudioSource {
|
|||
if (_downloadUrl == null) {
|
||||
if (_trackToken == null) {
|
||||
// TODO: get new track token?
|
||||
final track = await deezerAPI.track(trackId);
|
||||
final track = await DeezerAPI.instance.track(trackId);
|
||||
_trackToken = track.trackToken;
|
||||
_trackTokenExpiration = track.trackTokenExpiration;
|
||||
_mediaVersion = track.playbackDetails![1];
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
|
||||
class HiveStorage implements Storage {
|
||||
final String boxName;
|
||||
final String? boxPath;
|
||||
HiveStorage(this.boxName, {this.boxPath});
|
||||
|
||||
bool _initialized = false;
|
||||
late final Box<String> _box;
|
||||
|
||||
Future<void>? _initFuture;
|
||||
|
||||
Future<void> init(bool persistSession, bool ignoreExpires) =>
|
||||
_initFuture ??= _init(persistSession, ignoreExpires);
|
||||
|
||||
Future<void> _init(bool persistSession, bool ignoreExpires) async {
|
||||
if (_initialized) return;
|
||||
_initialized = true;
|
||||
_box = await Hive.openBox(boxName, path: boxPath);
|
||||
print('init() finished');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> read(String key) async {
|
||||
await _initFuture;
|
||||
return _box.get(key);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> write(String key, String value) => _box.put(key, value);
|
||||
@override
|
||||
Future<void> delete(String key) => _box.delete(key);
|
||||
@override
|
||||
Future<void> deleteAll(List<String> keys) => _box.deleteAll(keys);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:freezer/utils.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
part 'cookie_jar_isar_storage.g.dart';
|
||||
|
||||
class IsarStorage implements Storage {
|
||||
final String dbName;
|
||||
final String dbPath;
|
||||
IsarStorage(this.dbName, this.dbPath);
|
||||
|
||||
late final Isar _isar;
|
||||
|
||||
Future<void>? _initFuture;
|
||||
|
||||
@override
|
||||
Future<void> init(bool persistSession, bool ignoreExpires) =>
|
||||
_initFuture ??= _init(persistSession, ignoreExpires);
|
||||
|
||||
Future<void> _init(bool persistSession, bool ignoreExpires) async {
|
||||
_isar = await Isar.open([CookieSchema], directory: dbPath, name: dbName);
|
||||
print('init() finished');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> read(String key) async {
|
||||
await _initFuture;
|
||||
final cookie = await _isar.cookies.get(Utils.fastHash(key));
|
||||
if (cookie == null) return null;
|
||||
return cookie.value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> write(String key, String value) =>
|
||||
_isar.writeTxn(() => _isar.cookies.put(Cookie()
|
||||
..key = key
|
||||
..value = value));
|
||||
@override
|
||||
Future<void> delete(String key) =>
|
||||
_isar.writeTxn(() => _isar.cookies.delete(Utils.fastHash(key)));
|
||||
@override
|
||||
Future<void> deleteAll(List<String> keys) =>
|
||||
_isar.writeTxn(() => _isar.cookies
|
||||
.deleteAll(keys.map(Utils.fastHash).toList(growable: false)));
|
||||
}
|
||||
|
||||
@collection
|
||||
class Cookie {
|
||||
Id get isarId => Utils.fastHash(key);
|
||||
|
||||
late String key;
|
||||
late String value;
|
||||
}
|
|
@ -8,17 +8,17 @@ import 'package:freezer/api/cache.dart';
|
|||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/spotify.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'cookie_jar_hive_storage.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
|
||||
final deezerAPI = DeezerAPI();
|
||||
final cookieJar = PersistCookieJar(storage: HiveStorage('cookies'));
|
||||
|
||||
class DeezerAPI {
|
||||
/// Shorthand for GetIt.instance<DeezerAPI>()
|
||||
static DeezerAPI get instance => GetIt.instance<DeezerAPI>();
|
||||
|
||||
// from deemix: https://gitlab.com/RemixDev/deemix-js/-/blob/main/deemix/utils/deezer.js?ref_type=heads#L6
|
||||
static const CLIENT_ID = "172365";
|
||||
static const CLIENT_SECRET = "fb0bec7ccc063dab0417eb7b0d847f34";
|
||||
|
@ -40,7 +40,9 @@ class DeezerAPI {
|
|||
static String get userAgent => USER_AGENTS[defaultTargetPlatform]!;
|
||||
|
||||
static final _logger = Logger('DeezerAPI');
|
||||
DeezerAPI();
|
||||
|
||||
final CookieJar cookieJar;
|
||||
DeezerAPI(this.cookieJar);
|
||||
|
||||
set arl(String? arl) {
|
||||
if (arl == null) {
|
||||
|
@ -62,6 +64,9 @@ class DeezerAPI {
|
|||
String? favoritesPlaylistId;
|
||||
String? sid;
|
||||
|
||||
late String deezerLanguage;
|
||||
late String deezerCountry;
|
||||
|
||||
late String licenseToken;
|
||||
late bool canStreamLossless;
|
||||
late bool canStreamHQ;
|
||||
|
@ -77,13 +82,12 @@ class DeezerAPI {
|
|||
//Get headers
|
||||
Map<String, String> get headers => {
|
||||
"User-Agent": userAgent,
|
||||
"Content-Language":
|
||||
'${settings.deezerLanguage}-${settings.deezerCountry}',
|
||||
"Content-Language": '$deezerLanguage-$deezerCountry',
|
||||
"Cache-Control": "max-age=0",
|
||||
"Accept": "*/*",
|
||||
"Accept-Charset": "utf-8,ISO-8859-1;q=0.7,*;q=0.3",
|
||||
"Accept-Language":
|
||||
"${settings.deezerLanguage}-${settings.deezerCountry},${settings.deezerLanguage};q=0.9,en-US;q=0.8,en;q=0.7",
|
||||
"$deezerLanguage-$deezerCountry,$deezerLanguage;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||
"Connection": "keep-alive",
|
||||
};
|
||||
|
||||
|
@ -292,6 +296,9 @@ class DeezerAPI {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get single track url
|
||||
///
|
||||
/// Shorcut for [getTracksUrl([trackToken], format)[0]]
|
||||
Future<GetTrackUrlResponse> getTrackUrl(
|
||||
String trackToken, String format) async =>
|
||||
(await getTracksUrl([trackToken], format))[0];
|
||||
|
@ -306,7 +313,11 @@ class DeezerAPI {
|
|||
{
|
||||
"type": "FULL",
|
||||
"formats": [
|
||||
{"cipher": "BF_CBC_STRIPE", "format": format}
|
||||
{"cipher": "BF_CBC_STRIPE", "format": format},
|
||||
{
|
||||
"cipher": "BF_CBC_STRIPE",
|
||||
"format": "MP3_MISC" // allow for custom MP3s
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
|
@ -771,7 +782,7 @@ class DeezerAPI {
|
|||
'nb': 1000,
|
||||
'show_id': showId,
|
||||
'start': 0,
|
||||
'user_id': int.parse(deezerAPI.userId!)
|
||||
'user_id': int.parse(userId!)
|
||||
});
|
||||
return data['results']['EPISODES']['data']
|
||||
.map<ShowEpisode>((e) => ShowEpisode.fromPrivateJson(e))
|
||||
|
|
|
@ -174,7 +174,7 @@ class DownloadManager {
|
|||
if (track.artists == null ||
|
||||
track.artists!.isEmpty ||
|
||||
track.album == null) {
|
||||
track = await deezerAPI.track(track.id);
|
||||
track = await DeezerAPI.instance.track(track.id);
|
||||
}
|
||||
|
||||
//Add to DB
|
||||
|
@ -212,7 +212,7 @@ class DownloadManager {
|
|||
|
||||
//Get from API if no tracks
|
||||
if (album!.tracks == null || album.tracks!.isEmpty) {
|
||||
album = await deezerAPI.album(album.id);
|
||||
album = await DeezerAPI.instance.album(album.id);
|
||||
}
|
||||
|
||||
//Add to DB
|
||||
|
@ -258,7 +258,7 @@ class DownloadManager {
|
|||
//Get tracks if missing
|
||||
if (playlist!.tracks == null ||
|
||||
playlist.tracks!.length < playlist.trackCount!) {
|
||||
playlist = await deezerAPI.fullPlaylist(playlist.id);
|
||||
playlist = await DeezerAPI.instance.fullPlaylist(playlist.id);
|
||||
}
|
||||
|
||||
//Add to DB
|
||||
|
@ -643,6 +643,7 @@ class DownloadManager {
|
|||
|
||||
//Check storage permission
|
||||
static Future<bool> checkPermission() async {
|
||||
if (Platform.isLinux || Platform.isWindows) return true;
|
||||
if (await Permission.storage.request().isGranted) {
|
||||
return true;
|
||||
} else if ( // android 12 or later
|
||||
|
@ -748,7 +749,7 @@ class Download {
|
|||
{private = true, AudioQuality? quality}) async {
|
||||
//Get download info
|
||||
if (t.playbackDetails == null || t.playbackDetails == []) {
|
||||
t = await deezerAPI.track(t.id);
|
||||
t = await DeezerAPI.instance.track(t.id);
|
||||
}
|
||||
return {
|
||||
"private": private,
|
||||
|
|
|
@ -21,6 +21,7 @@ class Track {
|
|||
late final bool favorite;
|
||||
late final int? diskNumber;
|
||||
late final bool explicit;
|
||||
late final String localPath;
|
||||
|
||||
Track();
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
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;
|
||||
|
@ -20,8 +21,19 @@ class DownloadManager {
|
|||
|
||||
late Isar _isar;
|
||||
|
||||
SendPort? _sendPort;
|
||||
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(
|
||||
|
@ -56,13 +68,28 @@ class DownloadManager {
|
|||
|
||||
Future<bool> startService() async {
|
||||
if (Platform.isAndroid) {
|
||||
return FlutterBackgroundService().startService();
|
||||
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 receivePort = ReceivePort();
|
||||
_sendPort = receivePort.sendPort;
|
||||
_isolate = await Isolate.spawn(
|
||||
_startService, ServiceInterface(receivePort: receivePort));
|
||||
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;
|
||||
}
|
||||
|
@ -72,34 +99,44 @@ class DownloadManager {
|
|||
}
|
||||
|
||||
void invoke(String method, [Map<String, dynamic>? args]) {
|
||||
if (_sendPort != null) {
|
||||
_sendPort!.send({
|
||||
'method': method,
|
||||
if (args != null) ...args,
|
||||
});
|
||||
if (_service != null) {
|
||||
_service!.send(method, args);
|
||||
return;
|
||||
}
|
||||
|
||||
FlutterBackgroundService().invoke(method, args);
|
||||
}
|
||||
|
||||
Future<bool> addOfflineTrack(d.Track track,
|
||||
{bool private = true, BuildContext? context, isSingleton = false}) async {
|
||||
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;
|
||||
}
|
||||
|
||||
//Ask for quality
|
||||
//AudioQuality? quality;
|
||||
if (!private && settings.downloadQuality == AudioQuality.ASK) {
|
||||
// quality = await qualitySelect(context!);
|
||||
// if (quality == null) 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.track(track.id);
|
||||
track = await DeezerAPI.instance.track(track.id);
|
||||
}
|
||||
|
||||
// cache album art
|
||||
|
@ -120,7 +157,12 @@ class DownloadManager {
|
|||
}
|
||||
|
||||
// logic for downloading the track
|
||||
invoke('addDownloads', {'track': track.toJson()});
|
||||
invoke(
|
||||
'addDownloads',
|
||||
DownloadInfo(
|
||||
trackId: track.id,
|
||||
path: private ? null : settings.downloadPath,
|
||||
).toJson());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -128,6 +170,14 @@ class DownloadManager {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
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';
|
||||
import 'package:freezer/api/download_manager/service_interface.dart';
|
||||
import 'package:freezer/main.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
part 'download_service.freezed.dart';
|
||||
part 'download_service.g.dart';
|
||||
|
||||
class DownloadService {
|
||||
static const NOTIFICATION_ID = 6969;
|
||||
|
@ -12,12 +24,18 @@ class DownloadService {
|
|||
AudioQuality? _downloadQuality;
|
||||
bool useGetURL = false;
|
||||
|
||||
void run() {
|
||||
service.on('addDownloads').listen((event) {});
|
||||
service.on('updateQuality').listen((event) {
|
||||
late String downloadFilename;
|
||||
|
||||
late DeezerAPI _deezerAPI;
|
||||
|
||||
void run() async {
|
||||
service.on('addDownloads', (event) {
|
||||
downloadTrack(DownloadInfo.fromJson(event!));
|
||||
});
|
||||
service.on('updateQuality', (event) {
|
||||
_preferredQuality = AudioQuality.values[event!['q']!];
|
||||
});
|
||||
service.on('updateCapabilities').listen((event) {
|
||||
service.on('updateCapabilities', (event) {
|
||||
final bool canStreamHQ = event!['canStreamHQ'];
|
||||
final bool canStreamLossless = event['canStreamLossless'];
|
||||
|
||||
|
@ -26,12 +44,67 @@ class DownloadService {
|
|||
_downloadQuality = settings.maxQualityFor(
|
||||
_preferredQuality!, canStreamHQ, canStreamLossless);
|
||||
});
|
||||
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');
|
||||
}
|
||||
|
||||
void downloadTrack(String trackId) {
|
||||
// final deezerAudio = DeezerAudio(deezerAPI: deezerAPI, md5origin: md5origin, quality: quality, trackId: trackId, mediaVersion: mediaVersion)
|
||||
// if (useGetURL) {
|
||||
// final url =
|
||||
// }
|
||||
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!');
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DownloadInfo with _$DownloadInfo {
|
||||
const factory DownloadInfo({required String trackId, String? path}) =
|
||||
_DownloadInfo;
|
||||
factory DownloadInfo.fromJson(Map<String, dynamic> json) =>
|
||||
_$DownloadInfoFromJson(json);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,46 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter_background_service/flutter_background_service.dart';
|
||||
|
||||
typedef ListenerCallback = void Function(Map<String, dynamic>? args);
|
||||
|
||||
class ServiceInterface {
|
||||
final ReceivePort? receivePort;
|
||||
late SendPort sendPort;
|
||||
final ServiceInstance? service;
|
||||
|
||||
bool _isListening = false;
|
||||
final _listeners = HashMap<String, ListenerCallback>();
|
||||
|
||||
ServiceInterface({this.receivePort, this.service})
|
||||
: assert(receivePort != null || service != null);
|
||||
|
||||
Stream<Map<String, dynamic>?> on(String method) {
|
||||
void on(String method, ListenerCallback listener) {
|
||||
if (service != null) {
|
||||
return service!.on(method);
|
||||
service!.on(method).listen(listener);
|
||||
}
|
||||
|
||||
return receivePort!
|
||||
.where((event) => event['method'] == method)
|
||||
.map((event) => (event as Map?)?.cast<String, dynamic>());
|
||||
if (!_isListening) {
|
||||
_isListening = true;
|
||||
receivePort!.listen((message) {
|
||||
final method = message['_'];
|
||||
_listeners[method]!.call(message);
|
||||
});
|
||||
}
|
||||
|
||||
_listeners[method] = listener;
|
||||
}
|
||||
|
||||
void no(String method) {
|
||||
_listeners.remove(method);
|
||||
}
|
||||
|
||||
void send(String method, [Map<String, dynamic>? args]) {
|
||||
if (service != null) {
|
||||
return service!.invoke(method, args);
|
||||
}
|
||||
|
||||
sendPort!.send({'_': method, if (args != null) ...args});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
Importer importer = Importer();
|
||||
|
||||
|
@ -29,6 +30,8 @@ class Importer {
|
|||
int get error =>
|
||||
tracks.fold(0, (v, t) => (t.state == TrackImportState.ERROR) ? v + 1 : v);
|
||||
|
||||
final deezerAPI = GetIt.instance<DeezerAPI>();
|
||||
|
||||
Importer();
|
||||
|
||||
//Start importing wrapper
|
||||
|
@ -45,8 +48,8 @@ class Importer {
|
|||
}).toList();
|
||||
|
||||
//Create playlist
|
||||
playlistId =
|
||||
await deezerAPI.createPlaylist(title, description: description);
|
||||
playlistId = await DeezerAPI.instance
|
||||
.createPlaylist(title, description: description);
|
||||
|
||||
busy = true;
|
||||
done = false;
|
||||
|
|
|
@ -61,4 +61,12 @@ class Paths {
|
|||
|
||||
return (await getTemporaryDirectory()).path;
|
||||
}
|
||||
|
||||
static Future<String> offlineDir() async {
|
||||
final topDir = Platform.isLinux || Platform.isWindows
|
||||
? await dataDirectory()
|
||||
: (await getExternalStorageDirectory())!.path;
|
||||
final target = await Directory(path.join(topDir, 'offline')).create();
|
||||
return target.path;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class PipeAPI {
|
|||
|
||||
final _logger = Logger('PipeAPI');
|
||||
|
||||
Dio get dio => deezerAPI.dio;
|
||||
Dio get dio => DeezerAPI.instance.dio;
|
||||
|
||||
Future<void> authorize({bool force = false}) async {
|
||||
// authorize on pipe.deezer.com
|
||||
|
|
|
@ -27,7 +27,6 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
|
||||
PlayerHelper playerHelper = PlayerHelper();
|
||||
late AudioPlayerTask audioHandler;
|
||||
bool failsafe = false;
|
||||
|
||||
class AudioPlayerTaskInitArguments {
|
||||
|
@ -57,15 +56,6 @@ class AudioPlayerTaskInitArguments {
|
|||
lastFMUsername: settings.lastFMUsername,
|
||||
lastFMPassword: settings.lastFMPassword);
|
||||
}
|
||||
|
||||
static Future<AudioPlayerTaskInitArguments> loadSettings() async {
|
||||
final settings = await Settings.load();
|
||||
|
||||
final deezerAPI = DeezerAPI()..arl = settings.arl;
|
||||
await deezerAPI.authorize();
|
||||
|
||||
return from(settings: settings, deezerAPI: deezerAPI);
|
||||
}
|
||||
}
|
||||
|
||||
class AudioPlayerTask extends BaseAudioHandler {
|
||||
|
@ -145,11 +135,7 @@ class AudioPlayerTask extends BaseAudioHandler {
|
|||
late final LazyBox _box;
|
||||
|
||||
AudioPlayerTask([AudioPlayerTaskInitArguments? initArgs]) {
|
||||
if (initArgs == null) {
|
||||
unawaited(AudioPlayerTaskInitArguments.loadSettings().then(_start));
|
||||
return;
|
||||
}
|
||||
unawaited(_start(initArgs));
|
||||
unawaited(_start(initArgs!));
|
||||
}
|
||||
|
||||
Future<void> _start(AudioPlayerTaskInitArguments initArgs) async {
|
||||
|
|
|
@ -8,9 +8,12 @@ import 'package:freezer/api/player/audio_handler.dart';
|
|||
import 'package:freezer/main.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
AudioPlayerTask get audioHandler => GetIt.instance<AudioPlayerTask>();
|
||||
|
||||
class PlayerHelper {
|
||||
late StreamSubscription _customEventSubscription;
|
||||
late StreamSubscription _mediaItemSubscription;
|
||||
|
@ -66,9 +69,9 @@ class PlayerHelper {
|
|||
failsafe = true;
|
||||
|
||||
final initArgs = AudioPlayerTaskInitArguments.from(
|
||||
settings: settings, deezerAPI: deezerAPI);
|
||||
settings: settings, deezerAPI: DeezerAPI.instance);
|
||||
// initialize our audiohandler instance
|
||||
audioHandler = await AudioService.init<AudioPlayerTask>(
|
||||
final audioHandler = await AudioService.init<AudioPlayerTask>(
|
||||
builder: () => AudioPlayerTask(initArgs),
|
||||
config: AudioServiceConfig(
|
||||
notificationColor: settings.primaryColor,
|
||||
|
@ -87,6 +90,8 @@ class PlayerHelper {
|
|||
),
|
||||
cacheManager: cacheManager,
|
||||
);
|
||||
|
||||
GetIt.instance.registerSingleton<AudioPlayerTask>(audioHandler);
|
||||
}
|
||||
|
||||
Future<void> start() async {
|
||||
|
@ -214,7 +219,7 @@ class PlayerHelper {
|
|||
|
||||
//Play mix by track
|
||||
Future<void> playMix(String trackId, String trackTitle) async {
|
||||
List<Track> tracks = await deezerAPI.playMix(trackId);
|
||||
List<Track> tracks = await DeezerAPI.instance.playMix(trackId);
|
||||
await playFromTrackList(
|
||||
tracks,
|
||||
tracks[0].id,
|
||||
|
@ -232,7 +237,8 @@ class PlayerHelper {
|
|||
id: track.id,
|
||||
text: 'Mix based on %s'.i18n.fill([track.title!]),
|
||||
source: 'searchMix'));
|
||||
List<Track> tracks = await deezerAPI.getSearchTrackMix(track.id, false);
|
||||
List<Track> tracks =
|
||||
await DeezerAPI.instance.getSearchTrackMix(track.id, false);
|
||||
// discard first track (if it is the searched track)
|
||||
if (tracks[0].id == track.id) tracks.removeAt(0);
|
||||
await playFuture; // avoid race conditions
|
||||
|
@ -243,7 +249,8 @@ class PlayerHelper {
|
|||
}
|
||||
|
||||
Future<void> playSearchMix(String trackId, String trackTitle) async {
|
||||
List<Track> tracks = await deezerAPI.getSearchTrackMix(trackId, true);
|
||||
List<Track> tracks =
|
||||
await DeezerAPI.instance.getSearchTrackMix(trackId, true);
|
||||
await playFromTrackList(
|
||||
tracks,
|
||||
null, // we can avoid passing it, as the index is 0
|
||||
|
@ -309,9 +316,9 @@ class PlayerHelper {
|
|||
|
||||
//Flow songs cannot be accessed by smart track list call
|
||||
if (stl.id! == 'flow') {
|
||||
stl.tracks = await deezerAPI.flow(stl.flowConfig);
|
||||
stl.tracks = await DeezerAPI.instance.flow(stl.flowConfig);
|
||||
} else {
|
||||
stl = await deezerAPI.smartTrackList(stl.id);
|
||||
stl = await DeezerAPI.instance.smartTrackList(stl.id);
|
||||
}
|
||||
}
|
||||
QueueSource queueSource = QueueSource(
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freezer/api/player/audio_handler.dart';
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/importer.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:html/dom.dart' as dom;
|
||||
import 'package:http/http.dart' as http;
|
||||
|
@ -13,6 +14,7 @@ import 'dart:io';
|
|||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class SpotifyScrapper {
|
||||
static final deezerAPI = GetIt.instance<DeezerAPI>();
|
||||
//Parse spotify URL to URI (spotify:track:1234)
|
||||
static String? parseUrl(String url) {
|
||||
Uri uri = Uri.parse(url);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -13,6 +14,7 @@ import 'package:flutter_displaymode/flutter_displaymode.dart';
|
|||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/api/cookie_jar_isar_storage.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/paths.dart';
|
||||
import 'package:freezer/icons.dart';
|
||||
|
@ -28,6 +30,7 @@ import 'package:freezer/ui/login_screen.dart';
|
|||
import 'package:freezer/ui/player_screen.dart';
|
||||
import 'package:freezer/ui/search.dart';
|
||||
import 'package:freezer/ui/settings_screen.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:hive_flutter/adapters.dart';
|
||||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -94,7 +97,17 @@ void main() async {
|
|||
..registerAdapter(NavigationRailAppearanceAdapter())
|
||||
..registerAdapter(HiveCacheObjectAdapter(typeId: 35)); // not working?
|
||||
|
||||
Hive.init(await Paths.dataDirectory());
|
||||
final dataDir = await Paths.dataDirectory();
|
||||
|
||||
Hive.init(dataDir);
|
||||
|
||||
// photos
|
||||
cacheManager = CacheManager(Config(
|
||||
DefaultCacheManager.key,
|
||||
// cache aggressively
|
||||
stalePeriod: const Duration(days: 30),
|
||||
maxNrOfCacheObjects: 5000,
|
||||
));
|
||||
|
||||
//Initialize globals
|
||||
try {
|
||||
|
@ -110,16 +123,16 @@ void main() async {
|
|||
exit(1);
|
||||
}
|
||||
downloadManager.init();
|
||||
// photos
|
||||
cacheManager = CacheManager(Config(
|
||||
DefaultCacheManager.key,
|
||||
// cache aggressively
|
||||
stalePeriod: const Duration(days: 30),
|
||||
maxNrOfCacheObjects: 5000,
|
||||
));
|
||||
|
||||
// cacheManager = HiveCacheManager(
|
||||
// boxName: 'freezer-images', boxPath: await Paths.cacheDir());
|
||||
// TODO: WA
|
||||
final cookieJar =
|
||||
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;
|
||||
|
||||
Logger.root.onRecord.listen((record) {
|
||||
|
@ -138,6 +151,9 @@ void main() async {
|
|||
runApp(const FreezerApp());
|
||||
}
|
||||
|
||||
Future<PersistCookieJar> getCookieJar() async => PersistCookieJar(
|
||||
storage: IsarStorage('cookies', await Paths.dataDirectory()));
|
||||
|
||||
class FreezerApp extends StatefulWidget {
|
||||
const FreezerApp({super.key});
|
||||
|
||||
|
@ -276,6 +292,7 @@ class _LoginMainWrapperState extends State<LoginMainWrapper> {
|
|||
@override
|
||||
void initState() {
|
||||
if (settings.arl != null) {
|
||||
final deezerAPI = GetIt.instance<DeezerAPI>();
|
||||
//Load token on background
|
||||
deezerAPI.arl = settings.arl!;
|
||||
settings.offlineMode = true;
|
||||
|
@ -297,7 +314,7 @@ class _LoginMainWrapperState extends State<LoginMainWrapper> {
|
|||
}
|
||||
|
||||
Future _logOut() async {
|
||||
await deezerAPI.logout();
|
||||
await GetIt.instance<DeezerAPI>().logout();
|
||||
setState(() {
|
||||
settings.arl = null;
|
||||
settings.offlineMode = false;
|
||||
|
@ -423,13 +440,14 @@ class MainScreenState extends State<MainScreen>
|
|||
}
|
||||
|
||||
void _startPreload(String type) async {
|
||||
await deezerAPI.authorize();
|
||||
await DeezerAPI.instance.authorize();
|
||||
if (type == 'flow') {
|
||||
await playerHelper.playFromSmartTrackList(SmartTrackList(id: 'flow'));
|
||||
return;
|
||||
}
|
||||
if (type == 'favorites') {
|
||||
Playlist p = await deezerAPI.fullPlaylist(deezerAPI.favoritesPlaylistId);
|
||||
Playlist p = await DeezerAPI.instance
|
||||
.fullPlaylist(DeezerAPI.instance.favoritesPlaylistId);
|
||||
playerHelper.playFromPlaylist(p, p.tracks![0].id);
|
||||
}
|
||||
}
|
||||
|
@ -440,7 +458,7 @@ class MainScreenState extends State<MainScreen>
|
|||
await DownloadManager.platform.invokeMethod('getPreloadInfo');
|
||||
if (info != null) {
|
||||
//Used if started from android auto
|
||||
await deezerAPI.authorize();
|
||||
await DeezerAPI.instance.authorize();
|
||||
_startPreload(info);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class _AlbumDetailsState extends State<AlbumDetails> {
|
|||
//Get album from API, if doesn't have tracks
|
||||
if (album!.tracks == null || album!.tracks!.isEmpty) {
|
||||
try {
|
||||
Album a = await deezerAPI.album(album!.id);
|
||||
Album a = await DeezerAPI.instance.album(album!.id);
|
||||
//Preserve library
|
||||
a.library = album!.library;
|
||||
setState(() => album = a);
|
||||
|
@ -185,14 +185,15 @@ class _AlbumDetailsState extends State<AlbumDetails> {
|
|||
onPressed: () async {
|
||||
//Add to library
|
||||
if (!album!.library!) {
|
||||
await deezerAPI.addFavoriteAlbum(album!.id);
|
||||
await DeezerAPI.instance
|
||||
.addFavoriteAlbum(album!.id);
|
||||
ScaffoldMessenger.of(context)
|
||||
.snack('Added to library'.i18n);
|
||||
setState(() => album!.library = true);
|
||||
return;
|
||||
}
|
||||
//Remove
|
||||
await deezerAPI.removeAlbum(album!.id);
|
||||
await DeezerAPI.instance.removeAlbum(album!.id);
|
||||
ScaffoldMessenger.of(context)
|
||||
.snack('Album removed from library!'.i18n);
|
||||
setState(() => album!.library = false);
|
||||
|
@ -278,7 +279,7 @@ class _MakeAlbumOfflineState extends State<MakeAlbumOffline> {
|
|||
onChanged: (v) async {
|
||||
if (v) {
|
||||
//Add to offline
|
||||
await deezerAPI.addFavoriteAlbum(widget.album!.id);
|
||||
await DeezerAPI.instance.addFavoriteAlbum(widget.album!.id);
|
||||
downloadManager.addOfflineAlbum(widget.album, private: true);
|
||||
MenuSheet(context).showDownloadStartedToast();
|
||||
setState(() {
|
||||
|
@ -325,7 +326,7 @@ class _ArtistDetailsState extends State<ArtistDetails> {
|
|||
Future<Artist> _loadArtist(Artist artist) async {
|
||||
//Load artist from api if no albums
|
||||
if ((artist.albums ?? []).isEmpty) {
|
||||
return await deezerAPI.artist(artist.id);
|
||||
return await DeezerAPI.instance.artist(artist.id);
|
||||
}
|
||||
|
||||
return artist;
|
||||
|
@ -428,7 +429,8 @@ class _ArtistDetailsState extends State<ArtistDetails> {
|
|||
icon: const Icon(Icons.favorite),
|
||||
label: Text('Library'.i18n),
|
||||
onPressed: () async {
|
||||
await deezerAPI.addFavoriteArtist(widget.artist.id);
|
||||
await DeezerAPI.instance
|
||||
.addFavoriteArtist(widget.artist.id);
|
||||
ScaffoldMessenger.of(context)
|
||||
.snack('Added to library'.i18n);
|
||||
},
|
||||
|
@ -439,7 +441,7 @@ class _ArtistDetailsState extends State<ArtistDetails> {
|
|||
label: Text('Radio'.i18n),
|
||||
onPressed: () async {
|
||||
List<Track> tracks =
|
||||
(await deezerAPI.smartRadio(artist.id))!;
|
||||
(await DeezerAPI.instance.smartRadio(artist.id))!;
|
||||
playerHelper.playFromTrackList(
|
||||
tracks,
|
||||
tracks[0].id,
|
||||
|
@ -593,8 +595,8 @@ class _DiscographyScreenState extends State<DiscographyScreen> {
|
|||
//Fetch data
|
||||
List<Album>? data;
|
||||
try {
|
||||
data = await deezerAPI.discographyPage(artist!.id,
|
||||
start: artist!.albums!.length);
|
||||
data = await DeezerAPI.instance
|
||||
.discographyPage(artist!.id, start: artist!.albums!.length);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_error = true;
|
||||
|
@ -787,7 +789,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
//Get another page of tracks
|
||||
List<Track>? tracks;
|
||||
try {
|
||||
tracks = await deezerAPI.playlistTracksPage(playlist!.id, pos);
|
||||
tracks = await DeezerAPI.instance.playlistTracksPage(playlist!.id, pos);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_error = true;
|
||||
|
@ -816,7 +818,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
|
||||
//Preload tracks
|
||||
if (playlist!.tracks!.length < playlist!.trackCount!) {
|
||||
playlist = await deezerAPI.fullPlaylist(playlist!.id);
|
||||
playlist = await DeezerAPI.instance.fullPlaylist(playlist!.id);
|
||||
}
|
||||
setState(() => _sort = cache.sorts[index]);
|
||||
}
|
||||
|
@ -834,7 +836,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
|
||||
//Preload for sorting
|
||||
if (playlist!.tracks!.length < playlist!.trackCount!) {
|
||||
playlist = await deezerAPI.fullPlaylist(playlist!.id);
|
||||
playlist = await DeezerAPI.instance.fullPlaylist(playlist!.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -852,7 +854,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
//Load if no tracks
|
||||
if (playlist!.tracks!.isEmpty) {
|
||||
//Get correct metadata
|
||||
deezerAPI.playlist(playlist!.id).then((Playlist p) {
|
||||
DeezerAPI.instance.playlist(playlist!.id).then((Playlist p) {
|
||||
setState(() {
|
||||
playlist = p;
|
||||
});
|
||||
|
@ -987,7 +989,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
MakePlaylistOffline(playlist),
|
||||
if (playlist!.user!.name != deezerAPI.userName)
|
||||
if (playlist!.user!.name != DeezerAPI.instance.userName)
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
playlist!.library!
|
||||
|
@ -1000,14 +1002,14 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
onPressed: () async {
|
||||
//Add to library
|
||||
if (!playlist!.library!) {
|
||||
await deezerAPI.addPlaylist(playlist!.id);
|
||||
await DeezerAPI.instance.addPlaylist(playlist!.id);
|
||||
ScaffoldMessenger.of(context)
|
||||
.snack('Added to library'.i18n);
|
||||
setState(() => playlist!.library = true);
|
||||
return;
|
||||
}
|
||||
//Remove
|
||||
await deezerAPI.removePlaylist(playlist!.id);
|
||||
await DeezerAPI.instance.removePlaylist(playlist!.id);
|
||||
ScaffoldMessenger.of(context)
|
||||
.snack('Playlist removed from library!'.i18n);
|
||||
setState(() => playlist!.library = false);
|
||||
|
@ -1030,7 +1032,8 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
onSelected: (SortType s) async {
|
||||
if (playlist!.tracks!.length < playlist!.trackCount!) {
|
||||
//Preload whole playlist
|
||||
playlist = await deezerAPI.fullPlaylist(playlist!.id);
|
||||
playlist =
|
||||
await DeezerAPI.instance.fullPlaylist(playlist!.id);
|
||||
}
|
||||
setState(() => _sort!.type = s);
|
||||
|
||||
|
@ -1097,7 +1100,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
}, onSecondary: (details) {
|
||||
MenuSheet m = MenuSheet(context);
|
||||
m.defaultTrackMenu(t, details: details, options: [
|
||||
if (playlist!.user!.id == deezerAPI.userId)
|
||||
if (playlist!.user!.id == DeezerAPI.instance.userId)
|
||||
m.removeFromPlaylist(t, playlist)
|
||||
]);
|
||||
});
|
||||
|
@ -1150,8 +1153,8 @@ class _MakePlaylistOfflineState extends State<MakePlaylistOffline> {
|
|||
if (v) {
|
||||
//Add to offline
|
||||
if (widget.playlist!.user != null &&
|
||||
widget.playlist!.user!.id != deezerAPI.userId) {
|
||||
await deezerAPI.addPlaylist(widget.playlist!.id);
|
||||
widget.playlist!.user!.id != DeezerAPI.instance.userId) {
|
||||
await DeezerAPI.instance.addPlaylist(widget.playlist!.id);
|
||||
}
|
||||
downloadManager.addOfflinePlaylist(widget.playlist,
|
||||
private: true);
|
||||
|
@ -1197,7 +1200,7 @@ class _ShowScreenState extends State<ShowScreen> {
|
|||
//Fetch
|
||||
List<ShowEpisode>? e;
|
||||
try {
|
||||
e = await deezerAPI.allShowEpisodes(_show!.id);
|
||||
e = await DeezerAPI.instance.allShowEpisodes(_show!.id);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:cookie_jar/cookie_jar.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class ExternalLinkRoute extends StatefulWidget {
|
||||
|
@ -45,7 +46,8 @@ class _ExternalLinkRouteState extends State<ExternalLinkRoute> {
|
|||
}
|
||||
|
||||
Future<Map<String, String>> _resolveHeaders(Uri uri) async {
|
||||
List<Cookie> cookies = await cookieJar.loadForRequest(uri);
|
||||
List<Cookie> cookies =
|
||||
await GetIt.instance<CookieJar>().loadForRequest(uri);
|
||||
print(cookies);
|
||||
return {'Cookie': cookies.join(';')};
|
||||
}
|
||||
|
|
|
@ -148,9 +148,9 @@ class _HomePageWidgetState extends State<HomePageWidget> {
|
|||
//Fetch channel from api
|
||||
try {
|
||||
if (widget.channel == null) {
|
||||
homePage = await deezerAPI.homePage();
|
||||
homePage = await DeezerAPI.instance.homePage();
|
||||
} else {
|
||||
homePage = await deezerAPI.getChannel(widget.channel!.target);
|
||||
homePage = await DeezerAPI.instance.getChannel(widget.channel!.target);
|
||||
}
|
||||
} catch (e) {
|
||||
homePage = null;
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:freezer/ui/error.dart';
|
|||
import 'package:freezer/ui/importer_screen.dart';
|
||||
import 'package:freezer/ui/tiles.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import 'menu.dart';
|
||||
import '../api/download.dart';
|
||||
|
@ -241,6 +242,8 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
int? trackCount;
|
||||
Sorting? _sort = Sorting(sourceType: SortSourceTypes.TRACKS);
|
||||
|
||||
final deezerAPI = DeezerAPI.instance;
|
||||
|
||||
Playlist get _playlist => Playlist(id: deezerAPI.favoritesPlaylistId!);
|
||||
|
||||
List<Track> get _sorted {
|
||||
|
@ -595,7 +598,7 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
|||
Future _load() async {
|
||||
if (settings.offlineMode) return;
|
||||
try {
|
||||
List<Album> albums = await deezerAPI.getAlbums();
|
||||
List<Album> albums = await DeezerAPI.instance.getAlbums();
|
||||
setState(() => _albums = albums);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
@ -799,7 +802,7 @@ class _LibraryArtistsState extends State<LibraryArtists> {
|
|||
//Fetch
|
||||
List<Artist>? data;
|
||||
try {
|
||||
data = await deezerAPI.getArtists();
|
||||
data = await DeezerAPI.instance.getArtists();
|
||||
} catch (e) {}
|
||||
//Update UI
|
||||
setState(() {
|
||||
|
@ -929,6 +932,8 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
|||
final ScrollController _scrollController = ScrollController();
|
||||
String _filter = '';
|
||||
|
||||
final deezerAPI = DeezerAPI.instance;
|
||||
|
||||
List<Playlist> get _sorted {
|
||||
List<Playlist> playlists = List.from(_playlists!
|
||||
.where((p) => p.title!.toLowerCase().contains(_filter.toLowerCase())));
|
||||
|
|
|
@ -31,6 +31,7 @@ class LoginWidget extends StatefulWidget {
|
|||
class _LoginWidgetState extends State<LoginWidget> {
|
||||
late String _arl;
|
||||
String? _error;
|
||||
final deezerAPI = DeezerAPI.instance;
|
||||
|
||||
//Initialize deezer etc
|
||||
Future _init() async {
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/pipe_api.dart';
|
||||
import 'package:freezer/api/player/audio_handler.dart';
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:freezer/ui/error.dart';
|
||||
|
@ -38,7 +38,7 @@ class LyricsScreen extends StatelessWidget {
|
|||
}
|
||||
|
||||
class LyricsWidget extends StatefulWidget {
|
||||
const LyricsWidget({Key? key}) : super(key: key);
|
||||
const LyricsWidget({super.key});
|
||||
|
||||
@override
|
||||
State<LyricsWidget> createState() => _LyricsWidgetState();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/main.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
|
@ -15,6 +17,7 @@ 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;
|
||||
|
@ -267,21 +270,21 @@ class MenuSheet {
|
|||
MenuSheetOption(Text('Play next'.i18n),
|
||||
icon: const Icon(Icons.playlist_play), onTap: () async {
|
||||
//-1 = next
|
||||
await audioHandler.insertQueueItem(-1, await t.toMediaItem());
|
||||
await audioHandler.insertQueueItem(-1, t.toMediaItem());
|
||||
});
|
||||
|
||||
MenuSheetOption addToQueue(Track t) =>
|
||||
MenuSheetOption(Text('Add to queue'.i18n),
|
||||
icon: const Icon(Icons.playlist_add), onTap: () async {
|
||||
await audioHandler.addQueueItem(await t.toMediaItem());
|
||||
await audioHandler.addQueueItem(t.toMediaItem());
|
||||
});
|
||||
|
||||
MenuSheetOption addTrackFavorite(Track t) =>
|
||||
MenuSheetOption(Text('Add track to favorites'.i18n),
|
||||
icon: const Icon(Icons.favorite), onTap: () async {
|
||||
await deezerAPI.addFavoriteTrack(t.id);
|
||||
await DeezerAPI.instance.addFavoriteTrack(t.id);
|
||||
//Make track offline, if favorites are offline
|
||||
Playlist p = Playlist(id: deezerAPI.favoritesPlaylistId!);
|
||||
Playlist p = Playlist(id: DeezerAPI.instance.favoritesPlaylistId!);
|
||||
if (await downloadManager.checkOffline(playlist: p)) {
|
||||
downloadManager.addOfflinePlaylist(p);
|
||||
}
|
||||
|
@ -294,9 +297,12 @@ class MenuSheet {
|
|||
Text('Download'.i18n),
|
||||
icon: const Icon(Icons.file_download),
|
||||
onTap: () async {
|
||||
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();
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -311,7 +317,7 @@ class MenuSheet {
|
|||
return SelectPlaylistDialog(
|
||||
track: t,
|
||||
callback: (Playlist p) async {
|
||||
await deezerAPI.addToPlaylist(t.id, p.id);
|
||||
await DeezerAPI.instance.addToPlaylist(t.id, p.id);
|
||||
//Update the playlist if offline
|
||||
if (await downloadManager.checkOffline(playlist: p)) {
|
||||
downloadManager.addOfflinePlaylist(p);
|
||||
|
@ -327,7 +333,7 @@ class MenuSheet {
|
|||
Text('Remove from playlist'.i18n),
|
||||
icon: const Icon(Icons.delete),
|
||||
onTap: () async {
|
||||
await deezerAPI.removeFromPlaylist(t.id, p!.id);
|
||||
await DeezerAPI.instance.removeFromPlaylist(t.id, p!.id);
|
||||
ScaffoldMessenger.of(context)
|
||||
.snack('${'Track removed from'.i18n} ${p.title}');
|
||||
},
|
||||
|
@ -337,9 +343,9 @@ class MenuSheet {
|
|||
Text('Remove favorite'.i18n),
|
||||
icon: const Icon(Icons.delete),
|
||||
onTap: () async {
|
||||
await deezerAPI.removeFavorite(t.id);
|
||||
await DeezerAPI.instance.removeFavorite(t.id);
|
||||
//Check if favorites playlist is offline, update it
|
||||
Playlist p = Playlist(id: deezerAPI.favoritesPlaylistId!);
|
||||
Playlist p = Playlist(id: DeezerAPI.instance.favoritesPlaylistId!);
|
||||
if (await downloadManager.checkOffline(playlist: p)) {
|
||||
await downloadManager.addOfflinePlaylist(p);
|
||||
}
|
||||
|
@ -454,7 +460,7 @@ class MenuSheet {
|
|||
Text('Make offline'.i18n),
|
||||
icon: const Icon(Icons.offline_pin),
|
||||
onTap: () async {
|
||||
await deezerAPI.addFavoriteAlbum(a.id);
|
||||
await DeezerAPI.instance.addFavoriteAlbum(a.id);
|
||||
await downloadManager.addOfflineAlbum(a, private: true);
|
||||
|
||||
showDownloadStartedToast();
|
||||
|
@ -465,7 +471,7 @@ class MenuSheet {
|
|||
Text('Add to library'.i18n),
|
||||
icon: const Icon(Icons.library_music),
|
||||
onTap: () async {
|
||||
await deezerAPI.addFavoriteAlbum(a.id);
|
||||
await DeezerAPI.instance.addFavoriteAlbum(a.id);
|
||||
ScaffoldMessenger.of(context).snack('Added to library'.i18n);
|
||||
},
|
||||
);
|
||||
|
@ -475,7 +481,7 @@ class MenuSheet {
|
|||
Text('Remove album'.i18n),
|
||||
icon: const Icon(Icons.delete),
|
||||
onTap: () async {
|
||||
await deezerAPI.removeAlbum(a.id);
|
||||
await DeezerAPI.instance.removeAlbum(a.id);
|
||||
await downloadManager.removeOfflineAlbum(a.id);
|
||||
ScaffoldMessenger.of(context).snack('Album removed'.i18n);
|
||||
if (onRemove != null) onRemove();
|
||||
|
@ -508,7 +514,7 @@ class MenuSheet {
|
|||
Text('Remove from favorites'.i18n),
|
||||
icon: const Icon(Icons.delete),
|
||||
onTap: () async {
|
||||
await deezerAPI.removeArtist(a.id);
|
||||
await DeezerAPI.instance.removeArtist(a.id);
|
||||
ScaffoldMessenger.of(context)
|
||||
.snack('Artist removed from library'.i18n);
|
||||
if (onRemove != null) onRemove();
|
||||
|
@ -519,7 +525,7 @@ class MenuSheet {
|
|||
Text('Add to favorites'.i18n),
|
||||
icon: const Icon(Icons.favorite),
|
||||
onTap: () async {
|
||||
await deezerAPI.addFavoriteArtist(a.id);
|
||||
await DeezerAPI.instance.addFavoriteArtist(a.id);
|
||||
ScaffoldMessenger.of(context).snack('Added to library'.i18n);
|
||||
},
|
||||
);
|
||||
|
@ -541,7 +547,7 @@ class MenuSheet {
|
|||
addPlaylistOffline(playlist),
|
||||
downloadPlaylist(playlist),
|
||||
shareTile('playlist', playlist.id),
|
||||
if (playlist.user!.id == deezerAPI.userId)
|
||||
if (playlist.user!.id == DeezerAPI.instance.userId)
|
||||
editPlaylist(playlist, onUpdate: onUpdate),
|
||||
...options
|
||||
]);
|
||||
|
@ -556,12 +562,12 @@ class MenuSheet {
|
|||
Text('Remove from library'.i18n),
|
||||
icon: const Icon(Icons.delete),
|
||||
onTap: () async {
|
||||
if (p.user!.id!.trim() == deezerAPI.userId) {
|
||||
if (p.user!.id!.trim() == DeezerAPI.instance.userId) {
|
||||
//Delete playlist if own
|
||||
await deezerAPI.deletePlaylist(p.id);
|
||||
await DeezerAPI.instance.deletePlaylist(p.id);
|
||||
} else {
|
||||
//Just remove from library
|
||||
await deezerAPI.removePlaylist(p.id);
|
||||
await DeezerAPI.instance.removePlaylist(p.id);
|
||||
}
|
||||
downloadManager.removeOfflinePlaylist(p.id);
|
||||
if (onRemove != null) onRemove();
|
||||
|
@ -572,7 +578,7 @@ class MenuSheet {
|
|||
Text('Add playlist to library'.i18n),
|
||||
icon: const Icon(Icons.favorite),
|
||||
onTap: () async {
|
||||
await deezerAPI.addPlaylist(p.id);
|
||||
await DeezerAPI.instance.addPlaylist(p.id);
|
||||
ScaffoldMessenger.of(context).snack('Added playlist to library'.i18n);
|
||||
},
|
||||
);
|
||||
|
@ -582,7 +588,7 @@ class MenuSheet {
|
|||
icon: const Icon(Icons.offline_pin),
|
||||
onTap: () async {
|
||||
//Add to library
|
||||
await deezerAPI.addPlaylist(p.id);
|
||||
await DeezerAPI.instance.addPlaylist(p.id);
|
||||
downloadManager.addOfflinePlaylist(p, private: true);
|
||||
|
||||
showDownloadStartedToast();
|
||||
|
@ -825,7 +831,7 @@ class _SelectPlaylistDialogState extends State<SelectPlaylistDialog> {
|
|||
return AlertDialog(
|
||||
title: Text('Select playlist'.i18n),
|
||||
content: FutureBuilder(
|
||||
future: deezerAPI.getPlaylists(),
|
||||
future: DeezerAPI.instance.getPlaylists(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
const SizedBox(
|
||||
|
@ -957,7 +963,7 @@ class _CreatePlaylistDialogState extends State<CreatePlaylistDialog> {
|
|||
onPressed: () async {
|
||||
if (edit) {
|
||||
//Update
|
||||
await deezerAPI.updatePlaylist(widget.playlist!.id,
|
||||
await DeezerAPI.instance.updatePlaylist(widget.playlist!.id,
|
||||
_titleController!.value.text, _descController!.value.text,
|
||||
status: _playlistType);
|
||||
ScaffoldMessenger.of(context).snack('Playlist updated!'.i18n);
|
||||
|
@ -966,7 +972,7 @@ class _CreatePlaylistDialogState extends State<CreatePlaylistDialog> {
|
|||
if (widget.tracks != null) {
|
||||
tracks = widget.tracks!.map<String>((t) => t!.id).toList();
|
||||
}
|
||||
await deezerAPI.createPlaylist(_title,
|
||||
await DeezerAPI.instance.createPlaylist(_title,
|
||||
status: _playlistType,
|
||||
description: _description,
|
||||
trackIds: tracks);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
@ -15,12 +16,12 @@ class PlayerBar extends StatelessWidget {
|
|||
final Color? backgroundColor;
|
||||
final FocusNode? focusNode;
|
||||
const PlayerBar({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.onTap,
|
||||
this.shouldHaveHero = true,
|
||||
this.backgroundColor,
|
||||
this.focusNode,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final double iconSize = 28;
|
||||
|
||||
|
@ -193,12 +194,12 @@ class PlayPauseButton extends StatefulWidget {
|
|||
final Color? color;
|
||||
const PlayPauseButton(
|
||||
this.size, {
|
||||
Key? key,
|
||||
super.key,
|
||||
this.filled = false,
|
||||
this.material3 = true,
|
||||
this.color,
|
||||
this.iconColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<PlayPauseButton> createState() => _PlayPauseButtonState();
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:freezer/api/cache.dart';
|
|||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/player/audio_handler.dart';
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/main.dart';
|
||||
import 'package:freezer/page_routes/fade.dart';
|
||||
import 'package:freezer/settings.dart';
|
||||
|
@ -750,12 +751,12 @@ class _FavoriteButtonState extends State<FavoriteButton> {
|
|||
if (cache.checkTrackFavorite(Track.fromMediaItem(mediaItem))) {
|
||||
//Remove from library
|
||||
setState(() => cache.libraryTracks.remove(mediaItem.id));
|
||||
await deezerAPI.removeFavorite(mediaItem.id);
|
||||
await DeezerAPI.instance.removeFavorite(mediaItem.id);
|
||||
await cache.save();
|
||||
} else {
|
||||
//Add
|
||||
setState(() => cache.libraryTracks.add(mediaItem.id));
|
||||
await deezerAPI.addFavoriteTrack(mediaItem.id);
|
||||
await DeezerAPI.instance.addFavoriteTrack(mediaItem.id);
|
||||
await cache.save();
|
||||
}
|
||||
},
|
||||
|
@ -1266,8 +1267,8 @@ class BottomBarControls extends StatelessWidget {
|
|||
),
|
||||
iconSize: iconSize,
|
||||
onPressed: () async {
|
||||
unawaited(
|
||||
deezerAPI.dislikeTrack(audioHandler.mediaItem.value!.id));
|
||||
unawaited(DeezerAPI.instance
|
||||
.dislikeTrack(audioHandler.mediaItem.value!.id));
|
||||
if (playerHelper.queueIndex <
|
||||
audioHandler.queue.value.length - 1) {
|
||||
audioHandler.skipToNext();
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/player/audio_handler.dart';
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:freezer/ui/menu.dart';
|
||||
import 'package:freezer/ui/tiles.dart';
|
||||
|
|
|
@ -22,6 +22,7 @@ import '../api/definitions.dart';
|
|||
import 'error.dart';
|
||||
|
||||
FutureOr openScreenByURL(BuildContext context, String url) async {
|
||||
final deezerAPI = DeezerAPI.instance;
|
||||
DeezerLinkResponse? res = await deezerAPI.parseLink(Uri.parse(url));
|
||||
if (res == null) return;
|
||||
|
||||
|
@ -138,8 +139,8 @@ class _SearchScreenState extends State<SearchScreen> {
|
|||
final List<String>? suggestions;
|
||||
try {
|
||||
_searchCancelToken = CancelToken();
|
||||
suggestions = await deezerAPI.searchSuggestions(_controller.text,
|
||||
cancelToken: _searchCancelToken);
|
||||
suggestions = await DeezerAPI.instance
|
||||
.searchSuggestions(_controller.text, cancelToken: _searchCancelToken);
|
||||
} on DioException catch (e) {
|
||||
if (e.type != DioExceptionType.cancel) rethrow;
|
||||
return;
|
||||
|
@ -456,7 +457,7 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
if (widget.offline ?? false) {
|
||||
results = await downloadManager.search(widget.query);
|
||||
} else {
|
||||
results = await deezerAPI.search(widget.query);
|
||||
results = await DeezerAPI.instance.search(widget.query);
|
||||
}
|
||||
setState(() {
|
||||
_results = results;
|
||||
|
@ -896,8 +897,10 @@ class _SearchResultsScreenState extends State<SearchResultsScreen> {
|
|||
onTap: () async {
|
||||
//Load entire show, then play
|
||||
List<ShowEpisode> episodes =
|
||||
(await deezerAPI.allShowEpisodes(
|
||||
episode.show!.id))!;
|
||||
(await DeezerAPI
|
||||
.instance
|
||||
.allShowEpisodes(
|
||||
episode.show!.id))!;
|
||||
await playerHelper.playShowEpisode(
|
||||
episode.show!, episodes,
|
||||
index: episodes.indexWhere(
|
||||
|
@ -1051,7 +1054,7 @@ class EpisodeListScreen extends StatelessWidget {
|
|||
onTap: () async {
|
||||
//Load entire show, then play
|
||||
List<ShowEpisode> episodes =
|
||||
(await deezerAPI.allShowEpisodes(e.show!.id))!;
|
||||
(await DeezerAPI.instance.allShowEpisodes(e.show!.id))!;
|
||||
await playerHelper.playShowEpisode(e.show!, episodes,
|
||||
index: episodes.indexWhere((ep) => e.id == ep.id));
|
||||
},
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/api/player/systray.dart';
|
||||
import 'package:freezer/icons.dart';
|
||||
import 'package:freezer/ui/login_on_other_device.dart';
|
||||
|
@ -876,10 +877,13 @@ class _DeezerSettingsState extends State<DeezerSettings> {
|
|||
title: Text(ContentLanguage.all[i].name),
|
||||
subtitle: Text(ContentLanguage.all[i].code),
|
||||
onTap: () async {
|
||||
setState(() => settings.deezerLanguage =
|
||||
ContentLanguage.all[i].code);
|
||||
settings.deezerLanguage =
|
||||
ContentLanguage.all[i].code;
|
||||
setState(() {});
|
||||
await settings.save();
|
||||
deezerAPI.updateHeaders();
|
||||
DeezerAPI.instance.deezerLanguage =
|
||||
settings.deezerLanguage;
|
||||
DeezerAPI.instance.updateHeaders();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
)),
|
||||
|
@ -898,9 +902,10 @@ class _DeezerSettingsState extends State<DeezerSettings> {
|
|||
titlePadding: const EdgeInsets.all(8.0),
|
||||
isSearchable: true,
|
||||
onValuePicked: (Country country) {
|
||||
setState(
|
||||
() => settings.deezerCountry = country.isoCode);
|
||||
deezerAPI.updateHeaders();
|
||||
DeezerAPI.instance.deezerCountry =
|
||||
settings.deezerCountry = country.isoCode;
|
||||
setState(() {});
|
||||
DeezerAPI.instance.updateHeaders();
|
||||
settings.save();
|
||||
},
|
||||
));
|
||||
|
@ -1390,7 +1395,7 @@ class _GeneralSettingsState extends State<GeneralSettings> {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
deezerAPI.authorize().then((v) {
|
||||
DeezerAPI.instance.authorize().then((v) {
|
||||
if (v) {
|
||||
setState(() => settings.offlineMode = false);
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
import 'package:freezer/api/player/audio_handler.dart';
|
||||
import 'package:freezer/api/player/player_helper.dart';
|
||||
import 'package:freezer/icons.dart';
|
||||
import 'package:freezer/main.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
|
@ -246,7 +247,7 @@ class PlaylistTile extends StatelessWidget {
|
|||
if (playlist!.user == null ||
|
||||
playlist!.user!.name == null ||
|
||||
playlist!.user!.name == '' ||
|
||||
playlist!.user!.id == deezerAPI.userId) {
|
||||
playlist!.user!.id == DeezerAPI.instance.userId) {
|
||||
if (playlist!.trackCount == null) return '';
|
||||
return '${playlist!.trackCount} ${'Tracks'.i18n}';
|
||||
}
|
||||
|
@ -341,8 +342,9 @@ class PlaylistCardTile extends StatelessWidget {
|
|||
left: 8.0,
|
||||
child: PlayItemButton(
|
||||
onTap: () async {
|
||||
final Playlist fullPlaylist =
|
||||
await deezerAPI.fullPlaylist(playlist!.id);
|
||||
final Playlist fullPlaylist = await DeezerAPI
|
||||
.instance
|
||||
.fullPlaylist(playlist!.id);
|
||||
await playerHelper.playFromPlaylist(fullPlaylist);
|
||||
},
|
||||
))
|
||||
|
@ -639,7 +641,8 @@ class AlbumCard extends StatelessWidget {
|
|||
left: 8.0,
|
||||
child: PlayItemButton(
|
||||
onTap: () async {
|
||||
final fullAlbum = await deezerAPI.album(album.id);
|
||||
final fullAlbum =
|
||||
await DeezerAPI.instance.album(album.id);
|
||||
await playerHelper.playFromAlbum(fullAlbum);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -27,4 +27,21 @@ class Utils {
|
|||
}
|
||||
return bi;
|
||||
}
|
||||
|
||||
/// FOR ISAR, FROM ISAR DOCUMENTATION
|
||||
/// FNV-1a 64bit hash algorithm optimized for Dart Strings
|
||||
static int fastHash(String string) {
|
||||
var hash = 0xcbf29ce484222325;
|
||||
|
||||
var i = 0;
|
||||
while (i < string.length) {
|
||||
final codeUnit = string.codeUnitAt(i++);
|
||||
hash ^= codeUnit >> 8;
|
||||
hash *= 0x100000001b3;
|
||||
hash ^= codeUnit & 0xFF;
|
||||
hash *= 0x100000001b3;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
|
26
pubspec.lock
26
pubspec.lock
|
@ -567,6 +567,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.2.4"
|
||||
freezed:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: freezed
|
||||
sha256: "57247f692f35f068cae297549a46a9a097100685c6780fe67177503eea5ed4e5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.7"
|
||||
freezed_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -575,6 +591,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
get_it:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: get_it
|
||||
sha256: e6017ce7fdeaf218dc51a100344d8cb70134b80e28b760f8bb23c242437bafd7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.6.7"
|
||||
gettext_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -789,7 +813,7 @@ packages:
|
|||
path: "../just_audio_media_kit"
|
||||
relative: true
|
||||
source: path
|
||||
version: "2.0.1"
|
||||
version: "2.0.0"
|
||||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -107,6 +107,8 @@ dependencies:
|
|||
tray_manager: ^0.2.1
|
||||
window_manager:
|
||||
^0.3.8
|
||||
get_it: ^7.6.7
|
||||
freezed_annotation: ^2.4.1
|
||||
#deezcryptor:
|
||||
#path: deezcryptor/
|
||||
|
||||
|
@ -119,6 +121,7 @@ dev_dependencies:
|
|||
hive_generator: ^2.0.0
|
||||
flutter_lints: ^3.0.1
|
||||
isar_generator: ^3.1.0+1
|
||||
freezed: ^2.4.7
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
Loading…
Reference in New Issue