0.6.8 - bug fixes
This commit is contained in:
parent
c3a26b0e3b
commit
ff239aaf86
|
@ -247,17 +247,28 @@ public class Deezer {
|
||||||
return original + ".mp3";
|
return original + ".mp3";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String generateUserUploadedMP3Filename(String original, JSONObject privateJson) throws Exception {
|
// public static String generateUserUploadedMP3Filename(String original, JSONObject privateJson) throws Exception {
|
||||||
//Remove unavailable tags
|
// //Remove unavailable tags
|
||||||
String[] ignored = {"%feats%", "%trackNumber%", "%0trackNumber%", "%year%", "%date%"};
|
// String[] ignored = {"%feats%", "%trackNumber%", "%0trackNumber%", "%year%", "%date%"};
|
||||||
|
// for (String i : ignored) {
|
||||||
|
// original = original.replaceAll(i, "");
|
||||||
|
// }
|
||||||
|
// //Basic tags
|
||||||
|
// original = original.replaceAll("%title%", privateJson.getString("SNG_TITLE"));
|
||||||
|
// original = original.replaceAll("%album%", privateJson.getString("ALB_TITLE"));
|
||||||
|
// original = original.replaceAll("%artist%", privateJson.getString("ART_NAME"));
|
||||||
|
// original = original.replaceAll("%artists%", privateJson.getString("ART_NAME"));
|
||||||
|
// return original;
|
||||||
|
// }
|
||||||
|
|
||||||
|
//Deezer patched something so getting metadata of user uploaded MP3s is not working anymore
|
||||||
|
public static String generateUserUploadedMP3Filename(String original, String title) throws Exception {
|
||||||
|
String[] ignored = {"%feats%", "%trackNumber%", "%0trackNumber%", "%year%", "%date%", "%album%", "%artist%", "%artists%"};
|
||||||
for (String i : ignored) {
|
for (String i : ignored) {
|
||||||
original = original.replaceAll(i, "");
|
original = original.replaceAll(i, "");
|
||||||
}
|
}
|
||||||
//Basic tags
|
|
||||||
original = original.replaceAll("%title%", privateJson.getString("SNG_TITLE"));
|
original = original.replace("%title%", sanitize(title));
|
||||||
original = original.replaceAll("%album%", privateJson.getString("ALB_TITLE"));
|
|
||||||
original = original.replaceAll("%artist%", privateJson.getString("ART_NAME"));
|
|
||||||
original = original.replaceAll("%artists%", privateJson.getString("ART_NAME"));
|
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,10 +96,8 @@ public class DownloadService extends Service {
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
//Cancel notifications
|
//Cancel notifications
|
||||||
notificationManager.cancelAll();
|
notificationManager.cancelAll();
|
||||||
|
|
||||||
//Logger
|
//Logger
|
||||||
logger.close();
|
logger.close();
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,8 +114,10 @@ public class DownloadService extends Service {
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
//Get messenger
|
//Get messenger
|
||||||
if (intent != null)
|
if (intent != null) {
|
||||||
activityMessenger = intent.getParcelableExtra("activityMessenger");
|
activityMessenger = intent.getParcelableExtra("activityMessenger");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//return super.onStartCommand(intent, flags, startId);
|
//return super.onStartCommand(intent, flags, startId);
|
||||||
//Prevent battery savers I guess
|
//Prevent battery savers I guess
|
||||||
|
@ -295,18 +295,17 @@ public class DownloadService extends Service {
|
||||||
while (deezer.authorizing)
|
while (deezer.authorizing)
|
||||||
try {Thread.sleep(50);} catch (Exception ignored) {}
|
try {Thread.sleep(50);} catch (Exception ignored) {}
|
||||||
|
|
||||||
//Fetch metadata
|
//Don't fetch meta if user uploaded mp3
|
||||||
|
if (!download.isUserUploaded()) {
|
||||||
try {
|
try {
|
||||||
JSONObject privateRaw = deezer.callGWAPI("deezer.pageTrack", "{\"sng_id\": \"" + download.trackId + "\"}");
|
JSONObject privateRaw = deezer.callGWAPI("deezer.pageTrack", "{\"sng_id\": \"" + download.trackId + "\"}");
|
||||||
privateJson = privateRaw.getJSONObject("results").getJSONObject("DATA");
|
privateJson = privateRaw.getJSONObject("results").getJSONObject("DATA");
|
||||||
if (privateRaw.getJSONObject("results").has("LYRICS")) {
|
if (privateRaw.getJSONObject("results").has("LYRICS")) {
|
||||||
lyricsData = privateRaw.getJSONObject("results").getJSONObject("LYRICS");
|
lyricsData = privateRaw.getJSONObject("results").getJSONObject("LYRICS");
|
||||||
}
|
}
|
||||||
//Don't fetch meta if user uploaded mp3
|
|
||||||
if (!download.isUserUploaded()) {
|
|
||||||
trackJson = Deezer.callPublicAPI("track", download.trackId);
|
trackJson = Deezer.callPublicAPI("track", download.trackId);
|
||||||
albumJson = Deezer.callPublicAPI("album", Integer.toString(trackJson.getJSONObject("album").getInt("id")));
|
albumJson = Deezer.callPublicAPI("album", Integer.toString(trackJson.getJSONObject("album").getInt("id")));
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Unable to fetch track and album metadata! " + e.toString(), download);
|
logger.error("Unable to fetch track and album metadata! " + e.toString(), download);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -314,6 +313,7 @@ public class DownloadService extends Service {
|
||||||
exit();
|
exit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Fallback
|
//Fallback
|
||||||
Deezer.QualityInfo qualityInfo = new Deezer.QualityInfo(this.download.quality, this.download.trackId, this.download.md5origin, this.download.mediaVersion, logger);
|
Deezer.QualityInfo qualityInfo = new Deezer.QualityInfo(this.download.quality, this.download.trackId, this.download.md5origin, this.download.mediaVersion, logger);
|
||||||
|
@ -339,7 +339,7 @@ public class DownloadService extends Service {
|
||||||
//Check file
|
//Check file
|
||||||
try {
|
try {
|
||||||
if (download.isUserUploaded()) {
|
if (download.isUserUploaded()) {
|
||||||
outFile = new File(Deezer.generateUserUploadedMP3Filename(download.path, privateJson));
|
outFile = new File(Deezer.generateUserUploadedMP3Filename(download.path, download.title));
|
||||||
} else {
|
} else {
|
||||||
outFile = new File(Deezer.generateFilename(download.path, trackJson, albumJson, qualityInfo.quality));
|
outFile = new File(Deezer.generateFilename(download.path, trackJson, albumJson, qualityInfo.quality));
|
||||||
}
|
}
|
||||||
|
@ -700,6 +700,8 @@ public class DownloadService extends Service {
|
||||||
//Start/Resume
|
//Start/Resume
|
||||||
case SERVICE_START_DOWNLOAD:
|
case SERVICE_START_DOWNLOAD:
|
||||||
running = true;
|
running = true;
|
||||||
|
if (downloads.size() == 0)
|
||||||
|
loadDownloads();
|
||||||
updateQueue();
|
updateQueue();
|
||||||
updateState();
|
updateState();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -144,8 +144,9 @@ public class MainActivity extends FlutterActivity {
|
||||||
}
|
}
|
||||||
//Start/Resume downloading
|
//Start/Resume downloading
|
||||||
if (call.method.equals("start")) {
|
if (call.method.equals("start")) {
|
||||||
|
//Connected
|
||||||
sendMessage(DownloadService.SERVICE_START_DOWNLOAD, null);
|
sendMessage(DownloadService.SERVICE_START_DOWNLOAD, null);
|
||||||
result.success(null);
|
result.success(serviceBound);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//Stop downloading
|
//Stop downloading
|
||||||
|
@ -239,17 +240,24 @@ public class MainActivity extends FlutterActivity {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Start/Bind/Reconnect to download service
|
||||||
|
private void connectService() {
|
||||||
|
if (serviceBound)
|
||||||
|
return;
|
||||||
|
//Create messenger
|
||||||
|
activityMessenger = new Messenger(new IncomingHandler(this));
|
||||||
|
//Start
|
||||||
|
Intent intent = new Intent(this, DownloadService.class);
|
||||||
|
intent.putExtra("activityMessenger", activityMessenger);
|
||||||
|
startService(intent);
|
||||||
|
bindService(intent, connection, BIND_AUTO_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
||||||
//Bind downloader service
|
connectService();
|
||||||
activityMessenger = new Messenger(new IncomingHandler(this));
|
|
||||||
Intent intent = new Intent(this, DownloadService.class);
|
|
||||||
intent.putExtra("activityMessenger", activityMessenger);
|
|
||||||
startService(intent);
|
|
||||||
bindService(intent, connection, 0);
|
|
||||||
//Get DB
|
//Get DB
|
||||||
DownloadsDatabase dbHelper = new DownloadsDatabase(getApplicationContext());
|
DownloadsDatabase dbHelper = new DownloadsDatabase(getApplicationContext());
|
||||||
db = dbHelper.getWritableDatabase();
|
db = dbHelper.getWritableDatabase();
|
||||||
|
@ -274,17 +282,18 @@ public class MainActivity extends FlutterActivity {
|
||||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||||
Log.e(this.getLocalClassName(), e.getMessage());
|
Log.e(this.getLocalClassName(), e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
//Try reconnect
|
||||||
|
connectService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
//Unbind service on exit
|
|
||||||
if (serviceBound) {
|
|
||||||
unbindService(connection);
|
|
||||||
serviceBound = false;
|
|
||||||
}
|
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,6 +303,12 @@ public class MainActivity extends FlutterActivity {
|
||||||
//Stop server
|
//Stop server
|
||||||
if (streamServer != null)
|
if (streamServer != null)
|
||||||
streamServer.stop();
|
streamServer.stop();
|
||||||
|
|
||||||
|
//Unbind service on exit
|
||||||
|
if (serviceBound) {
|
||||||
|
unbindService(connection);
|
||||||
|
serviceBound = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Connection to download service
|
//Connection to download service
|
||||||
|
@ -302,12 +317,14 @@ public class MainActivity extends FlutterActivity {
|
||||||
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
||||||
serviceMessenger = new Messenger(iBinder);
|
serviceMessenger = new Messenger(iBinder);
|
||||||
serviceBound = true;
|
serviceBound = true;
|
||||||
|
Log.d("DD", "Service Bound!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceDisconnected(ComponentName componentName) {
|
public void onServiceDisconnected(ComponentName componentName) {
|
||||||
serviceMessenger = null;
|
serviceMessenger = null;
|
||||||
serviceBound = false;
|
serviceBound = false;
|
||||||
|
Log.d("DD", "Service UnBound!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ class DownloadManager {
|
||||||
|
|
||||||
//Start/Resume downloads
|
//Start/Resume downloads
|
||||||
Future start() async {
|
Future start() async {
|
||||||
|
//Returns whether service is bound or not, the delay is really shitty/hacky way, until i find a real solution
|
||||||
await updateServiceSettings();
|
await updateServiceSettings();
|
||||||
await platform.invokeMethod('start');
|
await platform.invokeMethod('start');
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,6 +319,8 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
|
|
||||||
//Queue
|
//Queue
|
||||||
List<MediaItem> _queue = <MediaItem>[];
|
List<MediaItem> _queue = <MediaItem>[];
|
||||||
|
List<MediaItem> _originalQueue;
|
||||||
|
bool _shuffle = false;
|
||||||
int _queueIndex = 0;
|
int _queueIndex = 0;
|
||||||
ConcatenatingAudioSource _audioSource;
|
ConcatenatingAudioSource _audioSource;
|
||||||
|
|
||||||
|
@ -471,6 +473,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
//Update buffering state
|
//Update buffering state
|
||||||
_skipState = AudioProcessingState.skippingToPrevious;
|
_skipState = AudioProcessingState.skippingToPrevious;
|
||||||
|
|
||||||
|
|
||||||
//Normal skip to previous
|
//Normal skip to previous
|
||||||
_queueIndex--;
|
_queueIndex--;
|
||||||
await _player.seekToPrevious();
|
await _player.seekToPrevious();
|
||||||
|
@ -542,7 +545,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
if (_skipState != null) return _skipState;
|
if (_skipState != null) return _skipState;
|
||||||
//SRC: audio_service example
|
//SRC: audio_service example
|
||||||
switch (_player.processingState) {
|
switch (_player.processingState) {
|
||||||
case ProcessingState.none:
|
case ProcessingState.idle:
|
||||||
return AudioProcessingState.stopped;
|
return AudioProcessingState.stopped;
|
||||||
case ProcessingState.loading:
|
case ProcessingState.loading:
|
||||||
return AudioProcessingState.connecting;
|
return AudioProcessingState.connecting;
|
||||||
|
@ -586,8 +589,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
_audioSource = ConcatenatingAudioSource(children: sources);
|
_audioSource = ConcatenatingAudioSource(children: sources);
|
||||||
//Load in just_audio
|
//Load in just_audio
|
||||||
try {
|
try {
|
||||||
await _player.load(_audioSource, initialPosition: Duration.zero, initialIndex: qi);
|
await _player.setAudioSource(_audioSource, initialIndex: qi, initialPosition: Duration.zero);
|
||||||
// await _player.seek(Duration.zero, index: qi);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//Error loading tracks
|
//Error loading tracks
|
||||||
}
|
}
|
||||||
|
@ -599,7 +601,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
String url = await _getTrackUrl(mi);
|
String url = await _getTrackUrl(mi);
|
||||||
if (url == null) return null;
|
if (url == null) return null;
|
||||||
if (url.startsWith('http')) return ProgressiveAudioSource(Uri.parse(url));
|
if (url.startsWith('http')) return ProgressiveAudioSource(Uri.parse(url));
|
||||||
return AudioSource.uri(Uri.parse(url));
|
return AudioSource.uri(Uri.parse(url), tag: mi.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _getTrackUrl(MediaItem mediaItem, {int quality}) async {
|
Future _getTrackUrl(MediaItem mediaItem, {int quality}) async {
|
||||||
|
@ -655,10 +657,26 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
await this._loadQueueFile();
|
await this._loadQueueFile();
|
||||||
//Shuffle
|
//Shuffle
|
||||||
if (name == 'shuffle') {
|
if (name == 'shuffle') {
|
||||||
|
String originalId = mediaItem.id;
|
||||||
|
if (!_shuffle) {
|
||||||
|
_shuffle = true;
|
||||||
|
_originalQueue = List.from(_queue);
|
||||||
_queue.shuffle();
|
_queue.shuffle();
|
||||||
AudioServiceBackground.setQueue(_queue);
|
|
||||||
|
} else {
|
||||||
|
_shuffle = false;
|
||||||
|
_queue = _originalQueue;
|
||||||
|
_originalQueue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Broken
|
||||||
|
// _queueIndex = _queue.indexWhere((mi) => mi.id == originalId);
|
||||||
_queueIndex = 0;
|
_queueIndex = 0;
|
||||||
|
AudioServiceBackground.setQueue(_queue);
|
||||||
|
AudioServiceBackground.setMediaItem(mediaItem);
|
||||||
|
await _player.stop();
|
||||||
await _loadQueue();
|
await _loadQueue();
|
||||||
|
await _player.play();
|
||||||
}
|
}
|
||||||
//Android auto callback
|
//Android auto callback
|
||||||
if (name == 'screenAndroidAuto' && _androidAutoCallback != null) {
|
if (name == 'screenAndroidAuto' && _androidAutoCallback != null) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -307,6 +307,11 @@ const language_en_us = {
|
||||||
"Episodes": "Episodes",
|
"Episodes": "Episodes",
|
||||||
"Show all episodes": "Show all episodes",
|
"Show all episodes": "Show all episodes",
|
||||||
"Album cover resolution": "Album cover resolution",
|
"Album cover resolution": "Album cover resolution",
|
||||||
"WARNING: Resolutions above 1200 aren't officially supported": "WARNING: Resolutions above 1200 aren't officially supported"
|
"WARNING: Resolutions above 1200 aren't officially supported": "WARNING: Resolutions above 1200 aren't officially supported",
|
||||||
|
|
||||||
|
//0.6.8:
|
||||||
|
"Album removed from library!": "Album removed from library!",
|
||||||
|
"Remove offline": "Remove offline",
|
||||||
|
"Playlist removed from library!": "Playlist removed from library!"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fluttericon/font_awesome5_icons.dart';
|
import 'package:fluttericon/font_awesome5_icons.dart';
|
||||||
|
@ -18,18 +19,35 @@ import 'cached_image.dart';
|
||||||
import 'tiles.dart';
|
import 'tiles.dart';
|
||||||
import 'menu.dart';
|
import 'menu.dart';
|
||||||
|
|
||||||
class AlbumDetails extends StatelessWidget {
|
class AlbumDetails extends StatefulWidget {
|
||||||
|
|
||||||
Album album;
|
Album album;
|
||||||
|
AlbumDetails(this.album, {Key key}): super(key: key);
|
||||||
|
|
||||||
AlbumDetails(this.album);
|
@override
|
||||||
|
_AlbumDetailsState createState() => _AlbumDetailsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AlbumDetailsState extends State<AlbumDetails> {
|
||||||
|
|
||||||
|
Album album;
|
||||||
|
bool _loading = true;
|
||||||
|
bool _error = false;
|
||||||
|
|
||||||
Future _loadAlbum() async {
|
Future _loadAlbum() async {
|
||||||
//Get album from API, if doesn't have tracks
|
//Get album from API, if doesn't have tracks
|
||||||
if (this.album.tracks == null || this.album.tracks.length == 0) {
|
if (this.album.tracks == null || this.album.tracks.length == 0) {
|
||||||
this.album = await deezerAPI.album(album.id);
|
try {
|
||||||
|
Album a = await deezerAPI.album(album.id);
|
||||||
|
//Preserve library
|
||||||
|
a.library = album.library;
|
||||||
|
setState(() => album = a);
|
||||||
|
} catch (e) {
|
||||||
|
setState(() => _error = true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setState(() => _loading = false);
|
||||||
|
}
|
||||||
|
|
||||||
//Get count of CDs in album
|
//Get count of CDs in album
|
||||||
int get cdCount {
|
int get cdCount {
|
||||||
|
@ -40,19 +58,18 @@ class AlbumDetails extends StatelessWidget {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
this.album = widget.album;
|
||||||
|
_loadAlbum();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: FutureBuilder(
|
body: _error ? ErrorScreen() : _loading ? Center(child: CircularProgressIndicator()) :
|
||||||
future: _loadAlbum(),
|
ListView(
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
|
||||||
|
|
||||||
//Wait for data
|
|
||||||
if (snapshot.connectionState != ConnectionState.done) return Center(child: CircularProgressIndicator(),);
|
|
||||||
//On error
|
|
||||||
if (snapshot.hasError) return ErrorScreen();
|
|
||||||
|
|
||||||
return ListView(
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
//Album art, title, artists
|
//Album art, title, artists
|
||||||
Container(
|
Container(
|
||||||
|
@ -151,18 +168,31 @@ class AlbumDetails extends StatelessWidget {
|
||||||
FlatButton(
|
FlatButton(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(Icons.favorite, size: 32),
|
Icon((album.library??false)? Icons.favorite : Icons.favorite_border, size: 32),
|
||||||
Container(width: 4,),
|
Container(width: 4,),
|
||||||
Text('Library'.i18n)
|
Text('Library'.i18n)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
//Add to library
|
||||||
|
if (!album.library) {
|
||||||
await deezerAPI.addFavoriteAlbum(album.id);
|
await deezerAPI.addFavoriteAlbum(album.id);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: 'Added to library'.i18n,
|
msg: 'Added to library'.i18n,
|
||||||
toastLength: Toast.LENGTH_SHORT,
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
gravity: ToastGravity.BOTTOM
|
gravity: ToastGravity.BOTTOM
|
||||||
);
|
);
|
||||||
|
setState(() => album.library = true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Remove
|
||||||
|
await deezerAPI.removeAlbum(album.id);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Album removed from library!'.i18n,
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM
|
||||||
|
);
|
||||||
|
setState(() => album.library = false);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MakeAlbumOffline(album: album),
|
MakeAlbumOffline(album: album),
|
||||||
|
@ -211,8 +241,6 @@ class AlbumDetails extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -812,7 +840,10 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: ListView(
|
body: DraggableScrollbar.rrect(
|
||||||
|
controller: _scrollController,
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
child: ListView(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(height: 4.0,),
|
Container(height: 4.0,),
|
||||||
|
@ -908,15 +939,31 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
MakePlaylistOffline(playlist),
|
MakePlaylistOffline(playlist),
|
||||||
|
|
||||||
|
if (playlist.user.name != deezerAPI.userName)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.favorite, size: 32),
|
icon: Icon(playlist.library ? Icons.favorite : Icons.favorite_outline, size: 32),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
//Add to library
|
||||||
|
if (!playlist.library) {
|
||||||
await deezerAPI.addPlaylist(playlist.id);
|
await deezerAPI.addPlaylist(playlist.id);
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: 'Added to library'.i18n,
|
msg: 'Added to library'.i18n,
|
||||||
toastLength: Toast.LENGTH_SHORT,
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
gravity: ToastGravity.BOTTOM
|
gravity: ToastGravity.BOTTOM
|
||||||
);
|
);
|
||||||
|
setState(() => playlist.library = true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Remove
|
||||||
|
await deezerAPI.removePlaylist(playlist.id);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Playlist removed from library!'.i18n,
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM
|
||||||
|
);
|
||||||
|
setState(() => playlist.library = false);
|
||||||
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -1006,6 +1053,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
||||||
if (_error)
|
if (_error)
|
||||||
ErrorScreen()
|
ErrorScreen()
|
||||||
],
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:freezer/main.dart';
|
||||||
import 'package:wakelock/wakelock.dart';
|
import 'package:wakelock/wakelock.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
@ -12,18 +13,19 @@ import 'package:freezer/api/player.dart';
|
||||||
import 'package:freezer/ui/details_screens.dart';
|
import 'package:freezer/ui/details_screens.dart';
|
||||||
import 'package:freezer/ui/error.dart';
|
import 'package:freezer/ui/error.dart';
|
||||||
import 'package:freezer/translations.i18n.dart';
|
import 'package:freezer/translations.i18n.dart';
|
||||||
|
import 'package:freezer/api/definitions.dart';
|
||||||
|
import 'package:freezer/ui/cached_image.dart';
|
||||||
import 'package:numberpicker/numberpicker.dart';
|
import 'package:numberpicker/numberpicker.dart';
|
||||||
import 'package:share/share.dart';
|
import 'package:share/share.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../api/definitions.dart';
|
|
||||||
import 'cached_image.dart';
|
|
||||||
|
|
||||||
class MenuSheet {
|
class MenuSheet {
|
||||||
|
|
||||||
BuildContext context;
|
BuildContext context;
|
||||||
|
Function navigateCallback;
|
||||||
|
|
||||||
MenuSheet(this.context);
|
MenuSheet(this.context, {this.navigateCallback});
|
||||||
|
|
||||||
//===================
|
//===================
|
||||||
// DEFAULT
|
// DEFAULT
|
||||||
|
@ -273,9 +275,13 @@ class MenuSheet {
|
||||||
leading: Icon(Icons.recent_actors),
|
leading: Icon(Icons.recent_actors),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_close();
|
_close();
|
||||||
Navigator.of(context).push(
|
navigatorKey.currentState.push(
|
||||||
MaterialPageRoute(builder: (context) => ArtistDetails(a))
|
MaterialPageRoute(builder: (context) => ArtistDetails(a))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.navigateCallback != null) {
|
||||||
|
this.navigateCallback();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -288,9 +294,13 @@ class MenuSheet {
|
||||||
leading: Icon(Icons.album),
|
leading: Icon(Icons.album),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_close();
|
_close();
|
||||||
Navigator.of(context).push(
|
navigatorKey.currentState.push(
|
||||||
MaterialPageRoute(builder: (context) => AlbumDetails(a))
|
MaterialPageRoute(builder: (context) => AlbumDetails(a))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.navigateCallback != null) {
|
||||||
|
this.navigateCallback();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -303,14 +313,24 @@ class MenuSheet {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget offlineTrack(Track track) => ListTile(
|
Widget offlineTrack(Track track) => FutureBuilder(
|
||||||
title: Text('Offline'.i18n),
|
future: downloadManager.checkOffline(track: track),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
bool isOffline = snapshot.data??(track.offline??false);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(isOffline ? 'Remove offline'.i18n : 'Offline'.i18n),
|
||||||
leading: Icon(Icons.offline_pin),
|
leading: Icon(Icons.offline_pin),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
if (isOffline) {
|
||||||
|
await downloadManager.removeOfflineTracks([track]);
|
||||||
|
} else {
|
||||||
await downloadManager.addOfflineTrack(track, private: true, context: context);
|
await downloadManager.addOfflineTrack(track, private: true, context: context);
|
||||||
|
}
|
||||||
_close();
|
_close();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
//===================
|
//===================
|
||||||
// ALBUM
|
// ALBUM
|
||||||
|
|
|
@ -51,16 +51,16 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
||||||
|
|
||||||
//Update notification
|
//Update notification
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
statusBarColor: palette.dominantColor.color.withOpacity(0.5)
|
statusBarColor: palette.dominantColor.color.withOpacity(0.7)
|
||||||
));
|
));
|
||||||
|
|
||||||
setState(() => _bgGradient = LinearGradient(
|
setState(() => _bgGradient = LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
colors: [palette.dominantColor.color.withOpacity(0.5), Color.fromARGB(0, 0, 0, 0)],
|
colors: [palette.dominantColor.color.withOpacity(0.7), Color.fromARGB(0, 0, 0, 0)],
|
||||||
stops: [
|
stops: [
|
||||||
0.0,
|
0.0,
|
||||||
0.4
|
0.6
|
||||||
]
|
]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -408,7 +408,9 @@ class PlayerMenuButton extends StatelessWidget {
|
||||||
icon: Icon(Icons.more_vert, size: ScreenUtil().setWidth(46)),
|
icon: Icon(Icons.more_vert, size: ScreenUtil().setWidth(46)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
|
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
|
||||||
MenuSheet m = MenuSheet(context);
|
MenuSheet m = MenuSheet(context, navigateCallback: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
});
|
||||||
if (AudioService.currentMediaItem.extras['show'] == null)
|
if (AudioService.currentMediaItem.extras['show'] == null)
|
||||||
m.defaultTrackMenu(t, options: [m.sleepTimer(), m.wakelock()]);
|
m.defaultTrackMenu(t, options: [m.sleepTimer(), m.wakelock()]);
|
||||||
else
|
else
|
||||||
|
|
|
@ -1362,7 +1362,7 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
||||||
subtitle: Text('Official Discord server'.i18n),
|
subtitle: Text('Official Discord server'.i18n),
|
||||||
leading: Icon(FontAwesome5.discord, color: Color(0xff7289da), size: 36.0),
|
leading: Icon(FontAwesome5.discord, color: Color(0xff7289da), size: 36.0),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launch('https://discord.gg/7ap654Tp3z');
|
launch('https://discord.gg/qwJpa3r4dQ');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
@ -1373,6 +1373,14 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
||||||
launch('https://git.rip/freezer/');
|
launch('https://git.rip/freezer/');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Donate'),
|
||||||
|
subtitle: Text('You should rather support your favorite artists, instead of this app!'),
|
||||||
|
leading: Icon(FontAwesome5.paypal, color: Colors.blue, size: 36.0),
|
||||||
|
onTap: () {
|
||||||
|
launch('https://paypal.me/exttex');
|
||||||
|
},
|
||||||
|
),
|
||||||
FreezerDivider(),
|
FreezerDivider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('exttex'),
|
title: Text('exttex'),
|
||||||
|
|
|
@ -517,21 +517,21 @@ packages:
|
||||||
name: just_audio
|
name: just_audio
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.6"
|
version: "0.6.1"
|
||||||
just_audio_platform_interface:
|
just_audio_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: just_audio_platform_interface
|
name: just_audio_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "2.0.0"
|
||||||
just_audio_web:
|
just_audio_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: just_audio_web
|
name: just_audio_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1"
|
version: "0.2.0"
|
||||||
language_pickers:
|
language_pickers:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 0.6.7+1
|
version: 0.6.8+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.8.0 <3.0.0"
|
sdk: ">=2.8.0 <3.0.0"
|
||||||
|
@ -79,7 +79,7 @@ dependencies:
|
||||||
audio_session: ^0.0.9
|
audio_session: ^0.0.9
|
||||||
audio_service:
|
audio_service:
|
||||||
path: ./audio_service
|
path: ./audio_service
|
||||||
just_audio: ^0.5.6
|
just_audio: 0.6.1
|
||||||
# path: ./just_audio
|
# path: ./just_audio
|
||||||
|
|
||||||
# cupertino_icons: ^0.1.3
|
# cupertino_icons: ^0.1.3
|
||||||
|
|
Loading…
Reference in New Issue