Downloads fix, Spanish translation

This commit is contained in:
exttex 2020-09-22 19:13:54 +02:00
parent 2858859abd
commit 17df300a18
18 changed files with 548 additions and 103 deletions

View file

@ -123,7 +123,7 @@ class DeezerAPI {
Map<dynamic, dynamic> data = await callApi('deezer.pageAlbum', params: { Map<dynamic, dynamic> data = await callApi('deezer.pageAlbum', params: {
'alb_id': id, 'alb_id': id,
'header': true, 'header': true,
'lang': 'us' 'lang': settings.deezerLanguage??'en'
}); });
return Album.fromPrivateJson(data['results']['DATA'], songsJson: data['results']['SONGS']); return Album.fromPrivateJson(data['results']['DATA'], songsJson: data['results']['SONGS']);
} }
@ -132,7 +132,7 @@ class DeezerAPI {
Future<Artist> artist(String id) async { Future<Artist> artist(String id) async {
Map<dynamic, dynamic> data = await callApi('deezer.pageArtist', params: { Map<dynamic, dynamic> data = await callApi('deezer.pageArtist', params: {
'art_id': id, 'art_id': id,
'lang': 'us', 'lang': settings.deezerLanguage??'en',
}); });
return Artist.fromPrivateJson( return Artist.fromPrivateJson(
data['results']['DATA'], data['results']['DATA'],
@ -145,7 +145,7 @@ class DeezerAPI {
Future<List<Track>> playlistTracksPage(String id, int start, {int nb = 50}) async { Future<List<Track>> playlistTracksPage(String id, int start, {int nb = 50}) async {
Map data = await callApi('deezer.pagePlaylist', params: { Map data = await callApi('deezer.pagePlaylist', params: {
'playlist_id': id, 'playlist_id': id,
'lang': 'us', 'lang': settings.deezerLanguage??'en',
'nb': nb, 'nb': nb,
'tags': true, 'tags': true,
'start': start 'start': start
@ -157,7 +157,7 @@ class DeezerAPI {
Future<Playlist> playlist(String id, {int nb = 100}) async { Future<Playlist> playlist(String id, {int nb = 100}) async {
Map<dynamic, dynamic> data = await callApi('deezer.pagePlaylist', params: { Map<dynamic, dynamic> data = await callApi('deezer.pagePlaylist', params: {
'playlist_id': id, 'playlist_id': id,
'lang': 'us', 'lang': settings.deezerLanguage??'en',
'nb': nb, 'nb': nb,
'tags': true, 'tags': true,
'start': 0 'start': 0
@ -167,6 +167,10 @@ class DeezerAPI {
//Get playlist with all tracks //Get playlist with all tracks
Future<Playlist> fullPlaylist(String id) async { Future<Playlist> fullPlaylist(String id) async {
return await playlist(id, nb: 100000);
//OLD WORKAROUND
/*
Playlist p = await playlist(id, nb: 200); Playlist p = await playlist(id, nb: 200);
for (int i=200; i<p.trackCount; i++) { for (int i=200; i<p.trackCount; i++) {
//Get another page of tracks //Get another page of tracks
@ -176,6 +180,7 @@ class DeezerAPI {
continue; continue;
} }
return p; return p;
*/
} }
//Add track to favorites //Add track to favorites
@ -305,7 +310,7 @@ class DeezerAPI {
"large-card": ["album", "playlist", "show", "video-link"], "large-card": ["album", "playlist", "show", "video-link"],
"ads": [] //Nope "ads": [] //Nope
}, },
"LANG": "us", "LANG": settings.deezerLanguage??'en',
"OPTIONS": [] "OPTIONS": []
})); }));
return HomePage.fromPrivateJson(data['results']); return HomePage.fromPrivateJson(data['results']);
@ -336,7 +341,7 @@ class DeezerAPI {
"large-card": ["album", "playlist", "show", "video-link"], "large-card": ["album", "playlist", "show", "video-link"],
"ads": [] //Nope "ads": [] //Nope
}, },
"LANG": "us", "LANG": settings.deezerLanguage??'en',
"OPTIONS": [] "OPTIONS": []
})); }));
return HomePage.fromPrivateJson(data['results']); return HomePage.fromPrivateJson(data['results']);

View file

@ -186,10 +186,12 @@ class Album {
int fans; int fans;
bool offline; //If the album is offline, or just saved in db as metadata bool offline; //If the album is offline, or just saved in db as metadata
bool library; bool library;
//TODO: Not in DB //TODO: Not in DB
AlbumType type; AlbumType type;
String releaseDate;
Album({this.id, this.title, this.art, this.artists, this.tracks, this.fans, this.offline, this.library, this.type}); Album({this.id, this.title, this.art, this.artists, this.tracks, this.fans, this.offline, this.library, this.type, this.releaseDate});
String get artistString => artists.map<String>((art) => art.name).join(', '); String get artistString => artists.map<String>((art) => art.name).join(', ');
Duration get duration => Duration(seconds: tracks.fold(0, (v, t) => v += t.duration.inSeconds)); Duration get duration => Duration(seconds: tracks.fold(0, (v, t) => v += t.duration.inSeconds));
@ -210,7 +212,8 @@ class Album {
tracks: (songsJson['data']??[]).map<Track>((dynamic track) => Track.fromPrivateJson(track)).toList(), tracks: (songsJson['data']??[]).map<Track>((dynamic track) => Track.fromPrivateJson(track)).toList(),
fans: json['NB_FAN'], fans: json['NB_FAN'],
library: library, library: library,
type: type type: type,
releaseDate: json['DIGITAL_RELEASE_DATE']??json['PHYSICAL_RELEASE_DATE']
); );
} }
Map<String, dynamic> toSQL({off = false}) => { Map<String, dynamic> toSQL({off = false}) => {
@ -281,7 +284,7 @@ class Artist {
albums: (albumsJson['data']??[]).map<Album>((dynamic data) => Album.fromPrivateJson(data)).toList(), albums: (albumsJson['data']??[]).map<Album>((dynamic data) => Album.fromPrivateJson(data)).toList(),
topTracks: (topJson['data']??[]).map<Track>((dynamic data) => Track.fromPrivateJson(data)).toList(), topTracks: (topJson['data']??[]).map<Track>((dynamic data) => Track.fromPrivateJson(data)).toList(),
library: library, library: library,
radio: _radio radio: _radio,
); );
} }
Map<String, dynamic> toSQL({off = false}) => { Map<String, dynamic> toSQL({off = false}) => {

View file

@ -70,6 +70,7 @@ Album _$AlbumFromJson(Map<String, dynamic> json) {
offline: json['offline'] as bool, offline: json['offline'] as bool,
library: json['library'] as bool, library: json['library'] as bool,
type: _$enumDecodeNullable(_$AlbumTypeEnumMap, json['type']), type: _$enumDecodeNullable(_$AlbumTypeEnumMap, json['type']),
releaseDate: json['releaseDate'] as String,
); );
} }
@ -83,6 +84,7 @@ Map<String, dynamic> _$AlbumToJson(Album instance) => <String, dynamic>{
'offline': instance.offline, 'offline': instance.offline,
'library': instance.library, 'library': instance.library,
'type': _$AlbumTypeEnumMap[instance.type], 'type': _$AlbumTypeEnumMap[instance.type],
'releaseDate': instance.releaseDate,
}; };
T _$enumDecode<T>( T _$enumDecode<T>(

View file

@ -132,10 +132,23 @@ class DownloadManager {
} }
).catchError((e, st) async { ).catchError((e, st) async {
if (stopped) return; if (stopped) return;
print('Download error: $e\n$st');
//Catch download errors
_download = null;
_cancelNotifications = true; _cancelNotifications = true;
//Deezer error - track is unavailable
if (queue[0].state == DownloadState.DEEZER_ERROR) {
await db.rawUpdate('UPDATE downloads SET state = 4 WHERE trackId = ?', [queue[0].track.id]);
queue.removeAt(0);
_cancelNotifications = false;
_download = null;
updateQueue();
return;
}
//Clean
_download = null;
stopped = true;
print('Download error: $e\n$st');
queue[0].state = DownloadState.NONE; queue[0].state = DownloadState.NONE;
//Shift to end //Shift to end
queue.add(queue[0]); queue.add(queue[0]);
@ -442,7 +455,7 @@ class DownloadManager {
Future<List<Download>> getFinishedDownloads() async { Future<List<Download>> getFinishedDownloads() async {
//Fetch from db //Fetch from db
List<Map> data = await db.rawQuery("SELECT * FROM downloads INNER JOIN tracks ON tracks.id = downloads.trackId WHERE downloads.state = 1"); List<Map> data = await db.rawQuery("SELECT * FROM downloads INNER JOIN tracks ON tracks.id = downloads.trackId WHERE downloads.state = 1 OR downloads.state > 3");
List<Download> downloads = data.map<Download>((d) => Download.fromSQL(d, parseTrack: true)).toList(); List<Download> downloads = data.map<Download>((d) => Download.fromSQL(d, parseTrack: true)).toList();
return downloads; return downloads;
} }
@ -545,6 +558,12 @@ class Download {
try {rawTrackPublic = await deezerAPI.callPublicApi('track/${this.track.id}');} catch (e) {rawTrackPublic = {};} try {rawTrackPublic = await deezerAPI.callPublicApi('track/${this.track.id}');} catch (e) {rawTrackPublic = {};}
try {rawAlbumPublic = await deezerAPI.callPublicApi('album/${this.track.album.id}');} catch (e) {rawAlbumPublic = {};} try {rawAlbumPublic = await deezerAPI.callPublicApi('album/${this.track.album.id}');} catch (e) {rawAlbumPublic = {};}
//Global block check
if (rawTrackPublic['available_countries'] != null && rawTrackPublic['available_countries'].length == 0) {
this.state = DownloadState.DEEZER_ERROR;
throw Exception('Download error - not on Deezer');
}
//Get path if public //Get path if public
RegExp sanitize = RegExp(r'[\/\\\?\%\*\:\|\"\<\>]'); RegExp sanitize = RegExp(r'[\/\\\?\%\*\:\|\"\<\>]');
//Download path //Download path
@ -608,6 +627,10 @@ class Download {
//Download //Download
this.state = DownloadState.DOWNLOADING; this.state = DownloadState.DOWNLOADING;
//Quality fallback
if (this.url == null)
await _fallback();
//Create download file //Create download file
File downloadFile = File(this.path + '.ENC'); File downloadFile = File(this.path + '.ENC');
//Get start position //Get start position
@ -616,17 +639,15 @@ class Download {
FileStat stat = await downloadFile.stat(); FileStat stat = await downloadFile.stat();
start = stat.size; start = stat.size;
} else { } else {
//Create file if doesnt exist //Create file if doesn't exist
await downloadFile.create(recursive: true); await downloadFile.create(recursive: true);
} }
//Quality fallback
if (this.url == null)
await _fallback();
//Download //Download
_cancel = CancelToken(); _cancel = CancelToken();
Response response = await dio.get( Response response;
try {
response = await dio.get(
this.url, this.url,
options: Options( options: Options(
responseType: ResponseType.stream, responseType: ResponseType.stream,
@ -636,6 +657,14 @@ class Download {
), ),
cancelToken: _cancel cancelToken: _cancel
); );
} on DioError catch (e) {
//Deezer fetch error
if (e.response.statusCode == 403 || e.response.statusCode == 404) {
this.state = DownloadState.DEEZER_ERROR;
}
throw Exception('Download error - Deezer blocked.');
}
//Size //Size
this.total = int.parse(response.headers['Content-Length'][0]) + start; this.total = int.parse(response.headers['Content-Length'][0]) + start;
this.received = start; this.received = start;
@ -655,7 +684,6 @@ class Download {
await _outSink.close(); await _outSink.close();
_cancel = null; _cancel = null;
this.state = DownloadState.POST; this.state = DownloadState.POST;
//Decrypt //Decrypt
await platformChannel.invokeMethod('decryptTrack', {'id': track.id, 'path': path}); await platformChannel.invokeMethod('decryptTrack', {'id': track.id, 'path': path});
@ -754,21 +782,23 @@ class Download {
'trackId': track.id, 'trackId': track.id,
'path': path, 'path': path,
'url': url, 'url': url,
'state': state == DownloadState.DONE ? 1:0, 'state': state.index,
'private': private?1:0 'private': private?1:0
}; };
factory Download.fromSQL(Map<String, dynamic> data, {parseTrack = false}) => Download( factory Download.fromSQL(Map<String, dynamic> data, {parseTrack = false}) => Download(
track: parseTrack?Track.fromSQL(data):Track(id: data['trackId']), track: parseTrack?Track.fromSQL(data):Track(id: data['trackId']),
path: data['path'], path: data['path'],
url: data['url'], url: data['url'],
state: data['state'] == 1 ? DownloadState.DONE:DownloadState.NONE, state: DownloadState.values[data['state']],
private: data['private'] == 1 private: data['private'] == 1
); );
} }
enum DownloadState { enum DownloadState {
NONE, NONE,
DONE,
DOWNLOADING, DOWNLOADING,
POST, POST,
DONE DEEZER_ERROR,
ERROR
} }

View file

@ -485,7 +485,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
//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') await _player.setShuffleModeEnabled(args); if (name == 'shuffle') {
await _player.setShuffleModeEnabled(args);
}
//Android auto callback //Android auto callback
if (name == 'screenAndroidAuto' && _androidAutoCallback != null) { if (name == 'screenAndroidAuto' && _androidAutoCallback != null) {
_androidAutoCallback.complete(jsonDecode(args).map<MediaItem>((m) => MediaItem.fromJson(m)).toList()); _androidAutoCallback.complete(jsonDecode(args).map<MediaItem>((m) => MediaItem.fromJson(m)).toList());

View file

@ -180,8 +180,8 @@ const language_en_us = {
"Language changed, please restart Freezer to apply!", "Language changed, please restart Freezer to apply!",
"Importing...": "Importing...", "Importing...": "Importing...",
"Radio": "Radio", "Radio": "Radio",
"Flow": "Flow", "Flow": "Flow",
"Track is not available on Deezer!": "Track is not available on Deezer!",
"Failed to download track! Please restart.": "Failed to download track! Please restart."
} }
}; };

172
lib/languages/es_es.dart Normal file
View file

@ -0,0 +1,172 @@
/*
Translated by: ArcherDelta & PetFix
*/
const language_es_es = {
"es_es": {
"Home": "Inicio",
"Search": "Buscar",
"Library": "Biblioteca",
"Offline mode, can't play flow or smart track lists.": "Modo sin conexión, no se puede reproducir el flow o las listas de pistas inteligentes.",
"Added to library": "Agregado a la biblioteca",
"Download": "Descargar",
"Disk": "Disco",
"Offline": "Sin conexión",
"Top Tracks": "Los mejores temas",
"Show more tracks": "Mostrar más pistas",
"Top": "Top",
"Top Albums": "Mejores álbumes",
"Show all albums": "Mostrar todos los álbumes",
"Discography": "Discografía",
"Default": "Predeterminado",
"Reverse": "Invertir",
"Alphabetic": "Alfabético",
"Artist": "Artista",
"Post processing...": "Post procesamiento...",
"Done": "Hecho",
"Delete": "Eliminar",
"Are you sure you want to delete this download?": "¿Estás seguro de que quieres borrar esta descarga?",
"Cancel": "Cancelar",
"Downloads": "Descargas",
"Clear queue": "Limpiar la cola",
"This won't delete currently downloading item": "Esto no borrará el elemento que se está descargando actualmente",
"Are you sure you want to delete all queued downloads?": "¿Estás seguro de que quieres borrar todas las descargas en cola?",
"Clear downloads history": "Borrar el historial de descargas",
"WARNING: This will only clear non-offline (external downloads)": "ADVERTENCIA: Esto sólo borrará las descargas que no están en modo sin conexión (descargas externas).",
"Please check your connection and try again later...": "Por favor, compruebe su conexión y vuelva a intentarlo más tarde...",
"Show more": "Mostrar más",
"Importer": "Importador",
"Currently supporting only Spotify, with 100 tracks limit": "Actualmente sólo se soporta Spotify, con un límite de 100 pistas",
"Due to API limitations": "Debido a limitaciones de API",
"Enter your playlist link below": "Ingrese el enlace de su lista de reproducción a continuación",
"Error loading URL!": "¡Error al cargar la URL!",
"Convert": "Convertir",
"Download only": "Sólo descargar",
"Downloading is currently stopped, click here to resume.": "La descarga está actualmente detenida, haga clic aquí para reanudarla.",
"Tracks": "Pistas",
"Albums": "Álbumes",
"Artists": "Artistas",
"Playlists": "Listas de reproducción",
"Import": "Importar",
"Import playlists from Spotify": "Importar listas de reproducción de Spotify",
"Statistics": "Estadísticas",
"Offline tracks": "Pistas sin conexión",
"Offline albums": "Álbumes sin conexión",
"Offline playlists": "Listas de reproducción sin conexión",
"Offline size": "El tamaño sin conexión",
"Free space": "Espacio libre",
"Loved tracks": "Pistas favoritas",
"Favorites": "Favoritas",
"All offline tracks": "Todas las pistas fuera de línea",
"Create new playlist": "Crear nueva lista de reproducción",
"Cannot create playlists in offline mode": "No se pueden crear listas de reproducción en el modo sin conexión",
"Error": "Error",
"Error logging in! Please check your token and internet connection and try again.": "¡Error al iniciar la sesión! Por favor, compruebe su token y su conexión a Internet e inténtelo de nuevo.",
"Dismiss": "Descartar",
"Welcome to": "Bienvenido a",
"Please login using your Deezer account.": "Por favor, inicie sesión con su cuenta de Deezer.",
"Login using browser": "Ingresar usando el navegador",
"Login using token": "Ingresar usando token",
"Enter ARL": "Ingrese ARL",
"Token (ARL)": "Token (ARL)",
"Save": "Guardar",
"If you don't have account, you can register on deezer.com for free.": "Si no tienes una cuenta, puedes registrarte en deezer.com de forma gratuita.",
"Open in browser": "Abrir en el navegador",
"By using this app, you don't agree with the Deezer ToS": "Al usar esta aplicación, no está de acuerdo con las Condiciones de servicio de Deezer",
"Play next": "Reproducir siguiente",
"Add to queue": "Añadir a la cola",
"Add track to favorites": "Agregar pista a favoritos",
"Add to playlist": "Agregar a la lista de reproducción",
"Select playlist": "Seleccionar lista de reproducción",
"Track added to": "Pista agregada a",
"Remove from playlist": "Quitar de la lista de reproducción",
"Track removed from": "Pista eliminada de",
"Remove favorite": "Eliminar favorito",
"Track removed from library": "Pista eliminada de la biblioteca",
"Go to": "Ir a",
"Make offline": "Hacerlo sin conexión",
"Add to library": "Agregar a la biblioteca",
"Remove album": "Eliminar álbum",
"Album removed": "Álbum eliminado",
"Remove from favorites": "Eliminar de favoritos",
"Artist removed from library": "Artista eliminado de la biblioteca",
"Add to favorites": "Agregar a favoritos",
"Remove from library": "Eliminar de la biblioteca",
"Add playlist to library": "Agregar lista de reproducción a la biblioteca",
"Added playlist to library": "Lista de reproducción agregada a la biblioteca",
"Make playlist offline": "Hacer lista de reproducción sin conexión",
"Download playlist": "Descargar lista de reproducción",
"Create playlist": "Crear lista de reproducción",
"Title": "Título",
"Description": "Descripción",
"Private": "Privado",
"Collaborative": "Colaborativo",
"Create": "Crear",
"Playlist created!": "Lista de reproducción creada!",
"Playing from:": "Reproduciendo desde:",
"Queue": "Cola",
"Offline search": "Búsqueda sin conexión",
"Search Results": "Resultados de la búsqueda",
"No results!": "No hay resultados!",
"Show all tracks": "Mostrar todas las pistas",
"Show all playlists": "Mostrar todas las listas de reproducción",
"Settings": "Ajustes",
"General": "General",
"Appearance": "Apariencia",
"Quality": "Calidad",
"Deezer": "Deezer",
"Theme": "Tema",
"Currently": "Actualmente",
"Select theme": "Seleccione el tema",
"Light (default)": "Claro (predeterminado)",
"Dark": "Oscuro",
"Black (AMOLED)": "Negro (AMOLED)",
"Deezer (Dark)": "Deezer (oscuro)",
"Primary color": "Color primario",
"Selected color": "Color seleccionado",
"Use album art primary color": "Usar el color primario de la carátula del álbum",
"Warning: might be buggy": "Advertencia: podría tener errores",
"Mobile streaming": "Transmisión móvil",
"Wifi streaming": "Transmisión WiFi",
"External downloads": "Descargas externas",
"Content language": "Lenguaje del contenido",
"Not app language, used in headers. Now": "No es un lenguaje de la aplicación, se usa en los encabezados. Ahora",
"Select language": "Seleccione el idioma",
"Content country": "País del contenido",
"Country used in headers. Now": "País utilizado en los encabezados. Ahora",
"Log tracks": "Seguimiento de las pistas",
"Send track listen logs to Deezer, enable it for features like Flow to work properly": "Envía los registros de escucha de las pistas a Deezer, habilítalo para que funciones como Flow funcionen correctamente",
"Offline mode": "Modo sin conexión",
"Will be overwritten on start.": "Se sobrescribirá al inicio.",
"Error logging in, check your internet connections.": "Error al iniciar sesión, verifique su conexión a internet.",
"Logging in...": "Ingresando...",
"Download path": "Ruta de las descargas",
"Downloads naming": "Nombramiento de las descargas",
"Downloaded tracks filename": "Nombre de archivo de las pistas descargadas",
"Valid variables are": "Las variables válidas son",
"Reset": "Reiniciar",
"Clear": "Limpiar",
"Create folders for artist": "Crear carpetas por artista",
"Create folders for albums": "Crear carpetas por álbumes",
"Separate albums by discs": "Separar los álbumes por discos",
"Overwrite already downloaded files": "Sobrescribir los archivos ya descargados",
"Copy ARL": "Copiar ARL",
"Copy userToken/ARL Cookie for use in other apps.": "Copia el Token de usuario/Cookie ARL para su uso en otras aplicaciones.",
"Copied": "Copiado",
"Log out": "Cerrar sesión",
"Due to plugin incompatibility, login using browser is unavailable without restart.": "Debido a la incompatibilidad de los plugins, no se puede iniciar la sesión con el navegador sin reiniciar.",
"(ARL ONLY) Continue": "Continuar (SÓLO ARL)",
"Log out & Exit": "Cerrar sesión y salir",
"Pick-a-Path": "Escoja una ruta",
"Select storage": "Seleccionar el almacenamiento",
"Go up": "Subir",
"Permission denied": "Permiso denegado",
"Language": "Idioma",
"Language changed, please restart Freezer to apply!": "¡El idioma ha cambiado, por favor reinicie Freezer para aplicarlo!",
"Importing...": "Importando...",
"Radio": "Radio",
"Flow": "Flow",
}
};

190
lib/languages/fil_ph.dart Normal file
View file

@ -0,0 +1,190 @@
/*
Translated by: Chino Pacia
*/
const language_fil_ph = {
"fil_ph": {
"Home": "Home",
"Search": "Maghanap",
"Library": "Library",
"Offline mode, can't play flow or smart track lists.":
"Ikaw ay naka-offline mode, hindi maaaring maka-play ng flow o smart track.",
"Added to library": "Idinagdag sa library",
"Download": "I-download",
"Disk": "Disk",
"Offline": "Offline",
"Top Tracks": "Mga Nangungunang Track",
"Show more tracks": "Ipakita ang maraming track",
"Top": "Nangunguna",
"Top Albums": "Nangungunang mga Album",
"Show all albums": "Ipakita lahat ng album",
"Discography": "Discography",
"Default": "Default",
"Reverse": "Pabalik",
"Alphabetic": "Alphabetic",
"Artist": "Artista",
"Post processing...": "Pagtatapos ng proseso...",
"Done": "Tapos na",
"Delete": "Burahin",
"Are you sure you want to delete this download?":
"Sigurado ka bang buburahin mo itong download?",
"Cancel": "I-kansel",
"Downloads": "Mga Download",
"Clear queue": "I-clear ang queue",
"This won't delete currently downloading item":
"Hindi nito buburahin ng kasalukuyang dina-download na item",
"Are you sure you want to delete all queued downloads?":
"Sigurado ka bang buburahin mo lahat ng nakapilang mga download?",
"Clear downloads history": "I-clear ang kasaysayan ng mga download",
"WARNING: This will only clear non-offline (external downloads)":
"BABALA: Buburahin lang nito ang hindi naka-offline (eksternal na download)",
"Please check your connection and try again later...":
"Pakitignan ang iyong koneksiyon at subukan ulit mamaya...",
"Show more": "Higit pa",
"Importer": "Taga-import",
"Currently supporting only Spotify, with 100 tracks limit":
"Kasalukuyang suportado lang ang Spotify, na limitado sa 100 track",
"Due to API limitations": "Dahil sa limitasyon ng API",
"Enter your playlist link below": "Ilagay dito ang iyong link ng playlist",
"Error loading URL!": "Hindi maikarga ang URL!",
"Convert": "I-convert",
"Download only": "Download lang",
"Downloading is currently stopped, click here to resume.":
"Nahinto ang iyong download, i-click dito para magpatuloy",
"Tracks": "Mga Track",
"Albums": "Mga Album",
"Artists": "Mga Artist",
"Playlists": "Mga Playlist",
"Import": "I-import",
"Import playlists from Spotify": "I_import ang mga playlist galing sa Spotify",
"Statistics": "Statistics",
"Offline tracks": "Mga offline na track",
"Offline albums": "Mga offline na album",
"Offline playlists": "Mga offline playlist",
"Offline size": "Laki ng offline",
"Free space": "Natitirang space",
"Loved tracks": "Pinusuang mga track",
"Favorites": "Mga paborito",
"All offline tracks": "Lahat ng track na offline",
"Create new playlist": "Gumawa ng bagong playlist",
"Cannot create playlists in offline mode":
"Hindi makagagawa ng playlist habang naka-offline mode",
"Error": "Error",
"Error logging in! Please check your token and internet connection and try again.":
"Hindi maka-login! Pakitignan ang iyong token at koneksiyon at subukang muli.",
"Dismiss": "I-Dismiss",
"Welcome to": "Welcome sa",
"Please login using your Deezer account.": "Paki-login ng iyong account sa Deezer",
"Login using browser": "Mag-login gamit ng browser",
"Login using token": "Mag-login gamit ng token",
"Enter ARL": "Ilagay ang ARL",
"Token (ARL)": "Token (ARL)",
"Save": "I-Save",
"If you don't have account, you can register on deezer.com for free.":
"Kung wala kang account, maaaring mag-rehistro ng libre sa deezer.com",
"Open in browser": "Buksan sa browser",
"By using this app, you don't agree with the Deezer ToS":
"Sa paggamit ng app na ito, ikaw ay hindi sumasang-ayon sa Deezer ToS",
"Play next": "I-play ang kasunod",
"Add to queue": "Idagdag sa pila",
"Add track to favorites": "Magdagdag ng track sa mga paborito",
"Add to playlist": "Idagdag sa playlist",
"Select playlist": "Piliin ang playlist",
"Track added to": "Idinagdag ang track sa",
"Remove from playlist": "Tinanggal sa playlist",
"Track removed from": "Tinanggal ang track sa",
"Remove favorite": "Tanggalin ang paborito",
"Track removed from library": "Tinanggal ang track sa library",
"Go to": "Pumunta sa",
"Make offline": "Gawing offline",
"Add to library": "Idagdag sa library",
"Remove album": "Tanggalin ang album",
"Album removed": "Tinanggal ang album",
"Remove from favorites": "Tanggalin sa mga paborito",
"Artist removed from library": "Tinanggal ang artist sa library",
"Add to favorites": "Idagdag sa mga paborito",
"Remove from library": "Tanggalin sa library",
"Add playlist to library": "Idagdag ang playlist sa library",
"Added playlist to library": "Idinagdag ang playlist sa library",
"Make playlist offline": "Gawing offline ang playlist",
"Download playlist": "I-download ang playlist",
"Create playlist": "Gumawa ng playlist",
"Title": "Pamagat",
"Description": "Deskripsiyon",
"Private": "Pribado",
"Collaborative": "Kolaboratib",
"Create": "Gawin",
"Playlist created!": "Nagawa na ang playlist!",
"Playing from:": "Nagpa-play galing sa:",
"Queue": "Pila",
"Offline search": "Offline na paghahanap",
"Search Results": "Resulta sa Paghahanap",
"No results!": "Walang maipakitang resulta!",
"Show all tracks": "Ipakita lahat ng mga track",
"Show all playlists": "Ipakita lahat ng mga playlist",
"Settings": "Mga Setting",
"General": "Pangkalahatan",
"Appearance": "Itsura",
"Quality": "Kalidad",
"Deezer": "Deezer",
"Theme": "Tema",
"Currently": "Ngayon",
"Select theme": "Piliin ang Tema",
"Light (default)": "Puti (Default)",
"Dark": "Dark",
"Black (AMOLED)": "Maitim (AMOLED)",
"Deezer (Dark)": "Deezer (Madilim)",
"Primary color": "Pangunahing kulay",
"Selected color": "Piniling kulay",
"Use album art primary color": "Gamitin ang art ng album para sa pangunahing kulay",
"Warning: might be buggy": "Babala: Maaaring magkaroon ng palagiang bug",
"Mobile streaming": "Pag-stream gamit ng mobile",
"Wifi streaming": "Pag-stream gamit ng Wifi",
"External downloads": "Eksternal na download",
"Content language": "Wika ng nilalaman",
"Not app language, used in headers. Now":
"gagamitin ang wika sa mga header, hindi sa app. Ngayon",
"Select language": "Piliin ang wika",
"Content country": "Bansa ng nilalaman",
"Country used in headers. Now": "Gagamitin ang bansa sa mga header. Ngayon",
"Log tracks": "Log ng mga track",
"Send track listen logs to Deezer, enable it for features like Flow to work properly":
"Ipadala ang mga log ng pinakinggang mga track sa Deezer, paganahin ito para sa mga feature kagaya ng Flow para mai-ayos",
"Offline mode": "Offline mode",
"Will be overwritten on start.": "Sasapawan sa simula pa lang.",
"Error logging in, check your internet connections.":
"Nagkaroon ng problema sa pag-login. Pakitignan ang iyong koneksiyon.",
"Logging in...": "Nagla-login...",
"Download path": "Paglalagyan ng download",
"Downloads naming": "Pagpangalan sa download",
"Downloaded tracks filename": "Pangalan ng file sa mga nadownload na track",
"Valid variables are": "Ang mga pwede lang ay",
"Reset": "I-Reset",
"Clear": "I-Clear",
"Create folders for artist": "Gumawa ng folder para sa mga artista",
"Create folders for albums": "Gumawa ng folder para sa mga album",
"Separate albums by discs": "Ihiwalay ang mga album base sa disk",
"Overwrite already downloaded files": "Sapawan ang mga file na nai-download",
"Copy ARL": "Kopyahin ang ARL",
"Copy userToken/ARL Cookie for use in other apps.":
"Kopyahin ang userToken/ARL Cookie para gamitin sa iba pang app.",
"Copied": "Nakopya na",
"Log out": "Mag-Log out",
"Due to plugin incompatibility, login using browser is unavailable without restart.":
"Hindi ka makakapag-login gamit ng browser kung hindi mo ito ire-restart dahil hindi pa maayos ang ginagamit na plugin sa ngayon",
"(ARL ONLY) Continue": "(ARL LANG) Ituloy",
"Log out & Exit": "Mag-Log out at Lumabas",
"Pick-a-Path": "Pumili-ng-Path",
"Select storage": "Piliin ang storage",
"Go up": "Pumunta sa itaas",
"Permission denied": "Hindi pinapayagan",
"Language": "Wika",
"Language changed, please restart Freezer to apply!":
"Pinalitan ang wika, pakibuksan muli ang Freezer para magamit!",
"Importing...": "Ini-import...",
"Radio": "Radyo",
"Flow": "Flow",
}
};

View file

@ -9,11 +9,12 @@ const language_ru_ru = {
"Home": "Главная", "Home": "Главная",
"Search": "Поиск", "Search": "Поиск",
"Library": "Библиотека", "Library": "Библиотека",
"Offline mode, can't play flow or smart track lists.": "Офлайн режим, нельзя воспроизводить потоки или умные списки треков.", "Offline mode, can't play flow or smart track lists.":
"Автономный режим, нельзя воспроизводить потоки или умные списки треков.",
"Added to library": "Добавить в библиотеку", "Added to library": "Добавить в библиотеку",
"Download": "Скачать", "Download": "Скачать",
"Disk": "Диск", "Disk": "Disk",
"Offline": "Скачанные треки", "Offline": "Офлайн",
"Top Tracks": "Лучшие треки", "Top Tracks": "Лучшие треки",
"Show more tracks": "Показать больше треков", "Show more tracks": "Показать больше треков",
"Top": "Top", "Top": "Top",
@ -23,16 +24,17 @@ const language_ru_ru = {
"Default": "По умолчанию", "Default": "По умолчанию",
"Reverse": "Обратный", "Reverse": "Обратный",
"Alphabetic": "По алфавиту", "Alphabetic": "По алфавиту",
"Artist": "Исполнитель", "Artist": "Артист",
"Post processing...": "Постобработка...", "Post processing...": "Постобработка...",
"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": "Очистить историю загрузок",
@ -42,18 +44,20 @@ const language_ru_ru = {
"Пожалуйста, проверьте ваше соединение и повторите попытку позже...", "Пожалуйста, проверьте ваше соединение и повторите попытку позже...",
"Show more": "Показать больше", "Show more": "Показать больше",
"Importer": "Импортер", "Importer": "Импортер",
"Currently supporting only Spotify, with 100 tracks limit": "В настоящее время поддерживается только Spotify, с ограничением 100 треков", "Currently supporting only Spotify, with 100 tracks limit":
"В настоящее время поддерживается только 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": "Альбомы", "Albums": "Альбомы",
"Artists": "Артисты", "Artists": "Артисты",
"Playlists": "Плейлисты", "Playlists": "Плейлисты",
"Import": "Импорт", "Import": "Import",
"Import playlists from Spotify": "Импортировать плейлисты из Spotify", "Import playlists from Spotify": "Импортировать плейлисты из Spotify",
"Statistics": "Статистика", "Statistics": "Статистика",
"Offline tracks": "Автономные треки", "Offline tracks": "Автономные треки",
@ -63,9 +67,10 @@ const language_ru_ru = {
"Free space": "Свободное место", "Free space": "Свободное место",
"Loved tracks": "Любимые треки", "Loved tracks": "Любимые треки",
"Favorites": "Избранное", "Favorites": "Избранное",
"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.":
"Ошибка входа! Проверьте свой токен и подключение к Интернету и повторите попытку.", "Ошибка входа! Проверьте свой токен и подключение к Интернету и повторите попытку.",
@ -122,38 +127,39 @@ const language_ru_ru = {
"Show all playlists": "Показать все плейлисты", "Show all playlists": "Показать все плейлисты",
"Settings": "Настройки", "Settings": "Настройки",
"General": "Общее", "General": "Общее",
"Appearance": "Интерфейс", "Appearance": "Внешность",
"Quality": "Качество звука", "Quality": "Качественный",
"Deezer": "Deezer", "Deezer": "Deezer",
"Theme": "Тема", "Theme": "Тема",
"Currently": "Выбрана тема", "Currently": "В настоящее время",
"Select theme": "Выберите тему", "Select theme": "Выберите тему",
"Light (default)": "Светлая (По умолчанию)", "Light (default)": "Светлая (По умолчанию)",
"Dark": "Dark (Темная тема)", "Dark": "Темная",
"Black (AMOLED)": "Черная (AMOLED)", "Black (AMOLED)": "Черная (AMOLED)",
"Deezer (Dark)": "Deezer (Dark)", "Deezer (Dark)": "Deezer (Dark)",
"Primary color": "Основной цвет", "Primary color": "Основной цвет",
"Selected color": "Выбранный цвет", "Selected color": "Выбранный цвет",
"Use album art primary color": "Использовать цвет обложки", "Use album art primary color": "Использовать основной цвет обложки альбома",
"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": "Именование загрузок",
"Downloaded tracks filename": "Имя файла загруженных треков", "Downloaded tracks filename": "Имя файла загруженных треков",
"Valid variables are": "Допустимые переменные:", "Valid variables are": "Допустимые переменные:",
"Reset": "Сброс", "Reset": "Сброс",
@ -175,8 +181,9 @@ const language_ru_ru = {
"Select storage": "Выберите хранилище", "Select storage": "Выберите хранилище",
"Go up": "Подниматься", "Go up": "Подниматься",
"Permission denied": "Доступ запрещен", "Permission denied": "Доступ запрещен",
"Language": "Язык приложения", "Language": "Язык",
"Language changed, please restart Freezer to apply!": "Язык изменен, перезапустите Freezer, чтобы применить!", "Language changed, please restart Freezer to apply!":
"Язык изменен, перезапустите Freezer, чтобы применить!",
"Importing...": "Импорт...", "Importing...": "Импорт...",
"Radio": "Радио" "Radio": "Радио"
} }

View file

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:freezer/languages/ar_ar.dart'; import 'package:freezer/languages/ar_ar.dart';
import 'package:freezer/languages/de_de.dart'; import 'package:freezer/languages/de_de.dart';
import 'package:freezer/languages/en_us.dart'; import 'package:freezer/languages/en_us.dart';
import 'package:freezer/languages/es_es.dart';
import 'package:freezer/languages/fil_ph.dart';
import 'package:freezer/languages/it_it.dart'; import 'package:freezer/languages/it_it.dart';
import 'package:freezer/languages/pt_br.dart'; import 'package:freezer/languages/pt_br.dart';
import 'package:freezer/languages/ru_ru.dart'; import 'package:freezer/languages/ru_ru.dart';
@ -13,12 +15,14 @@ const supportedLocales = [
const Locale('pt', 'BR'), const Locale('pt', 'BR'),
const Locale('it', 'IT'), const Locale('it', 'IT'),
const Locale('de', 'DE'), const Locale('de', 'DE'),
const Locale('ru', 'RU') const Locale('ru', 'RU'),
const Locale('es', 'ES'),
const Locale('fil', 'PH')
]; ];
extension Localization on String { extension Localization on String {
static var _t = Translations.byLocale("en_US") + static var _t = Translations.byLocale("en_US") +
language_en_us + language_ar_ar + language_pt_br + language_it_it + language_de_de + language_ru_ru; language_en_us + language_ar_ar + language_pt_br + language_it_it + language_de_de + language_ru_ru + language_fil_ph + language_es_es;
String get i18n => localize(this, _t); String get i18n => localize(this, _t);
} }

View file

@ -80,6 +80,16 @@ class AlbumDetails extends StatelessWidget {
color: Theme.of(context).primaryColor color: Theme.of(context).primaryColor
), ),
), ),
Container(height: 4.0),
if (album.releaseDate != null && album.releaseDate.length >= 4)
Text(
album.releaseDate,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12.0,
color: Theme.of(context).disabledColor
),
),
Container(height: 8.0,), Container(height: 8.0,),
], ],
), ),

