0.5.3 - Download fixes, shuffle fix, sorting in library

This commit is contained in:
exttex 2020-10-12 22:49:13 +02:00
parent 952cf0f508
commit 2f471268c6
18 changed files with 556 additions and 167 deletions

View File

@ -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);

View File

@ -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)],

View File

@ -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 {
trackJson = Deezer.callPublicAPI("track", download.trackId); JSONObject privateRaw = deezer.callGWAPI("song.getListData", "{\"sng_ids\": [" + download.trackId + "]}");
albumJson = Deezer.callPublicAPI("album", Integer.toString(trackJson.getJSONObject("album").getInt("id"))); 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);
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 {
outFile = new File(Deezer.generateFilename(download.path, trackJson, albumJson, newQuality)); if (download.isUserUploaded()) {
outFile = new File(Deezer.generateUserUploadedMP3Filename(download.path, privateJson));
} else {
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,18 +531,18 @@ 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) {
logger.warn("Error downloading lyrics! " + e.toString(), download);
} }
} catch (Exception e) {
logger.warn("Error downloading lyrics! " + e.toString(), download);
} }

View File

@ -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});

View File

@ -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',
};

View File

@ -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);

View File

@ -202,13 +202,11 @@ const language_ar_ar = {
//0.5.2 Strings: //0.5.2 Strings:
"Use system theme": "استخدم ثيم النظام", "Use system theme": "استخدم ثيم النظام",
"Light": "ابيض", "Light": "ابيض",
//0.5.3 Strings: //0.5.3 Strings:
"Popularity": "الشعبية", "Popularity": "الشعبية",
"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.": "إذا كنت تريد استخدام تسمية مخصصة، استخدم '/' كفاصل بين المسار."
} }
}; };

View File

