0.5.3 - Download fixes, shuffle fix, sorting in library
This commit is contained in:
parent
952cf0f508
commit
2f471268c6
|
@ -312,6 +312,20 @@ public class Deezer {
|
||||||
return original + ".mp3";
|
return original + ".mp3";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String generateUserUploadedMP3Filename(String original, JSONObject privateJson) throws Exception {
|
||||||
|
//Remove unavailable tags
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
//Tag track with data from API
|
//Tag track with data from API
|
||||||
public static void tagTrack(String path, JSONObject publicTrack, JSONObject publicAlbum, String cover, JSONObject lyricsData) throws Exception {
|
public static void tagTrack(String path, JSONObject publicTrack, JSONObject publicAlbum, String cover, JSONObject lyricsData) throws Exception {
|
||||||
TagOptionSingleton.getInstance().setAndroid(true);
|
TagOptionSingleton.getInstance().setAndroid(true);
|
||||||
|
|
|
@ -51,6 +51,11 @@ public class Download {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Negative TrackIDs = User uploaded MP3s.
|
||||||
|
public boolean isUserUploaded() {
|
||||||
|
return trackId.startsWith("-");
|
||||||
|
}
|
||||||
|
|
||||||
//Get download from SQLite cursor, HAS TO ALIGN
|
//Get download from SQLite cursor, HAS TO ALIGN
|
||||||
static Download fromSQL(Cursor cursor) {
|
static Download fromSQL(Cursor cursor) {
|
||||||
return new Download(cursor.getInt(0), cursor.getString(1), cursor.getInt(2) == 1, cursor.getInt(3), DownloadState.values()[cursor.getInt(4)],
|
return new Download(cursor.getInt(0), cursor.getString(1), cursor.getInt(2) == 1, cursor.getInt(3), DownloadState.values()[cursor.getInt(4)],
|
||||||
|
|
|
@ -274,6 +274,7 @@ public class DownloadService extends Service {
|
||||||
File outFile;
|
File outFile;
|
||||||
JSONObject trackJson;
|
JSONObject trackJson;
|
||||||
JSONObject albumJson;
|
JSONObject albumJson;
|
||||||
|
JSONObject privateJson;
|
||||||
boolean stopDownload = false;
|
boolean stopDownload = false;
|
||||||
DownloadThread(Download download) {
|
DownloadThread(Download download) {
|
||||||
this.download = download;
|
this.download = download;
|
||||||
|
@ -293,8 +294,13 @@ public class DownloadService extends Service {
|
||||||
|
|
||||||
//Fetch metadata
|
//Fetch metadata
|
||||||
try {
|
try {
|
||||||
|
JSONObject privateRaw = deezer.callGWAPI("song.getListData", "{\"sng_ids\": [" + download.trackId + "]}");
|
||||||
|
privateJson = privateRaw.getJSONObject("results").getJSONArray("data").getJSONObject(0);
|
||||||
|
//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();
|
||||||
|
@ -305,7 +311,7 @@ public class DownloadService extends Service {
|
||||||
|
|
||||||
//ISRC Fallback
|
//ISRC Fallback
|
||||||
try {
|
try {
|
||||||
if (trackJson.has("available_countries") && trackJson.getJSONArray("available_countries").length() == 0) {
|
if (!download.isUserUploaded() && trackJson.has("available_countries") && trackJson.getJSONArray("available_countries").length() == 0) {
|
||||||
logger.warn("ISRC Fallback!", download);
|
logger.warn("ISRC Fallback!", download);
|
||||||
JSONObject newTrackJson = Deezer.callPublicAPI("track", "isrc:" + trackJson.getString("isrc"));
|
JSONObject newTrackJson = Deezer.callPublicAPI("track", "isrc:" + trackJson.getString("isrc"));
|
||||||
//Same track check
|
//Same track check
|
||||||
|
@ -349,7 +355,11 @@ public class DownloadService extends Service {
|
||||||
if (!download.priv) {
|
if (!download.priv) {
|
||||||
//Check file
|
//Check file
|
||||||
try {
|
try {
|
||||||
|
if (download.isUserUploaded()) {
|
||||||
|
outFile = new File(Deezer.generateUserUploadedMP3Filename(download.path, privateJson));
|
||||||
|
} else {
|
||||||
outFile = new File(Deezer.generateFilename(download.path, trackJson, albumJson, newQuality));
|
outFile = new File(Deezer.generateFilename(download.path, trackJson, albumJson, newQuality));
|
||||||
|
}
|
||||||
parentDir = new File(outFile.getParent());
|
parentDir = new File(outFile.getParent());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error generating track filename (" + download.path + "): " + e.toString(), download);
|
logger.error("Error generating track filename (" + download.path + "): " + e.toString(), download);
|
||||||
|
@ -486,7 +496,8 @@ public class DownloadService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!download.priv) {
|
//Cover & Tags, ignore on user uploaded
|
||||||
|
if (!download.priv && !download.isUserUploaded()) {
|
||||||
|
|
||||||
//Download cover for each track
|
//Download cover for each track
|
||||||
File coverFile = new File(outFile.getPath().substring(0, outFile.getPath().lastIndexOf('.')) + ".jpg");
|
File coverFile = new File(outFile.getPath().substring(0, outFile.getPath().lastIndexOf('.')) + ".jpg");
|
||||||
|
@ -520,19 +531,19 @@ public class DownloadService extends Service {
|
||||||
|
|
||||||
JSONObject lyricsData = null;
|
JSONObject lyricsData = null;
|
||||||
//Lyrics
|
//Lyrics
|
||||||
if (settings.downloadLyrics) {
|
|
||||||
try {
|
try {
|
||||||
lyricsData = deezer.callGWAPI("song.getLyrics", "{\"sng_id\": " + download.trackId + "}");
|
lyricsData = deezer.callGWAPI("song.getLyrics", "{\"sng_id\": " + download.trackId + "}");
|
||||||
|
if (settings.downloadLyrics) {
|
||||||
String lrcData = Deezer.generateLRC(lyricsData, trackJson);
|
String lrcData = Deezer.generateLRC(lyricsData, trackJson);
|
||||||
//Create file
|
//Create file
|
||||||
String lrcFilename = outFile.getPath().substring(0, outFile.getPath().lastIndexOf(".")+1) + "lrc";
|
String lrcFilename = outFile.getPath().substring(0, outFile.getPath().lastIndexOf(".")+1) + "lrc";
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(lrcFilename);
|
FileOutputStream fileOutputStream = new FileOutputStream(lrcFilename);
|
||||||
fileOutputStream.write(lrcData.getBytes());
|
fileOutputStream.write(lrcData.getBytes());
|
||||||
fileOutputStream.close();
|
fileOutputStream.close();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Error downloading lyrics! " + e.toString(), download);
|
logger.warn("Error downloading lyrics! " + e.toString(), download);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Tag
|
//Tag
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:freezer/api/deezer.dart';
|
import 'package:freezer/api/deezer.dart';
|
||||||
import 'package:freezer/api/definitions.dart';
|
import 'package:freezer/api/definitions.dart';
|
||||||
import 'package:freezer/ui/details_screens.dart';
|
import 'package:freezer/ui/details_screens.dart';
|
||||||
|
import 'package:freezer/ui/library.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
@ -30,6 +31,14 @@ class Cache {
|
||||||
@JsonKey(defaultValue: {})
|
@JsonKey(defaultValue: {})
|
||||||
Map<String, SortType> playlistSort;
|
Map<String, SortType> playlistSort;
|
||||||
|
|
||||||
|
//Sort
|
||||||
|
@JsonKey(defaultValue: AlbumSortType.DEFAULT)
|
||||||
|
AlbumSortType albumSort;
|
||||||
|
@JsonKey(defaultValue: ArtistSortType.DEFAULT)
|
||||||
|
ArtistSortType artistSort;
|
||||||
|
@JsonKey(defaultValue: PlaylistSortType.DEFAULT)
|
||||||
|
PlaylistSortType libraryPlaylistSort;
|
||||||
|
|
||||||
|
|
||||||
Cache({this.libraryTracks});
|
Cache({this.libraryTracks});
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,16 @@ Cache _$CacheFromJson(Map<String, dynamic> json) {
|
||||||
..playlistSort = (json['playlistSort'] as Map<String, dynamic>)?.map(
|
..playlistSort = (json['playlistSort'] as Map<String, dynamic>)?.map(
|
||||||
(k, e) => MapEntry(k, _$enumDecodeNullable(_$SortTypeEnumMap, e)),
|
(k, e) => MapEntry(k, _$enumDecodeNullable(_$SortTypeEnumMap, e)),
|
||||||
) ??
|
) ??
|
||||||
{};
|
{}
|
||||||
|
..albumSort =
|
||||||
|
_$enumDecodeNullable(_$AlbumSortTypeEnumMap, json['albumSort']) ??
|
||||||
|
AlbumSortType.DEFAULT
|
||||||
|
..artistSort =
|
||||||
|
_$enumDecodeNullable(_$ArtistSortTypeEnumMap, json['artistSort']) ??
|
||||||
|
ArtistSortType.DEFAULT
|
||||||
|
..libraryPlaylistSort = _$enumDecodeNullable(
|
||||||
|
_$PlaylistSortTypeEnumMap, json['libraryPlaylistSort']) ??
|
||||||
|
PlaylistSortType.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> _$CacheToJson(Cache instance) => <String, dynamic>{
|
Map<String, dynamic> _$CacheToJson(Cache instance) => <String, dynamic>{
|
||||||
|
@ -27,6 +36,10 @@ Map<String, dynamic> _$CacheToJson(Cache instance) => <String, dynamic>{
|
||||||
'history': instance.history,
|
'history': instance.history,
|
||||||
'playlistSort': instance.playlistSort
|
'playlistSort': instance.playlistSort
|
||||||
?.map((k, e) => MapEntry(k, _$SortTypeEnumMap[e])),
|
?.map((k, e) => MapEntry(k, _$SortTypeEnumMap[e])),
|
||||||
|
'albumSort': _$AlbumSortTypeEnumMap[instance.albumSort],
|
||||||
|
'artistSort': _$ArtistSortTypeEnumMap[instance.artistSort],
|
||||||
|
'libraryPlaylistSort':
|
||||||
|
_$PlaylistSortTypeEnumMap[instance.libraryPlaylistSort],
|
||||||
};
|
};
|
||||||
|
|
||||||
T _$enumDecode<T>(
|
T _$enumDecode<T>(
|
||||||
|
@ -67,3 +80,25 @@ const _$SortTypeEnumMap = {
|
||||||
SortType.ALPHABETIC: 'ALPHABETIC',
|
SortType.ALPHABETIC: 'ALPHABETIC',
|
||||||
SortType.ARTIST: 'ARTIST',
|
SortType.ARTIST: 'ARTIST',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _$AlbumSortTypeEnumMap = {
|
||||||
|
AlbumSortType.DEFAULT: 'DEFAULT',
|
||||||
|
AlbumSortType.REVERSE: 'REVERSE',
|
||||||
|
AlbumSortType.ALPHABETIC: 'ALPHABETIC',
|
||||||
|
AlbumSortType.ARTIST: 'ARTIST',
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$ArtistSortTypeEnumMap = {
|
||||||
|
ArtistSortType.DEFAULT: 'DEFAULT',
|
||||||
|
ArtistSortType.REVERSE: 'REVERSE',
|
||||||
|
ArtistSortType.POPULARITY: 'POPULARITY',
|
||||||
|
ArtistSortType.ALPHABETIC: 'ALPHABETIC',
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$PlaylistSortTypeEnumMap = {
|
||||||
|
PlaylistSortType.DEFAULT: 'DEFAULT',
|
||||||
|
PlaylistSortType.REVERSE: 'REVERSE',
|
||||||
|
PlaylistSortType.ALPHABETIC: 'ALPHABETIC',
|
||||||
|
PlaylistSortType.USER: 'USER',
|
||||||
|
PlaylistSortType.TRACK_COUNT: 'TRACK_COUNT',
|
||||||
|
};
|
||||||
|
|
|
@ -246,6 +246,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
|
|
||||||
//Queue
|
//Queue
|
||||||
List<MediaItem> _queue = <MediaItem>[];
|
List<MediaItem> _queue = <MediaItem>[];
|
||||||
|
List<int> _shuffleHistory = [];
|
||||||
int _queueIndex = 0;
|
int _queueIndex = 0;
|
||||||
ConcatenatingAudioSource _audioSource;
|
ConcatenatingAudioSource _audioSource;
|
||||||
|
|
||||||
|
@ -363,13 +364,15 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
Future<void> onSkipToNext() async {
|
Future<void> onSkipToNext() async {
|
||||||
//Shuffle
|
//Shuffle
|
||||||
if (_player.shuffleModeEnabled??false) {
|
if (_player.shuffleModeEnabled??false) {
|
||||||
int newIndex = Random().nextInt(_queue.length)-1;
|
int newIndex = Random().nextInt(_queue.length-1);
|
||||||
//Update state
|
//Update state
|
||||||
_skipState = newIndex > _queueIndex
|
_skipState = newIndex > _queueIndex
|
||||||
? AudioProcessingState.skippingToNext
|
? AudioProcessingState.skippingToNext
|
||||||
: AudioProcessingState.skippingToPrevious;
|
: AudioProcessingState.skippingToPrevious;
|
||||||
|
|
||||||
|
if (_shuffleHistory.length == 0) _shuffleHistory.add(_queueIndex);
|
||||||
_queueIndex = newIndex;
|
_queueIndex = newIndex;
|
||||||
|
_shuffleHistory.add(newIndex);
|
||||||
await _player.seek(Duration.zero, index: _queueIndex);
|
await _player.seek(Duration.zero, index: _queueIndex);
|
||||||
_skipState = null;
|
_skipState = null;
|
||||||
return;
|
return;
|
||||||
|
@ -385,9 +388,24 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onSkipToPrevious() async {
|
Future<void> onSkipToPrevious() async {
|
||||||
if (_queueIndex == 0) return;
|
if (_queueIndex == 0 && !(_player.shuffleModeEnabled??false)) return;
|
||||||
//Update buffering state
|
//Update buffering state
|
||||||
_skipState = AudioProcessingState.skippingToPrevious;
|
_skipState = AudioProcessingState.skippingToPrevious;
|
||||||
|
|
||||||
|
//Shuffle history
|
||||||
|
if ((_player.shuffleModeEnabled??false) && _shuffleHistory.length > 1) {
|
||||||
|
_shuffleHistory.removeLast();
|
||||||
|
if (_shuffleHistory.last < _queue.length) {
|
||||||
|
_queueIndex = _shuffleHistory.last;
|
||||||
|
await _player.seek(Duration.zero, index: _queueIndex);
|
||||||
|
_skipState = null;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
_shuffleHistory = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Normal skip to previous
|
||||||
_queueIndex--;
|
_queueIndex--;
|
||||||
await _player.seekToPrevious();
|
await _player.seekToPrevious();
|
||||||
_skipState = null;
|
_skipState = null;
|
||||||
|
@ -553,9 +571,11 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
if (name == 'repeatType') {
|
if (name == 'repeatType') {
|
||||||
_player.setLoopMode(LoopMode.values[args]);
|
_player.setLoopMode(LoopMode.values[args]);
|
||||||
}
|
}
|
||||||
if (name == 'saveQueue') await this._saveQueue();
|
if (name == 'saveQueue')
|
||||||
|
await this._saveQueue();
|
||||||
//Load queue after some initialization in frontend
|
//Load queue after some initialization in frontend
|
||||||
if (name == 'load') await this._loadQueueFile();
|
if (name == 'load')
|
||||||
|
await this._loadQueueFile();
|
||||||
//Shuffle
|
//Shuffle
|
||||||
if (name == 'shuffle') {
|
if (name == 'shuffle') {
|
||||||
await _player.setShuffleModeEnabled(args);
|
await _player.setShuffleModeEnabled(args);
|
||||||
|
|
|
@ -208,7 +208,5 @@ const language_ar_ar = {
|
||||||
"User": "المستخدم",
|
"User": "المستخدم",
|
||||||
"Track count": "عدد الاغاني",
|
"Track count": "عدد الاغاني",
|
||||||
"If you want to use custom directory naming - use '/' as directory separator.": "إذا كنت تريد استخدام تسمية مخصصة، استخدم '/' كفاصل بين المسار."
|
"If you want to use custom directory naming - use '/' as directory separator.": "إذا كنت تريد استخدام تسمية مخصصة، استخدم '/' كفاصل بين المسار."
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -47,7 +47,8 @@ const language_el_gr = {
|
||||||
"Currently supporting only Spotify, with 100 tracks limit":
|
"Currently supporting only Spotify, with 100 tracks limit":
|
||||||
"Αυτήν τη στιγμή υποστηρίζεται μόνο το Spotify, με όριο 100 κομματιών",
|
"Αυτήν τη στιγμή υποστηρίζεται μόνο το Spotify, με όριο 100 κομματιών",
|
||||||
"Due to API limitations": "Λόγω περιορισμών API",
|
"Due to API limitations": "Λόγω περιορισμών API",
|
||||||
"Enter your playlist link below": "Εισαγάγετε τον σύνδεσμο λίστας αναπαραγωγής παρακάτω",
|
"Enter your playlist link below":
|
||||||
|
"Εισαγάγετε τον σύνδεσμο λίστας αναπαραγωγής παρακάτω",
|
||||||
"Error loading URL!": "Σφάλμα φόρτωσης διεύθυνσης URL!",
|
"Error loading URL!": "Σφάλμα φόρτωσης διεύθυνσης URL!",
|
||||||
"Convert": "Μετατροπή",
|
"Convert": "Μετατροπή",
|
||||||
"Download only": "Μόνο λήψη",
|
"Download only": "Μόνο λήψη",
|
||||||
|
@ -58,8 +59,9 @@ const language_el_gr = {
|
||||||
"Artists": "Καλλιτέχνες",
|
"Artists": "Καλλιτέχνες",
|
||||||
"Playlists": "Λίστες αναπαραγωγής",
|
"Playlists": "Λίστες αναπαραγωγής",
|
||||||
"Import": "Εισαγωγή",
|
"Import": "Εισαγωγή",
|
||||||
"Import playlists from Spotify": "Εισαγωγή λιστών αναπαραγωγής από το Spotify",
|
"Import playlists from Spotify":
|
||||||
"Statistics": "Στατιστική",
|
"Εισαγωγή λιστών αναπαραγωγής από το Spotify",
|
||||||
|
"Statistics": "Στατιστικά",
|
||||||
"Offline tracks": "Κομμάτια εκτός σύνδεσης",
|
"Offline tracks": "Κομμάτια εκτός σύνδεσης",
|
||||||
"Offline albums": "Album εκτός σύνδεσης",
|
"Offline albums": "Album εκτός σύνδεσης",
|
||||||
"Offline playlists": "Λίστες αναπαραγωγής εκτός σύνδεσης",
|
"Offline playlists": "Λίστες αναπαραγωγής εκτός σύνδεσης",
|
||||||
|
@ -104,7 +106,8 @@ const language_el_gr = {
|
||||||
"Remove album": "Κατάργηση album",
|
"Remove album": "Κατάργηση album",
|
||||||
"Album removed": "Το album καταργήθηκε",
|
"Album removed": "Το album καταργήθηκε",
|
||||||
"Remove from favorites": "Κατάργηση από τα αγαπημένα",
|
"Remove from favorites": "Κατάργηση από τα αγαπημένα",
|
||||||
"Artist removed from library": "Ο καλλιτέχνης καταργήθηκε από τη βιβλιοθήκη",
|
"Artist removed from library":
|
||||||
|
"Ο καλλιτέχνης καταργήθηκε από τη βιβλιοθήκη",
|
||||||
"Add to favorites": "Προσθήκη στα αγαπημένα",
|
"Add to favorites": "Προσθήκη στα αγαπημένα",
|
||||||
"Remove from library": "Κατάργηση από τη βιβλιοθήκη",
|
"Remove from library": "Κατάργηση από τη βιβλιοθήκη",
|
||||||
"Add playlist to library": "Προσθήκη λίστας αναπαραγωγής στη βιβλιοθήκη",
|
"Add playlist to library": "Προσθήκη λίστας αναπαραγωγής στη βιβλιοθήκη",
|
||||||
|
@ -126,7 +129,7 @@ const language_el_gr = {
|
||||||
"Show all tracks": "Εμφάνιση όλων των κομματιών",
|
"Show all tracks": "Εμφάνιση όλων των κομματιών",
|
||||||
"Show all playlists": "Εμφάνιση όλων των λιστών αναπαραγωγής",
|
"Show all playlists": "Εμφάνιση όλων των λιστών αναπαραγωγής",
|
||||||
"Settings": "Ρυθμίσεις",
|
"Settings": "Ρυθμίσεις",
|
||||||
"General": "Γενικός",
|
"General": "Γενικά",
|
||||||
"Appearance": "Εμφάνιση",
|
"Appearance": "Εμφάνιση",
|
||||||
"Quality": "Ποιότητα",
|
"Quality": "Ποιότητα",
|
||||||
"Deezer": "Deezer",
|
"Deezer": "Deezer",
|
||||||
|
@ -139,7 +142,8 @@ const language_el_gr = {
|
||||||
"Deezer (Dark)": "Deezer (Σκούρο)",
|
"Deezer (Dark)": "Deezer (Σκούρο)",
|
||||||
"Primary color": "Πρωτεύον χρώμα",
|
"Primary color": "Πρωτεύον χρώμα",
|
||||||
"Selected color": "Επιλεγμένο χρώμα",
|
"Selected color": "Επιλεγμένο χρώμα",
|
||||||
"Use album art primary color": "Χρησιμοποιήστε το πρωτεύον χρώμα του εξώφυλλου του album",
|
"Use album art primary color":
|
||||||
|
"Χρησιμοποιήστε το πρωτεύον χρώμα του εξώφυλλου του album",
|
||||||
"Warning: might be buggy": "Προειδοποίηση: μπορεί να μη λειτουργεί σωστά",
|
"Warning: might be buggy": "Προειδοποίηση: μπορεί να μη λειτουργεί σωστά",
|
||||||
"Mobile streaming": "Ροή μέσω δεδομένων κινητού δικτύου",
|
"Mobile streaming": "Ροή μέσω δεδομένων κινητού δικτύου",
|
||||||
"Wifi streaming": "Ροή μέσω WIFI",
|
"Wifi streaming": "Ροή μέσω WIFI",
|
||||||
|
@ -149,7 +153,8 @@ const language_el_gr = {
|
||||||
"Όχι γλώσσα εφαρμογής, χρησιμοποιείται στις κεφαλίδες. Τρέχουσα",
|
"Όχι γλώσσα εφαρμογής, χρησιμοποιείται στις κεφαλίδες. Τρέχουσα",
|
||||||
"Select language": "Επιλογή γλώσσας",
|
"Select language": "Επιλογή γλώσσας",
|
||||||
"Content country": "Χώρα περιεχομένου",
|
"Content country": "Χώρα περιεχομένου",
|
||||||
"Country used in headers. Now": "Χώρα που χρησιμοποιείται στις κεφαλίδες. Τρέχουσα",
|
"Country used in headers. Now":
|
||||||
|
"Χώρα που χρησιμοποιείται στις κεφαλίδες. Τρέχουσα",
|
||||||
"Log tracks": "Αρχεία καταγραφής",
|
"Log tracks": "Αρχεία καταγραφής",
|
||||||
"Send track listen logs to Deezer, enable it for features like Flow to work properly":
|
"Send track listen logs to Deezer, enable it for features like Flow to work properly":
|
||||||
"Αποστολή αρχείων καταγραφής ακρόασης στο Deezer, ενεργοποιήστε το για ορθή λειτουργία υπηρεσιών όπως το Flow",
|
"Αποστολή αρχείων καταγραφής ακρόασης στο Deezer, ενεργοποιήστε το για ορθή λειτουργία υπηρεσιών όπως το Flow",
|
||||||
|
@ -187,7 +192,53 @@ const language_el_gr = {
|
||||||
"Importing...": "Εισαγωγή...",
|
"Importing...": "Εισαγωγή...",
|
||||||
"Radio": "Ραδιόφωνο",
|
"Radio": "Ραδιόφωνο",
|
||||||
"Flow": "Flow",
|
"Flow": "Flow",
|
||||||
"Track is not available on Deezer!": "Το κομμάτι δεν είναι διαθέσιμο στο Deezer!",
|
"Track is not available on Deezer!":
|
||||||
"Failed to download track! Please restart.": "Αποτυχία λήψης κομματιού! Κάντε επανεκκίνηση. "
|
"Το κομμάτι δεν είναι διαθέσιμο στο Deezer!",
|
||||||
|
"Failed to download track! Please restart.":
|
||||||
|
"Αποτυχία λήψης κομματιού! Κάντε επανεκκίνηση. ",
|
||||||
|
|
||||||
|
//0.5.0 Strings:
|
||||||
|
"Storage permission denied!": "Η άδεια χώρου αποθήκευσης απορρίφθηκε!",
|
||||||
|
"Failed": "Απέτυχαν",
|
||||||
|
"Queued": "Σε ουρά",
|
||||||
|
//Updated in 0.5.1 - used in context of download:
|
||||||
|
"External": "Χώρος αποθήκευσης",
|
||||||
|
//0.5.0
|
||||||
|
"Restart failed downloads": "Επανεκκίνηση αποτυχημένων λήψεων",
|
||||||
|
"Clear failed": "Εκκαθάριση αποτυχημένων",
|
||||||
|
"Download Settings": "Ρυθμίσεις Λήψεων",
|
||||||
|
"Create folder for playlist": "Δημιουργία φακέλου για λίστα αναπαραγωγής",
|
||||||
|
"Download .LRC lyrics": "Λήψη στίχων .LRC",
|
||||||
|
"Proxy": "Μεσολαβητής",
|
||||||
|
"Not set": "Δεν ρυθμίστηκε",
|
||||||
|
"Search or paste URL": "Αναζήτηση ή επικόλληση διεύθυνσης URL",
|
||||||
|
"History": "Ιστορικό",
|
||||||
|
//Updated 0.5.1
|
||||||
|
"Download threads": "Ταυτόχρονες λήψεις",
|
||||||
|
//0.5.0
|
||||||
|
"Lyrics unavailable, empty or failed to load!":
|
||||||
|
"Οι στίχοι δεν είναι διαθέσιμοι, είναι άδειοι ή δεν φορτώθηκαν!",
|
||||||
|
"About": "Σχετικά",
|
||||||
|
"Telegram Channel": "Κανάλι Telegram ",
|
||||||
|
"To get latest releases": "Για να λάβετε τις τελευταίες κυκλοφορίες",
|
||||||
|
"Official chat": "Επίσημη συνομιλία",
|
||||||
|
"Telegram Group": "Ομάδα Telegram",
|
||||||
|
"Huge thanks to all the contributors! <3":
|
||||||
|
"Πολλά ευχαριστώ σε όλους τους συνεισφέροντες! <3",
|
||||||
|
"Edit playlist": "Edit playlist",
|
||||||
|
"Update": "Ενημέρωση",
|
||||||
|
"Playlist updated!": "Η λίστα αναπαραγωγής ενημερώθηκε!",
|
||||||
|
"Downloads added!": "Προστέθηκαν λήψεις!",
|
||||||
|
|
||||||
|
//0.5.1 Strings:
|
||||||
|
"Save cover file for every track": "Αποθήκευση εξώφυλλου για κάθε κομμάτι",
|
||||||
|
"Download Log": "Αρχείο καταγραφής λήψεων",
|
||||||
|
"Repository": "Repository",
|
||||||
|
"Source code, report issues there.":
|
||||||
|
"Πηγαίος κώδικας, αναφέρετε ζητήματα εκεί.",
|
||||||
|
|
||||||
|
//0.5.2 Strings:
|
||||||
|
"Use system theme": "Χρησιμοποίηση θέματος συστήματος",
|
||||||
|
"Light": "Φωτεινο"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -215,7 +215,8 @@ const language_es_es = {
|
||||||
"To get latest releases": "Para obtener los últimos lanzamientos",
|
"To get latest releases": "Para obtener los últimos lanzamientos",
|
||||||
"Official chat": "Chat oficial",
|
"Official chat": "Chat oficial",
|
||||||
"Telegram Group": "Grupo de Telegram",
|
"Telegram Group": "Grupo de Telegram",
|
||||||
"Huge thanks to all the contributors! <3": "Muchas gracias a todos los contribuyentes! <3",
|
"Huge thanks to all the contributors! <3":
|
||||||
|
"Muchas gracias a todos los contribuyentes contributors! <3",
|
||||||
"Edit playlist": "Editar lista de reproducción",
|
"Edit playlist": "Editar lista de reproducción",
|
||||||
"Update": "Actualizar",
|
"Update": "Actualizar",
|
||||||
"Playlist updated!": "Lista de reproducción actualizada!",
|
"Playlist updated!": "Lista de reproducción actualizada!",
|
||||||
|
|
|
@ -224,6 +224,9 @@ const language_ru_ru = {
|
||||||
"Save cover file for every track": "Обложки для каждого трека отдельным файлом",
|
"Save cover file for every track": "Обложки для каждого трека отдельным файлом",
|
||||||
"Download Log": "Лог загрузок (технические данные)",
|
"Download Log": "Лог загрузок (технические данные)",
|
||||||
"Repository": "Репозиторий",
|
"Repository": "Репозиторий",
|
||||||
"Source code, report issues there.": "Исходный код, вопросы, предложения."
|
"Source code, report issues there.": "Исходный код, вопросы, предложения.",
|
||||||
|
//0.5.2 Strings:
|
||||||
|
"Use system theme": "Использовать тему системы",
|
||||||
|
"Light": "Светлая"
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -44,7 +44,7 @@ const language_tr_tr = {
|
||||||
"Please check your connection and try again later...":
|
"Please check your connection and try again later...":
|
||||||
"Lütfen bağlantınızı kontrol edin ve daha sonra tekrar deneyin ...",
|
"Lütfen bağlantınızı kontrol edin ve daha sonra tekrar deneyin ...",
|
||||||
"Show more": "Daha fazla göster",
|
"Show more": "Daha fazla göster",
|
||||||
"Importer": "Importer",
|
"Importer": "Aktar",
|
||||||
"Currently supporting only Spotify, with 100 tracks limit":
|
"Currently supporting only Spotify, with 100 tracks limit":
|
||||||
"Şu anda 100 parça sınırıyla yalnızca Spotify'ı destekliyor",
|
"Şu anda 100 parça sınırıyla yalnızca Spotify'ı destekliyor",
|
||||||
"Due to API limitations": "API sınırlamaları nedeniyle",
|
"Due to API limitations": "API sınırlamaları nedeniyle",
|
||||||
|
@ -74,8 +74,8 @@ const language_tr_tr = {
|
||||||
"Çevrimdışı modda oynatma listeleri oluşturulamaz",
|
"Çevrimdışı modda oynatma listeleri oluşturulamaz",
|
||||||
"Error": "Hata",
|
"Error": "Hata",
|
||||||
"Error logging in! Please check your token and internet connection and try again.":
|
"Error logging in! Please check your token and internet connection and try again.":
|
||||||
"Oturum açma hatası! Lütfen tokeninizi ve internet bağlantınızı kontrol edin ve tekrar deneyin.",
|
"Oturum açılamadı! Lütfen tokeninizi ve internet bağlantınızı kontrol edin ve tekrar deneyin.",
|
||||||
"Dismiss": "Reddet",
|
"Dismiss": "Kapat",
|
||||||
"Welcome to": "Hoşgeldiniz",
|
"Welcome to": "Hoşgeldiniz",
|
||||||
"Please login using your Deezer account.":
|
"Please login using your Deezer account.":
|
||||||
"Lütfen Deezer hesabınızı kullanarak giriş yapın.",
|
"Lütfen Deezer hesabınızı kullanarak giriş yapın.",
|
||||||
|
@ -91,37 +91,37 @@ const language_tr_tr = {
|
||||||
"Bu uygulamayı kullanarak Deezer Hizmet Şartları'nı kabul etmiyorsunuz",
|
"Bu uygulamayı kullanarak Deezer Hizmet Şartları'nı kabul etmiyorsunuz",
|
||||||
"Play next": "Sonrakini çal",
|
"Play next": "Sonrakini çal",
|
||||||
"Add to queue": "Sıraya ekle",
|
"Add to queue": "Sıraya ekle",
|
||||||
"Add track to favorites": "Favorilere parça ekle",
|
"Add track to favorites": "Parçayı favorilere ekle",
|
||||||
"Add to playlist": "Oynatma listesine ekle",
|
"Add to playlist": "Oynatma listesine ekle",
|
||||||
"Select playlist": "Oynatma listesi seçin",
|
"Select playlist": "Oynatma listesi seçin",
|
||||||
"Track added to": "Parça şuraya eklendi",
|
"Track added to": "Parça şuraya eklendi",
|
||||||
"Remove from playlist": "Oynatma listesinden kaldır",
|
"Remove from playlist": "Oynatma listesinden kaldır",
|
||||||
"Track removed from": "Parça şuradan kaldırıldı",
|
"Track removed from": "Parça şuradan kaldırıldı",
|
||||||
"Remove favorite": "Favoriyi kaldır",
|
"Remove favorite": "Favorilerden kaldır",
|
||||||
"Track removed from library": "Parça kütüphaneden kaldırıldı",
|
"Track removed from library": "Parça kütüphaneden kaldırıldı",
|
||||||
"Go to": "Git",
|
"Go to": "Git",
|
||||||
"Make offline": "Çevrimdışı yap",
|
"Make offline": "Çevrimdışı yap",
|
||||||
"Add to library": "Kütüphaneye ekle",
|
"Add to library": "Kütüphaneye ekle",
|
||||||
"Remove album": "Albümü kaldır",
|
"Remove album": "Albümü kaldır",
|
||||||
"Album removed": "Albüm kaldırıldı",
|
"Album removed": "Albüm kaldırıldı",
|
||||||
"Remove from favorites": "Favorilerden çıkar",
|
"Remove from favorites": "Favorilerden kaldır",
|
||||||
"Artist removed from library": "Sanatçı kütüphaneden kaldırıldı",
|
"Artist removed from library": "Sanatçı kütüphaneden kaldırıldı",
|
||||||
"Add to favorites": "Favorilere ekle",
|
"Add to favorites": "Favorilere ekle",
|
||||||
"Remove from library": "Kütüphaneden kaldır",
|
"Remove from library": "Kütüphaneden kaldır",
|
||||||
"Add playlist to library": "Oynatma listesini kütüphaneye ekleyin",
|
"Add playlist to library": "Oynatma listesini kütüphaneye ekleyin",
|
||||||
"Added playlist to library": "Kütüphaneye oynatma listesi eklendi",
|
"Added playlist to library": "Oynatma listesi kütüphaneye eklendi",
|
||||||
"Make playlist offline": "Oynatma listesini çevrimdışı yapın",
|
"Make playlist offline": "Oynatma listesini çevrimdışı yapın",
|
||||||
"Download playlist": "Oynatma listesini indirin",
|
"Download playlist": "Oynatma listesini indirin",
|
||||||
"Create playlist": "Oynatma listesi oluştur",
|
"Create playlist": "Oynatma listesi oluştur",
|
||||||
"Title": "Başlık",
|
"Title": "Başlık",
|
||||||
"Description": "Açıklama",
|
"Description": "Açıklama",
|
||||||
"Private": "Özel",
|
"Private": "Özel",
|
||||||
"Collaborative": "İşbirlikçi",
|
"Collaborative": "Paylaşılan",
|
||||||
"Create": "Oluştur",
|
"Create": "Oluştur",
|
||||||
"Playlist created!": "Oynatma listesi oluşturuldu!",
|
"Playlist created!": "Oynatma listesi oluşturuldu!",
|
||||||
"Playing from:": "Şuradan oynatılıyor:",
|
"Playing from:": "Şuradan oynatılıyor:",
|
||||||
"Queue": "Kuyruk",
|
"Queue": "Kuyruk",
|
||||||
"Offline search": "Offline search",
|
"Offline search": "Çevrimdışı arama",
|
||||||
"Search Results": "Arama Sonuçları",
|
"Search Results": "Arama Sonuçları",
|
||||||
"No results!": "Sonuç yok!",
|
"No results!": "Sonuç yok!",
|
||||||
"Show all tracks": "Tüm parçaları göster",
|
"Show all tracks": "Tüm parçaları göster",
|
||||||
|
@ -134,24 +134,24 @@ const language_tr_tr = {
|
||||||
"Theme": "Tema",
|
"Theme": "Tema",
|
||||||
"Currently": "Şu anda",
|
"Currently": "Şu anda",
|
||||||
"Select theme": "Tema seçin",
|
"Select theme": "Tema seçin",
|
||||||
"Light (default)": "Light (Varsayılan)",
|
"Light (default)": "Açık (Varsayılan)",
|
||||||
"Dark": "Dark",
|
"Dark": "Koyu",
|
||||||
"Black (AMOLED)": "Black (AMOLED)",
|
"Black (AMOLED)": "Siyah (AMOLED)",
|
||||||
"Deezer (Dark)": "Deezer (Dark)",
|
"Deezer (Dark)": "Deezer (Dark)",
|
||||||
"Primary color": "Ana renk",
|
"Primary color": "Ana renk",
|
||||||
"Selected color": "Seçilen renk",
|
"Selected color": "Seçilen renk",
|
||||||
"Use album art primary color": "Albüm resmi ana rengini kullan",
|
"Use album art primary color": "Albüm resmini ana renk olarak kullan",
|
||||||
"Warning: might be buggy": "Uyarı: hatalı olabilir",
|
"Warning: might be buggy": "Uyarı: hatalı olabilir",
|
||||||
"Mobile streaming": "Mobil akış",
|
"Mobile streaming": "Mobil veri",
|
||||||
"Wifi streaming": "Wifi akışı",
|
"Wifi streaming": "Wifi",
|
||||||
"External downloads": "Harici indirmeler",
|
"External downloads": "Harici indirmeler",
|
||||||
"Content language": "İçerik dili",
|
"Content language": "İçerik dili",
|
||||||
"Not app language, used in headers. Now":
|
"Not app language, used in headers. Now":
|
||||||
"Not app language, used in headers. Now",
|
"Uygulama dili değil, başlıklarda kullanılacak. Şuan",
|
||||||
"Select language": "Dil seçin",
|
"Select language": "Dil seçin",
|
||||||
"Content country": "İçerik ülkesi",
|
"Content country": "İçerik ülkesi",
|
||||||
"Country used in headers. Now": "Başlıklarda kullanılan ülke. Şimdi",
|
"Country used in headers. Now": "Başlıklarda kullanılan ülke. Şuan",
|
||||||
"Log tracks": "Log tracks",
|
"Log tracks": "Parça günlükleri",
|
||||||
"Send track listen logs to Deezer, enable it for features like Flow to work properly":
|
"Send track listen logs to Deezer, enable it for features like Flow to work properly":
|
||||||
"Parça dinleme günlüklerini Deezer'a gönderin, Flow gibi özelliklerin düzgün çalışması için etkinleştirin",
|
"Parça dinleme günlüklerini Deezer'a gönderin, Flow gibi özelliklerin düzgün çalışması için etkinleştirin",
|
||||||
"Offline mode": "Çevrimdışı mod",
|
"Offline mode": "Çevrimdışı mod",
|
||||||
|
@ -159,25 +159,25 @@ const language_tr_tr = {
|
||||||
"Error logging in, check your internet connections.":
|
"Error logging in, check your internet connections.":
|
||||||
"Giriş hatası, internet bağlantılarınızı kontrol edin.",
|
"Giriş hatası, internet bağlantılarınızı kontrol edin.",
|
||||||
"Logging in...": "Giriş yapılıyor...",
|
"Logging in...": "Giriş yapılıyor...",
|
||||||
"Download path": "İndirme yolu",
|
"Download path": "İndirme konumu",
|
||||||
"Downloads naming": "İndirilenler adlandırma",
|
"Downloads naming": "İndirilenleri adlandır",
|
||||||
"Downloaded tracks filename": "İndirilen parçaların dosya adı",
|
"Downloaded tracks filename": "İndirilen parçaların dosya adı",
|
||||||
"Valid variables are": "Geçerli değişkenler",
|
"Valid variables are": "Geçerli değişkenler",
|
||||||
"Reset": "Sıfırla",
|
"Reset": "Sıfırla",
|
||||||
"Clear": "Temizle",
|
"Clear": "Temizle",
|
||||||
"Create folders for artist": "Sanatçı için klasörler oluşturun",
|
"Create folders for artist": "Sanatçılar için klasörler oluşturun",
|
||||||
"Create folders for albums": "Albümler için klasörler oluşturun",
|
"Create folders for albums": "Albümler için klasörler oluşturun",
|
||||||
"Separate albums by discs": "Albümleri disklere göre ayırın",
|
"Separate albums by discs": "Albümleri disklere göre ayırın",
|
||||||
"Overwrite already downloaded files": "Zaten indirilmiş dosyaların üzerine yaz",
|
"Overwrite already downloaded files": "İndirilmiş dosyaların üzerine yaz",
|
||||||
"Copy ARL": "ARL kopyala",
|
"Copy ARL": "ARL kopyala",
|
||||||
"Copy userToken/ARL Cookie for use in other apps.":
|
"Copy userToken/ARL Cookie for use in other apps.":
|
||||||
"Diğer uygulamalarda kullanmak için userToken / ARL Cookie'yi kopyalayın.",
|
"Diğer uygulamalarda kullanmak için userToken / ARL Cookie'yi kopyalayın.",
|
||||||
"Copied": "Kopyalandı",
|
"Copied": "Kopyalandı",
|
||||||
"Log out": "Çıkış yap",
|
"Log out": "Çıkış yap",
|
||||||
"Due to plugin incompatibility, login using browser is unavailable without restart.":
|
"Due to plugin incompatibility, login using browser is unavailable without restart.":
|
||||||
"Eklenti uyumsuzluğu nedeniyle, yeniden başlatmadan tarayıcı kullanarak oturum açılamaz.",
|
"Eklenti uyumsuzluğu nedeniyle, yeniden başlatmadan tarayıcı kullanılarak oturum açılamaz.",
|
||||||
"(ARL ONLY) Continue": "(SADECE ARL) Devam et",
|
"(ARL ONLY) Continue": "(SADECE ARL) Devam et",
|
||||||
"Log out & Exit": "Çıkış yap & Çık",
|
"Log out & Exit": "Çıkış yap & Kapat",
|
||||||
"Pick-a-Path": "Konum seç",
|
"Pick-a-Path": "Konum seç",
|
||||||
"Select storage": "Depolama seç",
|
"Select storage": "Depolama seç",
|
||||||
"Go up": "Yukarı git",
|
"Go up": "Yukarı git",
|
||||||
|
@ -196,7 +196,7 @@ const language_tr_tr = {
|
||||||
"Failed": "Başarısız",
|
"Failed": "Başarısız",
|
||||||
"Queued": "Sıraya alındı",
|
"Queued": "Sıraya alındı",
|
||||||
//Updated in 0.5.1 - used in context of download:
|
//Updated in 0.5.1 - used in context of download:
|
||||||
"External": "Storage",
|
"External": "Depolama",
|
||||||
//0.5.0
|
//0.5.0
|
||||||
"Restart failed downloads": "Başarısız indirmeleri yeniden başlatın",
|
"Restart failed downloads": "Başarısız indirmeleri yeniden başlatın",
|
||||||
"Clear failed": "Silinemedi",
|
"Clear failed": "Silinemedi",
|
||||||
|
@ -213,7 +213,7 @@ const language_tr_tr = {
|
||||||
"Lyrics unavailable, empty or failed to load!": "Sözler mevcut değil, boş veya yüklenemedi!",
|
"Lyrics unavailable, empty or failed to load!": "Sözler mevcut değil, boş veya yüklenemedi!",
|
||||||
"About": "Hakkında",
|
"About": "Hakkında",
|
||||||
"Telegram Channel": "Telegram Kanalı",
|
"Telegram Channel": "Telegram Kanalı",
|
||||||
"To get latest releases": "En son sürümleri almak için",
|
"To get latest releases": "En son sürümleri indirmek için",
|
||||||
"Official chat": "Resmi sohbet",
|
"Official chat": "Resmi sohbet",
|
||||||
"Telegram Group": "Telegram Grubu",
|
"Telegram Group": "Telegram Grubu",
|
||||||
"Huge thanks to all the contributors! <3": "Katkıda bulunanlara çok teşekkürler! <3",
|
"Huge thanks to all the contributors! <3": "Katkıda bulunanlara çok teşekkürler! <3",
|
||||||
|
@ -225,7 +225,11 @@ const language_tr_tr = {
|
||||||
//0.5.1 Strings:
|
//0.5.1 Strings:
|
||||||
"Save cover file for every track": "Her parça için kapak dosyasını kaydedin",
|
"Save cover file for every track": "Her parça için kapak dosyasını kaydedin",
|
||||||
"Download Log": "İndirme Kayıtları",
|
"Download Log": "İndirme Kayıtları",
|
||||||
"Repository": "Depo",
|
"Repository": "Repo",
|
||||||
"Source code, report issues there.": "Kaynak kodu, sorunları bildirin"
|
"Source code, report issues there.": "Kaynak kodu, sorunları bildirin",
|
||||||
|
|
||||||
|
//0.5.2 Strings:
|
||||||
|
"Use system theme": "Sistem temasını kullan",
|
||||||
|
"Light": "Açık"
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -664,7 +664,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
||||||
tracks.sort((a, b) => a.title.compareTo(b.title));
|
tracks.sort((a, b) => a.title.compareTo(b.title));
|
||||||
return tracks;
|
return tracks;
|
||||||
case SortType.ARTIST:
|
case SortType.ARTIST:
|
||||||
tracks.sort((a, b) => a.artists[0].name.compareTo(b.artists[0].name));
|
tracks.sort((a, b) => a.artists[0].name.toLowerCase().compareTo(b.artists[0].name.toLowerCase()));
|
||||||
return tracks;
|
return tracks;
|
||||||
case SortType.REVERSE:
|
case SortType.REVERSE:
|
||||||
return tracks.reversed.toList();
|
return tracks.reversed.toList();
|
||||||
|
|
|
@ -419,6 +419,13 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum AlbumSortType {
|
||||||
|
DEFAULT,
|
||||||
|
REVERSE,
|
||||||
|
ALPHABETIC,
|
||||||
|
ARTIST
|
||||||
|
}
|
||||||
|
|
||||||
class LibraryAlbums extends StatefulWidget {
|
class LibraryAlbums extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_LibraryAlbumsState createState() => _LibraryAlbumsState();
|
_LibraryAlbumsState createState() => _LibraryAlbumsState();
|
||||||
|
@ -427,6 +434,25 @@ class LibraryAlbums extends StatefulWidget {
|
||||||
class _LibraryAlbumsState extends State<LibraryAlbums> {
|
class _LibraryAlbumsState extends State<LibraryAlbums> {
|
||||||
|
|
||||||
List<Album> _albums;
|
List<Album> _albums;
|
||||||
|
AlbumSortType _sort = AlbumSortType.DEFAULT;
|
||||||
|
|
||||||
|
List<Album> get _sorted {
|
||||||
|
List<Album> albums = List.from(_albums);
|
||||||
|
switch (_sort) {
|
||||||
|
case AlbumSortType.DEFAULT:
|
||||||
|
return _albums;
|
||||||
|
case AlbumSortType.REVERSE:
|
||||||
|
return _albums.reversed.toList();
|
||||||
|
case AlbumSortType.ALPHABETIC:
|
||||||
|
albums.sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()));
|
||||||
|
return albums;
|
||||||
|
case AlbumSortType.ARTIST:
|
||||||
|
albums.sort((a, b) => a.artists[0].name.toLowerCase().compareTo(b.artists[0].name.toLowerCase()));
|
||||||
|
return albums;
|
||||||
|
}
|
||||||
|
return albums;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Future _load() async {
|
Future _load() async {
|
||||||
if (settings.offlineMode) return;
|
if (settings.offlineMode) return;
|
||||||
|
@ -439,13 +465,44 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_load();
|
_load();
|
||||||
|
_sort = cache.albumSort??AlbumSortType.DEFAULT;
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text('Albums'.i18n),),
|
appBar: AppBar(
|
||||||
|
title: Text('Albums'.i18n),
|
||||||
|
actions: [
|
||||||
|
PopupMenuButton(
|
||||||
|
child: Icon(Icons.sort, size: 32.0),
|
||||||
|
onSelected: (AlbumSortType s) async {
|
||||||
|
setState(() => _sort = s);
|
||||||
|
cache.albumSort = s;
|
||||||
|
await cache.save();
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => <PopupMenuEntry<AlbumSortType>>[
|
||||||
|
PopupMenuItem(
|
||||||
|
value: AlbumSortType.DEFAULT,
|
||||||
|
child: Text('Default'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: AlbumSortType.REVERSE,
|
||||||
|
child: Text('Reverse'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: AlbumSortType.ALPHABETIC,
|
||||||
|
child: Text('Alphabetic'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: AlbumSortType.ARTIST,
|
||||||
|
child: Text('Artist'.i18n),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(height: 8.0,),
|
Container(height: 8.0,),
|
||||||
|
@ -459,7 +516,7 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
||||||
|
|
||||||
if (_albums != null)
|
if (_albums != null)
|
||||||
...List.generate(_albums.length, (int i) {
|
...List.generate(_albums.length, (int i) {
|
||||||
Album a = _albums[i];
|
Album a = _sorted[i];
|
||||||
return AlbumTile(
|
return AlbumTile(
|
||||||
a,
|
a,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -523,27 +580,118 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ArtistSortType {
|
||||||
|
DEFAULT,
|
||||||
|
REVERSE,
|
||||||
|
POPULARITY,
|
||||||
|
ALPHABETIC
|
||||||
|
}
|
||||||
|
|
||||||
class LibraryArtists extends StatefulWidget {
|
class LibraryArtists extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_LibraryArtistsState createState() => _LibraryArtistsState();
|
_LibraryArtistsState createState() => _LibraryArtistsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LibraryArtistsState extends State<LibraryArtists> {
|
class _LibraryArtistsState extends State<LibraryArtists> {
|
||||||
|
|
||||||
|
List<Artist> _artists;
|
||||||
|
ArtistSortType _sort = ArtistSortType.DEFAULT;
|
||||||
|
bool _loading = true;
|
||||||
|
bool _error = false;
|
||||||
|
|
||||||
|
List<Artist> get _sorted {
|
||||||
|
List<Artist> artists = List.from(_artists);
|
||||||
|
switch (_sort) {
|
||||||
|
case ArtistSortType.DEFAULT:
|
||||||
|
return _artists;
|
||||||
|
case ArtistSortType.REVERSE:
|
||||||
|
return _artists.reversed.toList();
|
||||||
|
case ArtistSortType.POPULARITY:
|
||||||
|
artists.sort((a, b) => b.fans - a.fans);
|
||||||
|
return artists;
|
||||||
|
case ArtistSortType.ALPHABETIC:
|
||||||
|
artists.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||||
|
return artists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load data
|
||||||
|
Future _load() async {
|
||||||
|
setState(() => _loading = true);
|
||||||
|
//Fetch
|
||||||
|
List<Artist> data;
|
||||||
|
try {
|
||||||
|
data = await deezerAPI.getArtists();
|
||||||
|
} catch (e) {}
|
||||||
|
//Update UI
|
||||||
|
setState(() {
|
||||||
|
if (data != null) {
|
||||||
|
_artists = data;
|
||||||
|
} else {
|
||||||
|
_error = true;
|
||||||
|
}
|
||||||
|
_loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_sort = cache.artistSort;
|
||||||
|
_load();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text('Artists'.i18n),),
|
appBar: AppBar(
|
||||||
body: FutureBuilder(
|
title: Text('Artists'.i18n),
|
||||||
future: deezerAPI.getArtists(),
|
actions: [
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
PopupMenuButton(
|
||||||
|
child: Icon(Icons.sort, size: 32.0),
|
||||||
if (snapshot.hasError) return ErrorScreen();
|
onSelected: (ArtistSortType s) async {
|
||||||
if (!snapshot.hasData) return Center(child: CircularProgressIndicator(),);
|
setState(() => _sort = s);
|
||||||
|
cache.artistSort = s;
|
||||||
return ListView(
|
await cache.save();
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => <PopupMenuEntry<ArtistSortType>>[
|
||||||
|
PopupMenuItem(
|
||||||
|
value: ArtistSortType.DEFAULT,
|
||||||
|
child: Text('Default'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: ArtistSortType.REVERSE,
|
||||||
|
child: Text('Reverse'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: ArtistSortType.ALPHABETIC,
|
||||||
|
child: Text('Alphabetic'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: ArtistSortType.POPULARITY,
|
||||||
|
child: Text('Popularity'.i18n),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
...List.generate(snapshot.data.length, (i) {
|
if (_loading)
|
||||||
Artist a = snapshot.data[i];
|
Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [CircularProgressIndicator()],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
if (_error)
|
||||||
|
Center(child: ErrorScreen()),
|
||||||
|
|
||||||
|
if (!_loading && !_error)
|
||||||
|
...List.generate(_artists.length, (i) {
|
||||||
|
Artist a = _sorted[i];
|
||||||
return ArtistHorizontalTile(
|
return ArtistHorizontalTile(
|
||||||
a,
|
a,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -554,19 +702,26 @@ class _LibraryArtistsState extends State<LibraryArtists> {
|
||||||
onHold: () {
|
onHold: () {
|
||||||
MenuSheet m = MenuSheet(context);
|
MenuSheet m = MenuSheet(context);
|
||||||
m.defaultArtistMenu(a, onRemove: () {
|
m.defaultArtistMenu(a, onRemove: () {
|
||||||
setState(() => {});
|
setState(() {
|
||||||
|
_artists.remove(a);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PlaylistSortType {
|
||||||
|
DEFAULT,
|
||||||
|
REVERSE,
|
||||||
|
ALPHABETIC,
|
||||||
|
USER,
|
||||||
|
TRACK_COUNT
|
||||||
|
}
|
||||||
|
|
||||||
class LibraryPlaylists extends StatefulWidget {
|
class LibraryPlaylists extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
@ -576,6 +731,26 @@ class LibraryPlaylists extends StatefulWidget {
|
||||||
class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
||||||
|
|
||||||
List<Playlist> _playlists;
|
List<Playlist> _playlists;
|
||||||
|
PlaylistSortType _sort = PlaylistSortType.DEFAULT;
|
||||||
|
|
||||||
|
List<Playlist> get _sorted {
|
||||||
|
List<Playlist> playlists = List.from(_playlists);
|
||||||
|
switch (_sort) {
|
||||||
|
case PlaylistSortType.DEFAULT:
|
||||||
|
return _playlists;
|
||||||
|
case PlaylistSortType.REVERSE:
|
||||||
|
return _playlists.reversed.toList();
|
||||||
|
case PlaylistSortType.USER:
|
||||||
|
playlists.sort((a, b) => (a.user.name??deezerAPI.userName).toLowerCase().compareTo((b.user.name??deezerAPI.userName).toLowerCase()));
|
||||||
|
return playlists;
|
||||||
|
case PlaylistSortType.TRACK_COUNT:
|
||||||
|
playlists.sort((a, b) => b.trackCount - a.trackCount);
|
||||||
|
return playlists;
|
||||||
|
case PlaylistSortType.ALPHABETIC:
|
||||||
|
playlists.sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()));
|
||||||
|
return playlists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future _load() async {
|
Future _load() async {
|
||||||
if (!settings.offlineMode) {
|
if (!settings.offlineMode) {
|
||||||
|
@ -588,6 +763,7 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
_sort = cache.libraryPlaylistSort;
|
||||||
_load();
|
_load();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
@ -606,7 +782,41 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text('Playlists'.i18n),),
|
appBar: AppBar(
|
||||||
|
title: Text('Playlists'.i18n),
|
||||||
|
actions: [
|
||||||
|
PopupMenuButton(
|
||||||
|
child: Icon(Icons.sort, size: 32.0),
|
||||||
|
onSelected: (PlaylistSortType s) async {
|
||||||
|
setState(() => _sort = s);
|
||||||
|
cache.libraryPlaylistSort = s;
|
||||||
|
await cache.save();
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => <PopupMenuEntry<PlaylistSortType>>[
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PlaylistSortType.DEFAULT,
|
||||||
|
child: Text('Default'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PlaylistSortType.REVERSE,
|
||||||
|
child: Text('Reverse'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PlaylistSortType.USER,
|
||||||
|
child: Text('User'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PlaylistSortType.TRACK_COUNT,
|
||||||
|
child: Text('Track count'.i18n),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PlaylistSortType.ALPHABETIC,
|
||||||
|
child: Text('Alphabetic'.i18n),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ListTile(
|
ListTile(
|
||||||
|
@ -652,7 +862,7 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
||||||
|
|
||||||
if (_playlists != null)
|
if (_playlists != null)
|
||||||
...List.generate(_playlists.length, (int i) {
|
...List.generate(_playlists.length, (int i) {
|
||||||
Playlist p = _playlists[i];
|
Playlist p = _sorted[i];
|
||||||
return PlaylistTile(
|
return PlaylistTile(
|
||||||
p,
|
p,
|
||||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
||||||
|
|
|
@ -90,15 +90,19 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||||
//Try logging in
|
//Try logging in
|
||||||
try {
|
try {
|
||||||
deezerAPI.arl = settings.arl;
|
deezerAPI.arl = settings.arl;
|
||||||
bool resp = await deezerAPI.rawAuthorize(onError: (e) => _error = e.toString());
|
bool resp = await deezerAPI.rawAuthorize(onError: (e) => setState(() => _error = e.toString()));
|
||||||
if (resp == false) { //false, not null
|
if (resp == false) { //false, not null
|
||||||
|
if (settings.arl.length != 192) {
|
||||||
|
if (_error == null) _error = '';
|
||||||
|
_error += 'Invalid ARL length!';
|
||||||
|
}
|
||||||
setState(() => settings.arl = null);
|
setState(() => settings.arl = null);
|
||||||
errorDialog();
|
errorDialog();
|
||||||
}
|
}
|
||||||
//On error show dialog and reset to null
|
//On error show dialog and reset to null
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_error = e;
|
_error = e.toString();
|
||||||
print('Login error: ' + e);
|
print('Login error: ' + e.toString());
|
||||||
setState(() => settings.arl = null);
|
setState(() => settings.arl = null);
|
||||||
errorDialog();
|
errorDialog();
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,7 +498,7 @@ class _DeezerSettingsState extends State<DeezerSettings> {
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('Proxy'.i18n),
|
title: Text('Proxy'.i18n),
|
||||||
leading: Icon(Icons.vpn_key),
|
leading: Icon(Icons.vpn_key),
|
||||||
subtitle: Text(settings.proxyAddress??'Not set'),
|
subtitle: Text(settings.proxyAddress??'Not set'.i18n),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
String _new;
|
String _new;
|
||||||
showDialog(
|
showDialog(
|
||||||
|
@ -606,7 +606,8 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
||||||
),
|
),
|
||||||
Container(height: 8.0),
|
Container(height: 8.0),
|
||||||
Text(
|
Text(
|
||||||
'Valid variables are'.i18n + ': %artists%, %artist%, %title%, %album%, %trackNumber%, %0trackNumber%, %feats%, %playlistTrackNumber%, %0playlistTrackNumber%, %year%, %date%',
|
'Valid variables are'.i18n + ': %artists%, %artist%, %title%, %album%, %trackNumber%, %0trackNumber%, %feats%, %playlistTrackNumber%, %0playlistTrackNumber%, %year%, %date%\n\n' +
|
||||||
|
"If you want to use custom directory naming - use '/' as directory separator.".i18n,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12.0,
|
fontSize: 12.0,
|
||||||
),
|
),
|
||||||
|
@ -658,8 +659,8 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
||||||
),
|
),
|
||||||
Slider(
|
Slider(
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 6,
|
max: 16,
|
||||||
divisions: 5,
|
divisions: 15,
|
||||||
value: _downloadThreads,
|
value: _downloadThreads,
|
||||||
label: _downloadThreads.round().toString(),
|
label: _downloadThreads.round().toString(),
|
||||||
onChanged: (double v) => setState(() => _downloadThreads = v),
|
onChanged: (double v) => setState(() => _downloadThreads = v),
|
||||||
|
@ -1040,15 +1041,6 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
||||||
|
|
||||||
String _version = '';
|
String _version = '';
|
||||||
|
|
||||||
//Title, Subtitle, URL
|
|
||||||
static final List<List<String>> credits = [
|
|
||||||
['exttex', 'Developer'],
|
|
||||||
['Bas Curtiz', 'Icon, logo, banner, design suggestions, tester'],
|
|
||||||
['Deemix', 'Better app <3', 'https://codeberg.org/RemixDev/deemix'],
|
|
||||||
['Tobs, Xandar Null, Francesco', 'Beta testers'],
|
|
||||||
['Annexhack', 'Android Auto help']
|
|
||||||
];
|
|
||||||
|
|
||||||
static final List<List<String>> translators = [
|
static final List<List<String>> translators = [
|
||||||
['Xandar Null', 'Arabic'],
|
['Xandar Null', 'Arabic'],
|
||||||
['Markus', 'German'],
|
['Markus', 'German'],
|
||||||
|
@ -1111,22 +1103,51 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('Repository'.i18n),
|
title: Text('Repository'.i18n),
|
||||||
subtitle: Text('Source code, report issues there.'),
|
subtitle: Text('Source code, report issues there.'.i18n),
|
||||||
leading: Icon(Icons.code, color: Colors.green, size: 36.0),
|
leading: Icon(Icons.code, color: Colors.green, size: 36.0),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launch('https://notabug.org/exttex/freezer');
|
launch('https://notabug.org/exttex/freezer');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
...List.generate(credits.length, (i) => ListTile(
|
ListTile(
|
||||||
title: Text(credits[i][0]),
|
title: Text('exttex'),
|
||||||
subtitle: Text(credits[i][1]),
|
subtitle: Text('Developer'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Bas Curtiz'),
|
||||||
|
subtitle: Text('Icon, logo, banner, design suggestions, tester'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Tobs'),
|
||||||
|
subtitle: Text('Alpha testers'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Deemix'),
|
||||||
|
subtitle: Text('Better app <3'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (credits[i].length >= 3) {
|
launch('https://codeberg.org/RemixDev/deemix');
|
||||||
launch(credits[i][2]);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Xandar Null'),
|
||||||
|
subtitle: Text('Tester, translations help'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Francesco'),
|
||||||
|
subtitle: Text('Tester'),
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
settings.primaryColor = Color(0xff333333);
|
||||||
|
});
|
||||||
|
updateTheme();
|
||||||
|
settings.save();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Annexhack'),
|
||||||
|
subtitle: Text('Android Auto help'),
|
||||||
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
...List.generate(translators.length, (i) => ListTile(
|
...List.generate(translators.length, (i) => ListTile(
|
||||||
title: Text(translators[i][0]),
|
title: Text(translators[i][0]),
|
||||||
|
|
|
@ -205,7 +205,9 @@ class ArtistHorizontalTile extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 2.0),
|
||||||
|
child: ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
artist.name,
|
artist.name,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
@ -217,6 +219,7 @@ class ArtistHorizontalTile extends StatelessWidget {
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onLongPress: onHold,
|
onLongPress: onHold,
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.5.2+1
|
version: 0.5.3+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.8.0 <3.0.0"
|
sdk: ">=2.8.0 <3.0.0"
|
||||||
|
|
Loading…
Reference in New Issue