View file

@ -21,6 +21,10 @@ class DownloadTile extends StatelessWidget {
return 'Post processing...'.i18n; return 'Post processing...'.i18n;
case DownloadState.DONE: case DownloadState.DONE:
return 'Done'.i18n; //Shouldn't be visible return 'Done'.i18n; //Shouldn't be visible
case DownloadState.DEEZER_ERROR:
return 'Track is not available on Deezer!'.i18n;
case DownloadState.ERROR:
return 'Failed to download track! Please restart.'.i18n;
} }
return ''; return '';
} }
@ -28,7 +32,6 @@ class DownloadTile extends StatelessWidget {
Widget get progressBar { Widget get progressBar {
switch (download.state) { switch (download.state) {
case DownloadState.DOWNLOADING: case DownloadState.DOWNLOADING:
print(download.track.id);
return LinearProgressIndicator(value: download.received / download.total); return LinearProgressIndicator(value: download.received / download.total);
case DownloadState.POST: case DownloadState.POST:
return LinearProgressIndicator(); return LinearProgressIndicator();

View file

@ -42,7 +42,6 @@ class _PlayerScreenState extends State<PlayerScreen> {
playerHelper.startService(); playerHelper.startService();
return Center(child: CircularProgressIndicator(),); return Center(child: CircularProgressIndicator(),);
} }
return OrientationBuilder( return OrientationBuilder(
builder: (context, orientation) { builder: (context, orientation) {
//Landscape //Landscape

View file

@ -83,6 +83,10 @@ class _SearchScreenState extends State<SearchScreen> {
IconButton( IconButton(
icon: Icon(Icons.clear), icon: Icon(Icons.clear),
onPressed: () { onPressed: () {
setState(() {
_suggestions = [];
_query = '';
});
_controller.clear(); _controller.clear();
}, },
), ),

View file

@ -43,6 +43,21 @@ class _SettingsScreenState extends State<SettingsScreen> {
super.initState(); super.initState();
} }
List<Map<String, String>> _languages() {
defaultLanguagesList.add({
'name': 'Filipino',
'isoCode': 'fil'
});
List<Map<String, String>> _l = supportedLocales.map<Map<String, String>>((l) {
Map _lang = defaultLanguagesList.firstWhere((lang) => lang['isoCode'] == l.languageCode);
return {
'name': _lang['name'] + ' (${l.toString()})',
'isoCode': _lang['isoCode']
};
}).toList();
return _l;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -90,11 +105,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
titlePadding: EdgeInsets.all(8.0), titlePadding: EdgeInsets.all(8.0),
title: Text('Select language'.i18n), title: Text('Select language'.i18n),
isSearchable: false, isSearchable: false,
languagesList: supportedLocales.map<Map<String, String>>((l) { languagesList: _languages(),
Map _lang = defaultLanguagesList.firstWhere((lang) => lang['isoCode'] == l.languageCode);
_lang['name'] = _lang['name'] + ' (${l.toString()})';
return _lang;
}).toList(),
onValuePicked: (Language l) async { onValuePicked: (Language l) async {
setState(() { setState(() {
Locale locale = supportedLocales.firstWhere((_l) => _l.languageCode == l.isoCode); Locale locale = supportedLocales.firstWhere((_l) => _l.languageCode == l.isoCode);
@ -407,6 +418,9 @@ class _DeezerSettingsState extends State<DeezerSettings> {
titlePadding: EdgeInsets.all(8.0), titlePadding: EdgeInsets.all(8.0),
isSearchable: true, isSearchable: true,
title: Text('Select language'.i18n), title: Text('Select language'.i18n),
languagesList: defaultLanguagesList.map<Map<String, String>>((l) => {
'isoCode': l['isoCode'], 'name': l['name'] + ' (${l["isoCode"]})'
}).toList(),
onValuePicked: (Language language) { onValuePicked: (Language language) {
setState(() => settings.deezerLanguage = language.isoCode); setState(() => settings.deezerLanguage = language.isoCode);
settings.save(); settings.save();

View file

@ -489,7 +489,7 @@ packages:
path: just_audio path: just_audio
relative: true relative: true
source: path source: path
version: "0.3.1" version: "0.4.4"
language_pickers: language_pickers:
dependency: "direct main" dependency: "direct main"
description: description:

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.4.0+1 version: 0.4.1+1
environment: environment:
sdk: ">=2.8.0 <3.0.0" sdk: ">=2.8.0 <3.0.0"