@ -9,7 +9,7 @@ const language_de_de = {
"Search": "Suche", "Search": "Suche",
"Library": "Mediathek", "Library": "Mediathek",
"Offline mode, can't play flow or smart track lists.": "Offline mode, can't play flow or smart track lists.":
"Offline-Modus, kann keine Flow- oder Smart Track-Listen abspielen.", "Offline-Modus, kann keine Flow- oder Smart Track-Listen abspielen.",
"Added to library": "Zur Mediathek hinzufügen", "Added to library": "Zur Mediathek hinzufügen",
"Download": "Download", "Download": "Download",
"Disk": "Disk", "Disk": "Disk",
@ -28,31 +28,31 @@ const language_de_de = {
"Done": "Erledigt", "Done": "Erledigt",
"Delete": "Gelöscht", "Delete": "Gelöscht",
"Are you sure you want to delete this download?": "Are you sure you want to delete this download?":
"Bist du sicher, dass du diesen Download löschen willst?", "Bist du sicher, dass du diesen Download löschen willst?",
"Cancel": "Abbrechen", "Cancel": "Abbrechen",
"Downloads": "Downloads", "Downloads": "Downloads",
"Clear queue": "Warteschleife löschen", "Clear queue": "Warteschleife löschen",
"This won't delete currently downloading item": "This won't delete currently downloading item":
"Dies löscht das derzeit heruntergeladene Element nicht", "Dies löscht das derzeit heruntergeladene Element nicht",
"Are you sure you want to delete all queued downloads?": "Are you sure you want to delete all queued downloads?":
"Bist du sicher, dass du alle Downloads aus der Warteschleife löschen willst?", "Bist du sicher, dass du alle Downloads aus der Warteschleife löschen willst?",
"Clear downloads history": "Download-Verlauf löschen", "Clear downloads history": "Download-Verlauf löschen",
"WARNING: This will only clear non-offline (external downloads)": "WARNING: This will only clear non-offline (external downloads)":
"ACHTUNG: (Externe Downloads) werden entfernt", "ACHTUNG: (Externe Downloads) werden entfernt",
"Please check your connection and try again later...": "Please check your connection and try again later...":
"Bitte überprüfe deine Verbindung und versuche es später noch einmal...", "Bitte überprüfe deine Verbindung und versuche es später noch einmal...",
"Show more": "Mehr anzeigen", "Show more": "Mehr anzeigen",
"Importer": "Importieren", "Importer": "Importieren",
"Currently supporting only Spotify, with 100 tracks limit": "Currently supporting only Spotify, with 100 tracks limit":
"Derzeit begrenzt auf maximal 100 Titel", "Derzeit begrenzt auf maximal 100 Titel",
"Due to API limitations": "Aufgrund von API-Einschränkungen", "Due to API limitations": "Aufgrund von API-Einschränkungen",
"Enter your playlist link below": "Enter your playlist link below":
"Gebe deinen Wiedergabelisten-Link unten ein", "Gebe deinen Wiedergabelisten-Link unten ein",
"Error loading URL!": "Fehler beim Laden der URL!", "Error loading URL!": "Fehler beim Laden der URL!",
"Convert": "Konvertieren", "Convert": "Konvertieren",
"Download only": "Nur Herunterladen", "Download only": "Nur Herunterladen",
"Downloading is currently stopped, click here to resume.": "Downloading is currently stopped, click here to resume.":
"Das Herunterladen ist derzeit gestoppt, klicke hier, um fortzufahren.", "Das Herunterladen ist derzeit gestoppt, klicke hier, um fortzufahren.",
"Tracks": "Titel", "Tracks": "Titel",
"Albums": "Alben", "Albums": "Alben",
"Artists": "Künstler", "Artists": "Künstler",
@ -70,24 +70,24 @@ const language_de_de = {
"All offline tracks": "Alle Offline-Titel", "All offline tracks": "Alle Offline-Titel",
"Create new playlist": "Neue Wiedergabeliste erstellen", "Create new playlist": "Neue Wiedergabeliste erstellen",
"Cannot create playlists in offline mode": "Cannot create playlists in offline mode":
"Wiedergabelisten können im Offline-Modus nicht erstellt werden", "Wiedergabelisten können im Offline-Modus nicht erstellt werden",
"Error": "Fehler", "Error": "Fehler",
"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.":
"Fehler beim Einloggen! Bitte überprüfe dein Token und deine Internetverbindung und versuche es erneut.", "Fehler beim Einloggen! Bitte überprüfe dein Token und deine Internetverbindung und versuche es erneut.",
"Dismiss": "Verwerfen", "Dismiss": "Verwerfen",
"Welcome to": "Willkommen bei", "Welcome to": "Willkommen bei",
"Please login using your Deezer account.": "Please login using your Deezer account.":
"Bitte melde dich mit deinem Deezer-Konto an.", "Bitte melde dich mit deinem Deezer-Konto an.",
"Login using browser": "Anmeldung über Browser", "Login using browser": "Anmeldung über Browser",
"Login using token": "Anmeldung per Token", "Login using token": "Anmeldung per Token",
"Enter ARL": "ARL eingeben", "Enter ARL": "ARL eingeben",
"Token (ARL)": "Token (ARL)", "Token (ARL)": "Token (ARL)",
"Save": "Speichern", "Save": "Speichern",
"If you don't have account, you can register on deezer.com for free.": "If you don't have account, you can register on deezer.com for free.":
"Wenn Du noch kein Konto hast, kannst Du Dich kostenlos auf deezer.com registrieren.", "Wenn Du noch kein Konto hast, kannst Du Dich kostenlos auf deezer.com registrieren.",
"Open in browser": "Im Browser öffnen", "Open in browser": "Im Browser öffnen",
"By using this app, you don't agree with the Deezer ToS": "By using this app, you don't agree with the Deezer ToS":
"Wenn Du diese Anwendung verwendest, bist Du nicht mit den Deezer ToS einverstanden", "Wenn Du diese Anwendung verwendest, bist Du nicht mit den Deezer ToS einverstanden",
"Play next": "Als nächstes spielen", "Play next": "Als nächstes spielen",
"Add to queue": "Zur Warteschleife hinzufügen", "Add to queue": "Zur Warteschleife hinzufügen",
"Add track to favorites": "Titel zu Favoriten hinzufügen", "Add track to favorites": "Titel zu Favoriten hinzufügen",
@ -151,11 +151,11 @@ const language_de_de = {
"Country used in headers. Now": "Aktuell", "Country used in headers. Now": "Aktuell",
"Log tracks": "Protokolliere Titel", "Log tracks": "Protokolliere Titel",
"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":
"Gehörte Titel-Protokolle an Deezer senden, damit Flow richtig funktioniert", "Gehörte Titel-Protokolle an Deezer senden, damit Flow richtig funktioniert",
"Offline mode": "Offline-Modus", "Offline mode": "Offline-Modus",
"Will be overwritten on start.": "Wird beim Start überschrieben.", "Will be overwritten on start.": "Wird beim Start überschrieben.",
"Error logging in, check your internet connections.": "Error logging in, check your internet connections.":
"Fehler beim Anmelden, überprüfe deine Internetverbindung.", "Fehler beim Anmelden, überprüfe deine Internetverbindung.",
"Logging in...": "Angemeldet...", "Logging in...": "Angemeldet...",
"Download path": "Download-Pfad", "Download path": "Download-Pfad",
"Downloads naming": "Benennung der Downloads", "Downloads naming": "Benennung der Downloads",
@ -167,14 +167,14 @@ const language_de_de = {
"Create folders for albums": "Ordner für Alben erstellen", "Create folders for albums": "Ordner für Alben erstellen",
"Separate albums by discs": "Alben nach Discs trennen", "Separate albums by discs": "Alben nach Discs trennen",
"Overwrite already downloaded files": "Overwrite already downloaded files":
"Bereits heruntergeladene Dateien überschreiben", "Bereits heruntergeladene Dateien überschreiben",
"Copy ARL": "ARL kopieren", "Copy ARL": "ARL kopieren",
"Copy userToken/ARL Cookie for use in other apps.": "Copy userToken/ARL Cookie for use in other apps.":
"UserToken / ARL-Cookie zur Verwendung in anderen Anwendungen kopieren.", "UserToken / ARL-Cookie zur Verwendung in anderen Anwendungen kopieren.",
"Copied": "Kopiert", "Copied": "Kopiert",
"Log out": "Abmelden", "Log out": "Abmelden",
"Due to plugin incompatibility, login using browser is unavailable without restart.": "Due to plugin incompatibility, login using browser is unavailable without restart.":
"Aufgrund von Plugin-Inkompatibilität ist die Anmeldung mit dem Browser ohne Neustart nicht möglich.", "Aufgrund von Plugin-Inkompatibilität ist die Anmeldung mit dem Browser ohne Neustart nicht möglich.",
"(ARL ONLY) Continue": "(NUR ARL) Fortfahren", "(ARL ONLY) Continue": "(NUR ARL) Fortfahren",
"Log out & Exit": "Abmelden & Beenden", "Log out & Exit": "Abmelden & Beenden",
"Pick-a-Path": "Wähle einen Pfad", "Pick-a-Path": "Wähle einen Pfad",
@ -183,7 +183,7 @@ const language_de_de = {
"Permission denied": "Zugriff verweigert", "Permission denied": "Zugriff verweigert",
"Language": "Sprache", "Language": "Sprache",
"Language changed, please restart Freezer to apply!": "Language changed, please restart Freezer to apply!":
"Sprache geändert, bitte Freezer neu starten!", "Sprache geändert, bitte Freezer neu starten!",
"Importing...": "Importiere...", "Importing...": "Importiere...",
"Radio": "Radio", "Radio": "Radio",
//0.5.0 Strings: //0.5.0 Strings:

View File

@ -10,7 +10,7 @@ const language_el_gr = {
"Search": "Αναζήτηση", "Search": "Αναζήτηση",
"Library": "Βιβλιοθήκη", "Library": "Βιβλιοθήκη",
"Offline mode, can't play flow or smart track lists.": "Offline mode, can't play flow or smart track lists.":
"Λειτουργία εκτός σύνδεσης, δεν είναι δυνατή η αναπαραγωγή flow ή έξυπνων λιστών κομματιών.", "Λειτουργία εκτός σύνδεσης, δεν είναι δυνατή η αναπαραγωγή flow ή έξυπνων λιστών κομματιών.",
"Added to library": "Προστέθηκε στη βιβλιοθήκη", "Added to library": "Προστέθηκε στη βιβλιοθήκη",
"Download": "Λήψη", "Download": "Λήψη",
"Disk": "Δίσκος", "Disk": "Δίσκος",
@ -29,37 +29,39 @@ const language_el_gr = {
"Done": "Ολοκληρώθηκε", "Done": "Ολοκληρώθηκε",
"Delete": "Διαγραφή", "Delete": "Διαγραφή",
"Are you sure you want to delete this download?": "Are you sure you want to delete this download?":
"Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν τη λήψη;", "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν τη λήψη;",
"Cancel": "Άκυρο", "Cancel": "Άκυρο",
"Downloads": "Λήψεις", "Downloads": "Λήψεις",
"Clear queue": "Εκκαθάριση ουράς", "Clear queue": "Εκκαθάριση ουράς",
"This won't delete currently downloading item": "This won't delete currently downloading item":
"Αυτό δεν θα διαγράψει το τρέχον αντικείμενο λήψης", "Αυτό δεν θα διαγράψει το τρέχον αντικείμενο λήψης",
"Are you sure you want to delete all queued downloads?": "Are you sure you want to delete all queued downloads?":
"Είστε βέβαιοι ότι θέλετε να διαγράψετε όλες τις λήψεις στην ουρά;", "Είστε βέβαιοι ότι θέλετε να διαγράψετε όλες τις λήψεις στην ουρά;",
"Clear downloads history": "Διαγραφή ιστορικού λήψεων", "Clear downloads history": "Διαγραφή ιστορικού λήψεων",
"WARNING: This will only clear non-offline (external downloads)": "WARNING: This will only clear non-offline (external downloads)":
"ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Αυτό θα καθαρίσει μόνο τις εκτός σύνδεσης (εξωτερικές) λήψεις", "ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Αυτό θα καθαρίσει μόνο τις εκτός σύνδεσης (εξωτερικές) λήψεις",
"Please check your connection and try again later...": "Please check your connection and try again later...":
"Ελέγξτε τη σύνδεσή σας και δοκιμάστε ξανά αργότερα...", "Ελέγξτε τη σύνδεσή σας και δοκιμάστε ξανά αργότερα...",
"Show more": "Δείτε περισσότερα", "Show more": "Δείτε περισσότερα",
"Importer": "Εισαγωγέας", "Importer": "Εισαγωγέας",
"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": "Μόνο λήψη",
"Downloading is currently stopped, click here to resume.": "Downloading is currently stopped, click here to resume.":
"Η λήψη έχει σταματήσει, κάντε κλικ εδώ για να συνεχίσετε.", "Η λήψη έχει σταματήσει, κάντε κλικ εδώ για να συνεχίσετε.",
"Tracks": "Κομμάτια", "Tracks": "Κομμάτια",
"Albums": "Album", "Albums": "Album",
"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": "Λίστες αναπαραγωγής εκτός σύνδεσης",
@ -70,24 +72,24 @@ const language_el_gr = {
"All offline tracks": "Όλα τα κομμάτια εκτός σύνδεσης", "All offline tracks": "Όλα τα κομμάτια εκτός σύνδεσης",
"Create new playlist": "Δημιουργία λίστας αναπαραγωγής", "Create new playlist": "Δημιουργία λίστας αναπαραγωγής",
"Cannot create playlists in offline mode": "Cannot create playlists in offline mode":
"Δεν είναι δυνατή η δημιουργία λιστών αναπαραγωγής σε λειτουργία εκτός σύνδεσης", "Δεν είναι δυνατή η δημιουργία λιστών αναπαραγωγής σε λειτουργία εκτός σύνδεσης",
"Error": "Σφάλμα", "Error": "Σφάλμα",
"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.":
"Σφάλμα σύνδεσης! Ελέγξτε το token και τη σύνδεσή σας στο δίκτυο και δοκιμάστε ξανά.", "Σφάλμα σύνδεσης! Ελέγξτε το token και τη σύνδεσή σας στο δίκτυο και δοκιμάστε ξανά.",
"Dismiss": "Απόρριψη", "Dismiss": "Απόρριψη",
"Welcome to": "Καλωσήρθατε στο", "Welcome to": "Καλωσήρθατε στο",
"Please login using your Deezer account.": "Please login using your Deezer account.":
"Συνδεθείτε χρησιμοποιώντας τον λογαριασμό σας στο Deezer.", "Συνδεθείτε χρησιμοποιώντας τον λογαριασμό σας στο Deezer.",
"Login using browser": "Σύνδεση χρησιμοποιώντας το πρόγραμμα περιήγησης", "Login using browser": "Σύνδεση χρησιμοποιώντας το πρόγραμμα περιήγησης",
"Login using token": "Σύνδεση χρησιμοποιώντας token", "Login using token": "Σύνδεση χρησιμοποιώντας token",
"Enter ARL": "Εισαγωγή ARL", "Enter ARL": "Εισαγωγή ARL",
"Token (ARL)": "Token (ARL)", "Token (ARL)": "Token (ARL)",
"Save": "Αποθήκευση", "Save": "Αποθήκευση",
"If you don't have account, you can register on deezer.com for free.": "If you don't have account, you can register on deezer.com for free.":
"Εάν δεν έχετε λογαριασμό, μπορείτε να εγγραφείτε δωρεάν στο deezer.com.", "Εάν δεν έχετε λογαριασμό, μπορείτε να εγγραφείτε δωρεάν στο deezer.com.",
"Open in browser": "Ανοιγμα σε πρόγραμμα περιήγησης", "Open in browser": "Ανοιγμα σε πρόγραμμα περιήγησης",
"By using this app, you don't agree with the Deezer ToS": "By using this app, you don't agree with the Deezer ToS":
"Χρησιμοποιώντας αυτήν την εφαρμογή, δεν συμφωνείτε με τους κανονισμούς χρήσης Deezer", "Χρησιμοποιώντας αυτήν την εφαρμογή, δεν συμφωνείτε με τους κανονισμούς χρήσης Deezer",
"Play next": "Παίξε αμέσως μετά", "Play next": "Παίξε αμέσως μετά",
"Add to queue": "Προσθήκη στην ουρά", "Add to queue": "Προσθήκη στην ουρά",
"Add track to favorites": "Προσθήκη κομμάτι στα αγαπημένα", "Add track to favorites": "Προσθήκη κομμάτι στα αγαπημένα",
@ -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,24 +142,26 @@ 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",
"External downloads": "Εξωτερικές λήψεις", "External downloads": "Εξωτερικές λήψεις",
"Content language": "Γλώσσα περιεχομένου", "Content language": "Γλώσσα περιεχομένου",
"Not app language, used in headers. Now": "Not app language, used in headers. Now":
"Όχι γλώσσα εφαρμογής, χρησιμοποιείται στις κεφαλίδες. Τρέχουσα", "Όχι γλώσσα εφαρμογής, χρησιμοποιείται στις κεφαλίδες. Τρέχουσα",
"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",
"Offline mode": "Λειτουργία εκτός σύνδεσης", "Offline mode": "Λειτουργία εκτός σύνδεσης",
"Will be overwritten on start.": "Θα αντικατασταθεί κατά την εκκίνηση.", "Will be overwritten on start.": "Θα αντικατασταθεί κατά την εκκίνηση.",
"Error logging in, check your internet connections.": "Error logging in, check your internet connections.":
"Σφάλμα σύνδεσης, ελέγξτε την σύνδεσή σας στο Δίκτυο.", "Σφάλμα σύνδεσης, ελέγξτε την σύνδεσή σας στο Δίκτυο.",
"Logging in...": "Σύνδεση...", "Logging in...": "Σύνδεση...",
"Download path": "Διαδρομή λήψεων", "Download path": "Διαδρομή λήψεων",
"Downloads naming": "Ονομασία λήψεων", "Downloads naming": "Ονομασία λήψεων",
@ -170,11 +175,11 @@ const language_el_gr = {
"Overwrite already downloaded files": "Αντικατάσταση ήδη ληφθέντων αρχείων", "Overwrite already downloaded files": "Αντικατάσταση ήδη ληφθέντων αρχείων",
"Copy ARL": "Αντιγραφή ARL", "Copy ARL": "Αντιγραφή ARL",
"Copy userToken/ARL Cookie for use in other apps.": "Copy userToken/ARL Cookie for use in other apps.":
"Αντιγραφή userToken/ARL Cookie για χρήση σε άλλες εφαρμογές.", "Αντιγραφή userToken/ARL Cookie για χρήση σε άλλες εφαρμογές.",
"Copied": "Αντιγράφηκε", "Copied": "Αντιγράφηκε",
"Log out": "Αποσύνδεση", "Log out": "Αποσύνδεση",
"Due to plugin incompatibility, login using browser is unavailable without restart.": "Due to plugin incompatibility, login using browser is unavailable without restart.":
"Λόγω ασυμβατότητας προσθηκών, η σύνδεση μέσω προγράμματος περιήγησης δεν είναι διαθέσιμη χωρίς επανεκκίνηση.", "Λόγω ασυμβατότητας προσθηκών, η σύνδεση μέσω προγράμματος περιήγησης δεν είναι διαθέσιμη χωρίς επανεκκίνηση.",
"(ARL ONLY) Continue": "(ARL ΜΟΝΟ) Συνέχεια", "(ARL ONLY) Continue": "(ARL ΜΟΝΟ) Συνέχεια",
"Log out & Exit": "Αποσύνδεση & Έξοδος", "Log out & Exit": "Αποσύνδεση & Έξοδος",
"Pick-a-Path": "Διαλέξτε ένα μονοπάτι", "Pick-a-Path": "Διαλέξτε ένα μονοπάτι",
@ -183,11 +188,57 @@ const language_el_gr = {
"Permission denied": "Η άδεια απορρίφθηκε", "Permission denied": "Η άδεια απορρίφθηκε",
"Language": "Γλώσσα", "Language": "Γλώσσα",
"Language changed, please restart Freezer to apply!": "Language changed, please restart Freezer to apply!":
"Η γλώσσα άλλαξε, κάντε επανεκκίνηση του Freezer για εφαρμογή!", "Η γλώσσα άλλαξε, κάντε επανεκκίνηση του Freezer για εφαρμογή!",
"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": "Φωτεινο"
} }
}; };

View File

@ -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!",

View File

@ -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": "Светлая"
} }
}; };

View File

@ -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": "ıklama", "Description": "ı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)": "ı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": "ık"
} }
}; };

View File

@ -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();

View File

@ -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,50 +580,148 @@ 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();
children: <Widget>[ },
...List.generate(snapshot.data.length, (i) { itemBuilder: (context) => <PopupMenuEntry<ArtistSortType>>[
Artist a = snapshot.data[i]; PopupMenuItem(
return ArtistHorizontalTile( value: ArtistSortType.DEFAULT,
a, child: Text('Default'.i18n),
onTap: () { ),
Navigator.of(context).push( PopupMenuItem(
MaterialPageRoute(builder: (context) => ArtistDetails(a)) value: ArtistSortType.REVERSE,
); child: Text('Reverse'.i18n),
}, ),
onHold: () { PopupMenuItem(
MenuSheet m = MenuSheet(context); value: ArtistSortType.ALPHABETIC,
m.defaultArtistMenu(a, onRemove: () { child: Text('Alphabetic'.i18n),
setState(() => {}); ),
}); PopupMenuItem(
}, value: ArtistSortType.POPULARITY,
); child: Text('Popularity'.i18n),
}), ),
], ],
); )
}, ],
),
body: ListView(
children: <Widget>[
if (_loading)
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(
a,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => ArtistDetails(a))
);
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultArtistMenu(a, onRemove: () {
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(

View File

@ -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();
} }

View File

@ -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]),

View File

@ -205,18 +205,21 @@ class ArtistHorizontalTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return Padding(
title: Text( padding: EdgeInsets.symmetric(vertical: 2.0),
artist.name, child: ListTile(
maxLines: 1, title: Text(
artist.name,
maxLines: 1,
),
leading: CachedImage(
url: artist.picture.thumb,
circular: true,
),
onTap: onTap,
onLongPress: onHold,
trailing: trailing,
), ),
leading: CachedImage(
url: artist.picture.thumb,
circular: true,
),
onTap: onTap,
onLongPress: onHold,
trailing: trailing,
); );
} }
} }

View File

@ -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"