0.5.4 - Download fixes, C decryptor, gestures in full and small player, sharing, link support
This commit is contained in:
parent
af49aeb974
commit
480800857e
170 changed files with 32703 additions and 293 deletions
|
@ -38,7 +38,12 @@ class Cache {
|
|||
ArtistSortType artistSort;
|
||||
@JsonKey(defaultValue: PlaylistSortType.DEFAULT)
|
||||
PlaylistSortType libraryPlaylistSort;
|
||||
@JsonKey(defaultValue: SortType.DEFAULT)
|
||||
SortType trackSort;
|
||||
|
||||
//If download threads warning was shown
|
||||
@JsonKey(defaultValue: false)
|
||||
bool threadsWarning;
|
||||
|
||||
Cache({this.libraryTracks});
|
||||
|
||||
|
|
|
@ -28,7 +28,10 @@ Cache _$CacheFromJson(Map<String, dynamic> json) {
|
|||
ArtistSortType.DEFAULT
|
||||
..libraryPlaylistSort = _$enumDecodeNullable(
|
||||
_$PlaylistSortTypeEnumMap, json['libraryPlaylistSort']) ??
|
||||
PlaylistSortType.DEFAULT;
|
||||
PlaylistSortType.DEFAULT
|
||||
..trackSort = _$enumDecodeNullable(_$SortTypeEnumMap, json['trackSort']) ??
|
||||
SortType.DEFAULT
|
||||
..threadsWarning = json['threadsWarning'] as bool ?? false;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$CacheToJson(Cache instance) => <String, dynamic>{
|
||||
|
@ -40,6 +43,8 @@ Map<String, dynamic> _$CacheToJson(Cache instance) => <String, dynamic>{
|
|||
'artistSort': _$ArtistSortTypeEnumMap[instance.artistSort],
|
||||
'libraryPlaylistSort':
|
||||
_$PlaylistSortTypeEnumMap[instance.libraryPlaylistSort],
|
||||
'trackSort': _$SortTypeEnumMap[instance.trackSort],
|
||||
'threadsWarning': instance.threadsWarning,
|
||||
};
|
||||
|
||||
T _$enumDecode<T>(
|
||||
|
|
|
@ -513,7 +513,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
|
||||
List<AudioSource> sources = [];
|
||||
for(int i=0; i<_queue.length; i++) {
|
||||
sources.add(await _mediaItemToAudioSource(_queue[i]));
|
||||
AudioSource s = await _mediaItemToAudioSource(_queue[i]);
|
||||
if (s != null)
|
||||
sources.add(s);
|
||||
}
|
||||
|
||||
_audioSource = ConcatenatingAudioSource(children: sources);
|
||||
|
@ -530,6 +532,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
|
||||
Future<AudioSource> _mediaItemToAudioSource(MediaItem mi) async {
|
||||
String url = await _getTrackUrl(mi);
|
||||
if (url == null) return null;
|
||||
if (url.startsWith('http')) return ProgressiveAudioSource(Uri.parse(url));
|
||||
return AudioSource.uri(Uri.parse(url));
|
||||
}
|
||||
|
@ -550,6 +553,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
quality = mobileQuality;
|
||||
if (conn == ConnectivityResult.wifi) quality = wifiQuality;
|
||||
|
||||
if ((playbackDetails??[]).length < 2) return null;
|
||||
String url = 'https://dzcdn.net/?md5=${playbackDetails[0]}&mv=${playbackDetails[1]}&q=${quality.toString()}#${mediaItem.id}';
|
||||
return url;
|
||||
}
|
||||
|
@ -663,7 +667,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
|
||||
_queue.insert(index, mi);
|
||||
await AudioServiceBackground.setQueue(_queue);
|
||||
await _audioSource.insert(index, await _mediaItemToAudioSource(mi));
|
||||
AudioSource _newSource = await _mediaItemToAudioSource(mi);
|
||||
if (_newSource != null)
|
||||
await _audioSource.insert(index,_newSource);
|
||||
|
||||
_saveQueue();
|
||||
}
|
||||
|
@ -673,7 +679,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
Future onAddQueueItem(MediaItem mi) async {
|
||||
_queue.add(mi);
|
||||
await AudioServiceBackground.setQueue(_queue);
|
||||
await _audioSource.add(await _mediaItemToAudioSource(mi));
|
||||
AudioSource _newSource = await _mediaItemToAudioSource(mi);
|
||||
if (_newSource != null)
|
||||
await _audioSource.add(_newSource);
|
||||
_saveQueue();
|
||||
}
|
||||
|
||||
|
|
|
@ -226,8 +226,8 @@ const language_de_de = {
|
|||
//0.5.2 Strings:
|
||||
"Use system theme": "Systemvorgabe benutzen",
|
||||
"Light": "Heller Modus",
|
||||
|
||||
//0.5.3 Strings:
|
||||
|
||||
//0.5.3 Strings:
|
||||
"Popularity": "Beliebtheit",
|
||||
"User": "Benutzer",
|
||||
"Track count": "Anzahl der Titel",
|
||||
|
|
|
@ -228,6 +228,12 @@ const language_en_us = {
|
|||
"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."
|
||||
"If you want to use custom directory naming - use '/' as directory separator.": "If you want to use custom directory naming - use '/' as directory separator.",
|
||||
|
||||
//0.5.4 Strings:
|
||||
"Share": "Share",
|
||||
"Save album cover": "Save album cover",
|
||||
"Warning": "Warning",
|
||||
"Using too many concurrent downloads on older/weaker devices might cause crashes!": "Using too many concurrent downloads on older/weaker devices might cause crashes!"
|
||||
}
|
||||
};
|
||||
|
|
|
@ -194,7 +194,7 @@ const language_fr_fr = {
|
|||
"Track is not available on Deezer!":
|
||||
"La piste n'est pas disponible sur Deezer!",
|
||||
"Failed to download track! Please restart.":
|
||||
"Echec du téléchargement de la piste ! Veuillez réessayer."
|
||||
"Echec du téléchargement de la piste ! Veuillez réessayer.",
|
||||
|
||||
//0.5.0 Strings:
|
||||
"Storage permission denied!": "Autorisation d'accès au stockage refusée!",
|
||||
|
@ -215,23 +215,27 @@ const language_fr_fr = {
|
|||
//Updated 0.5.1
|
||||
"Download threads": "Téléchargements simultanés",
|
||||
//0.5.0
|
||||
"Lyrics unavailable, empty or failed to load!": "Paroles indisponibles, vides ou erreur de chargement !",
|
||||
"Lyrics unavailable, empty or failed to load!":
|
||||
"Paroles indisponibles, vides ou erreur de chargement !",
|
||||
"About": "A propos",
|
||||
"Telegram Channel": "Telegram Channel",
|
||||
"To get latest releases": "Pour obtenir les dernières versions de l'app",
|
||||
"Official chat": "Chat officiel",
|
||||
"Telegram Group": "Groupe Telegram",
|
||||
"Huge thanks to all the contributors! <3": "Un grand merci à tous les contributeurs ! <3",
|
||||
"Huge thanks to all the contributors! <3":
|
||||
"Un grand merci à tous les contributeurs ! <3",
|
||||
"Edit playlist": "Modifier la playlist",
|
||||
"Update": "Mettre à jour",
|
||||
"Playlist updated!": "Playlist mise à jour !",
|
||||
"Downloads added!": "Téléchargements ajoutés !",
|
||||
|
||||
//0.5.1 Strings:
|
||||
"Save cover file for every track": "Sauvegarder la pochette pour chaque piste",
|
||||
"Save cover file for every track":
|
||||
"Sauvegarder la pochette pour chaque piste",
|
||||
"Download Log": "Journal des téléchargements",
|
||||
"Repository": "Dépôt",
|
||||
"Source code, report issues there.": "Code source, signaler les problèmes ici.",
|
||||
"Source code, report issues there.":
|
||||
"Code source, signaler les problèmes ici.",
|
||||
|
||||
//0.5.2 Strings:
|
||||
"Use system theme": "Utiliser le thème du système",
|
||||
|
@ -241,6 +245,7 @@ const language_fr_fr = {
|
|||
"Popularity": "Popularité",
|
||||
"User": "Utilisateur",
|
||||
"Track count": "Nombre de pistes",
|
||||
"If you want to use custom directory naming - use '/' as directory separator.": "Si vous souhaitez utiliser un nom de répertoire personnalisé, utilisez '/' comme séparateur."
|
||||
"If you want to use custom directory naming - use '/' as directory separator.":
|
||||
"Si vous souhaitez utiliser un nom de répertoire personnalisé, utilisez '/' comme séparateur."
|
||||
}
|
||||
};
|
||||
|
|
239
lib/languages/id_id.dart
Normal file
239
lib/languages/id_id.dart
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
|
||||
Translated by: LenteraMalam
|
||||
|
||||
*/
|
||||
|
||||
const language_id_id = {
|
||||
"id_id": {
|
||||
"Home": "Beranda",
|
||||
"Search": "Cari",
|
||||
"Library": "Perpustakaan",
|
||||
"Offline mode, can't play flow or smart track lists.":
|
||||
"Mode offline, tidak dapat memutar aliran atau daftar putar pintar.",
|
||||
"Added to library": "Ditambahkan ke Perpustakaan",
|
||||
"Download": "Unduh",
|
||||
"Disk": "Disk",
|
||||
"Offline": "Offline",
|
||||
"Top Tracks": "Lagu Populer",
|
||||
"Show more tracks": "Tampilkan lebih banyak lagu",
|
||||
"Top": "Populer",
|
||||
"Top Albums": "Album Populer",
|
||||
"Show all albums": "Tampilkan semua album",
|
||||
"Discography": "Diskografi",
|
||||
"Default": "Default",
|
||||
"Reverse": "Membalik",
|
||||
"Alphabetic": "Alfabet",
|
||||
"Artist": "Artis",
|
||||
"Post processing...": "Sedang diproses...",
|
||||
"Done": "Selesai",
|
||||
"Delete": "Hapus",
|
||||
"Are you sure you want to delete this download?":
|
||||
"Apakah kamu yakin ingin menghapus unduhan ini?",
|
||||
"Cancel": "Batalkan",
|
||||
"Downloads": "Unduhan",
|
||||
"Clear queue": "Bersihkan antrean",
|
||||
"This won't delete currently downloading item":
|
||||
"Ini tidak akan menghapus item yang sedang diunduh",
|
||||
"Are you sure you want to delete all queued downloads?":
|
||||
"Apakah kamu yakin ingin menghapus semua antrean yang terunduh?",
|
||||
"Clear downloads history": "Bersihkan riwayat unduhan",
|
||||
"WARNING: This will only clear non-offline (external downloads)":
|
||||
"PERINGATAN: Ini hanya akan menghapus non-offline (unduhan eksternal)",
|
||||
"Please check your connection and try again later...":
|
||||
"Periksa kembali koneksi internet anda dan ulangi kembali...",
|
||||
"Show more": "Tampilkan lebih banyak",
|
||||
"Importer": "Pengimport",
|
||||
"Currently supporting only Spotify, with 100 tracks limit":
|
||||
"Saat ini hanya mendukung Spotify, dengan batas 100 lagu",
|
||||
"Due to API limitations": "Karena keterbatasan API",
|
||||
"Enter your playlist link below": "Masukkan link playlist Anda di bawah ini",
|
||||
"Error loading URL!": "Gagal memuat URL!",
|
||||
"Convert": "Konversikan",
|
||||
"Download only": "Hanya mengunduh",
|
||||
"Downloading is currently stopped, click here to resume.":
|
||||
"Pengunduhan saat ini dihentikan, klik di sini untuk melanjutkan.",
|
||||
"Tracks": "Lagu",
|
||||
"Albums": "Album",
|
||||
"Artists": "Artis",
|
||||
"Playlists": "Daftar Putar",
|
||||
"Import": "Impo",
|
||||
"Import playlists from Spotify": "Impor playlist dari Spotify",
|
||||
"Statistics": "Statistik",
|
||||
"Offline tracks": "Lagu offline",
|
||||
"Offline albums": "Album offline",
|
||||
"Offline playlists": "Daftar putar offline",
|
||||
"Offline size": "Ukuran offline",
|
||||
"Free space": "Penyimpanan tersedia",
|
||||
"Loved tracks": "Lagu yang disukai",
|
||||
"Favorites": "Favorit",
|
||||
"All offline tracks": "Semua lagu offline",
|
||||
"Create new playlist": "Buat daftar putar baru",
|
||||
"Cannot create playlists in offline mode":
|
||||
"Tidak dapat membuat daftar putar di mode offline",
|
||||
"Error": "Terjadi kesalahan",
|
||||
"Error logging in! Please check your token and internet connection and try again.":
|
||||
"Kesalahan saat masuk! Periksa token dan koneksi internet Anda, lalu coba lagi.",
|
||||
"Dismiss": "Abaikan",
|
||||
"Welcome to": "Selamat datang di",
|
||||
"Please login using your Deezer account.":
|
||||
"Silakan masuk menggunakan akun Deezer Anda.",
|
||||
"Login using browser": "Masuk menggunakan browser",
|
||||
"Login using token": "Masuk menggunakan token",
|
||||
"Enter ARL": "Masukkan ARL",
|
||||
"Token (ARL)": "Token (ARL)",
|
||||
"Save": "Simpan",
|
||||
"If you don't have account, you can register on deezer.com for free.":
|
||||
"Jika Anda tidak memiliki akun, Anda dapat mendaftar di deezer.com secara gratis.",
|
||||
"Open in browser": "Buka di browser",
|
||||
"By using this app, you don't agree with the Deezer ToS":
|
||||
"Dengan menggunakan aplikasi ini, Anda tidak setuju dengan ToS Deezer",
|
||||
"Play next": "Putar selanjutnya",
|
||||
"Add to queue": "Tambahkan ke antrean",
|
||||
"Add track to favorites": "Tambahkan lagu ke favorit",
|
||||
"Add to playlist": "Tambahkan ke daftar putar",
|
||||
"Select playlist": "Pilih daftar putar",
|
||||
"Track added to": "Lagu ditamhahkan ke",
|
||||
"Remove from playlist": "Hapus dari daftar putar",
|
||||
"Track removed from": "Lagu dihapus dari",
|
||||
"Remove favorite": "Hapus favorit",
|
||||
"Track removed from library": "Lagu dihapus dari perpustakaan",
|
||||
"Go to": "Pergi ke",
|
||||
"Make offline": "Buat offline",
|
||||
"Add to library": "Tambahkan ke perpustakaan",
|
||||
"Remove album": "Hapus album",
|
||||
"Album removed": "Album dihapus",
|
||||
"Remove from favorites": "Hapus dari favorit",
|
||||
"Artist removed from library": "Artis dihapus dari perpustakaan",
|
||||
"Add to favorites": "Tambahkan ke favorit",
|
||||
"Remove from library": "Hapus dari perpustakaan",
|
||||
"Add playlist to library": "Tambahkan daftar putar ke perpustakaan",
|
||||
"Added playlist to library": "Menambahkan daftar putar ke perpustakaan",
|
||||
"Make playlist offline": "Buat daftar putar offline",
|
||||
"Download playlist": "Unduh daftar putar",
|
||||
"Create playlist": "Buat daftar putar",
|
||||
"Title": "Judul",
|
||||
"Description": "Deskripsi",
|
||||
"Private": "Pribadi",
|
||||
"Collaborative": "Kolaboratif",
|
||||
"Create": "Buat",
|
||||
"Playlist created!": "Daftar putar berhasil dibuat!",
|
||||
"Playing from:": "Memainkan:",
|
||||
"Queue": "Antrean",
|
||||
"Offline search": "Pencarian offline",
|
||||
"Search Results": "Hasil perncarian",
|
||||
"No results!": "Hasil tidak ditemukan!",
|
||||
"Show all tracks": "Tampilkan semua lagu",
|
||||
"Show all playlists": "Tampilkan semua daftar putar",
|
||||
"Settings": "Pengaturan",
|
||||
"General": "Umum",
|
||||
"Appearance": "Tampilan",
|
||||
"Quality": "Kualitas",
|
||||
"Deezer": "Deezer",
|
||||
"Theme": "Tema",
|
||||
"Currently": "Saat ini",
|
||||
"Select theme": "Pilih tema",
|
||||
"Dark": "Gelap",
|
||||
"Black (AMOLED)": "Hitam (AMOLED)",
|
||||
"Deezer (Dark)": "Deezer (Gelap)",
|
||||
"Primary color": "Warna utama",
|
||||
"Selected color": "Warna yang dipilih",
|
||||
"Use album art primary color": "Gunakan foto album sebagai warna utama",
|
||||
"Warning: might be buggy": "Peringatan: masih ada bug",
|
||||
"Mobile streaming": "Mobile streaming",
|
||||
"Wifi streaming": "Wifi streaming",
|
||||
"External downloads": "Unduhan eksternal",
|
||||
"Content language": "Bahasa konten",
|
||||
"Not app language, used in headers. Now":
|
||||
"Bukan bahasa aplikasi, digunakan di header. Digunakan",
|
||||
"Select language": "Pilih bahasa",
|
||||
"Content country": "Wilayah konten",
|
||||
"Country used in headers. Now": "Negara digunakan di header. Digunakan",
|
||||
"Log tracks": "Catatan lagu",
|
||||
"Send track listen logs to Deezer, enable it for features like Flow to work properly":
|
||||
"Kirim catatan mendengarkan lagu ke Deezer, aktifkan agar fitur seperti Flow berfungsi dengan benar",
|
||||
"Offline mode": "Mode offline",
|
||||
"Will be overwritten on start.": "Akan ditimpa saat mulai.",
|
||||
"Error logging in, check your internet connections.":
|
||||
"Kesalahan saat masuk, periksa koneksi internet Anda.",
|
||||
"Logging in...": "Masuk...",
|
||||
"Download path": "Path unduhan",
|
||||
"Downloads naming": "Penamaan unduhan",
|
||||
"Downloaded tracks filename": "Nama file yang diunduh",
|
||||
"Valid variables are": "Variabel yang valid",
|
||||
"Reset": "Atur ulang",
|
||||
"Clear": "Bersihkan",
|
||||
"Create folders for artist": "Buat folder dari artis",
|
||||
"Create folders for albums": "Buat folder dari album",
|
||||
"Separate albums by discs": "Pisahkan album dengan disk",
|
||||
"Overwrite already downloaded files": "Timpa file yang sudah diunduh",
|
||||
"Copy ARL": "Salin ARL",
|
||||
"Copy userToken/ARL Cookie for use in other apps.":
|
||||
"Salin Token/ARL Cookie untuk digunakan di apps lain.",
|
||||
"Copied": "Tersalin",
|
||||
"Log out": "Keluar",
|
||||
"Due to plugin incompatibility, login using browser is unavailable without restart.":
|
||||
"Karena ketidakcocokan plugin, masuk menggunakan browser tidak tersedia tanpa restart.",
|
||||
"(ARL ONLY) Continue": "(HANYA ARL) Lanjutkan",
|
||||
"Log out & Exit": "Keluar",
|
||||
"Pick-a-Path": "Pilih-sebuah-Jalur",
|
||||
"Select storage": "Pilih penyimpanan",
|
||||
"Go up": "Naik",
|
||||
"Permission denied": "Akses dilarang",
|
||||
"Language": "Bahasa",
|
||||
"Language changed, please restart Freezer to apply!":
|
||||
"Bahasa diganti, Mulai ulang aplikasi untuk menerapkannya!",
|
||||
"Importing...": "Mengimpor...",
|
||||
"Radio": "Radio",
|
||||
"Flow": "Flow",
|
||||
"Track is not available on Deezer!": "Lagu tidak tersedia di Deezer!",
|
||||
"Failed to download track! Please restart.": "Gagal untuk mengunduh lagu! Ulangi kembali.",
|
||||
|
||||
//0.5.0 Strings:
|
||||
"Storage permission denied!": "Izin penyimpanan ditolak!",
|
||||
"Failed": "Gagal",
|
||||
"Queued": "Dalam antrean",
|
||||
//Updated in 0.5.1 - used in context of download:
|
||||
"External": "Penyimpanan",
|
||||
//0.5.0
|
||||
"Restart failed downloads": "Gagal memulai ulang unduhan",
|
||||
"Clear failed": "Gagal membersihkan",
|
||||
"Download Settings": "Pengaturan unduhan",
|
||||
"Create folder for playlist": "Buat folder dari daftar putar",
|
||||
"Download .LRC lyrics": "Unduh lirik .LRC",
|
||||
"Proxy": "Proxy",
|
||||
"Not set": "Tidak diatur",
|
||||
"Search or paste URL": "Cari atau masukkan URL",
|
||||
"History": "Riwayat",
|
||||
//Updated 0.5.1
|
||||
"Download threads": "Unduh bersamaan",
|
||||
//0.5.0
|
||||
"Lyrics unavailable, empty or failed to load!": "Lirik tidak tersedia, kosong atau gagal untuk memuat!",
|
||||
"About": "Tentang",
|
||||
"Telegram Channel": "Channel Telegram",
|
||||
"To get latest releases": "Untuk mendapatkan rilisan terbaru",
|
||||
"Official chat": "Obrolan resmi",
|
||||
"Telegram Group": "Grub Telegram",
|
||||
"Huge thanks to all the contributors! <3": "Terima kasih banyak untuk semua kontributor! <3",
|
||||
"Edit playlist": "Edit daftar putar",
|
||||
"Update": "Perbarui",
|
||||
"Playlist updated!": "Daftar putar diperbarui!",
|
||||
"Downloads added!": "Unduhan ditambahkan!",
|
||||
|
||||
//0.5.1 Strings:
|
||||
"Save cover file for every track": "Simpan cover foto dari setiap lagu",
|
||||
"Download Log": "Catatan unduhan",
|
||||
"Repository": "Repository",
|
||||
"Source code, report issues there.": "Kode sumber, laporkan masalah disini.",
|
||||
|
||||
//0.5.2 Strings:
|
||||
"Use system theme": "Gunakan tema sistem",
|
||||
"Light": "Cerah",
|
||||
|
||||
//0.5.3 Strings:
|
||||
"Popularity": "Popularitas",
|
||||
"User": "Pengguna",
|
||||
"Track count": "Jumlah lagu",
|
||||
"If you want to use custom directory naming - use '/' as directory separator.": "Jika Anda ingin menggunakan penamaan direktori kustom - gunakan '/' sebagai pemisah direktori."
|
||||
}
|
||||
};
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:custom_navigator/custom_navigator.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -11,6 +13,7 @@ import 'package:freezer/ui/search.dart';
|
|||
import 'package:i18n_extension/i18n_widget.dart';
|
||||
import 'package:move_to_background/move_to_background.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:uni_links/uni_links.dart';
|
||||
|
||||
import 'api/deezer.dart';
|
||||
import 'api/download.dart';
|
||||
|
@ -42,6 +45,7 @@ class FreezerApp extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _FreezerAppState extends State<FreezerApp> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
//Make update theme global
|
||||
|
@ -144,16 +148,42 @@ class MainScreen extends StatefulWidget {
|
|||
_MainScreenState createState() => _MainScreenState();
|
||||
}
|
||||
|
||||
class _MainScreenState extends State<MainScreen> {
|
||||
class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateMixin{
|
||||
List<Widget> _screens = [HomeScreen(), SearchScreen(), LibraryScreen()];
|
||||
int _selected = 0;
|
||||
StreamSubscription _urlLinkStream;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
//Setup URLs
|
||||
setupUniLinks();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_urlLinkStream != null)
|
||||
_urlLinkStream.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void setupUniLinks() async {
|
||||
//Listen to URLs
|
||||
_urlLinkStream = getUriLinksStream().listen((Uri uri) {
|
||||
openScreenByURL(context, uri.toString());
|
||||
}, onError: (err) {});
|
||||
//Get initial link on cold start
|
||||
try {
|
||||
String link = await getInitialLink();
|
||||
if (link != null && link.length > 4)
|
||||
openScreenByURL(context, link);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
|
@ -60,6 +60,8 @@ class Settings {
|
|||
bool downloadLyrics;
|
||||
@JsonKey(defaultValue: false)
|
||||
bool trackCover;
|
||||
@JsonKey(defaultValue: true)
|
||||
bool albumCover;
|
||||
|
||||
//Appearance
|
||||
@JsonKey(defaultValue: Themes.Dark)
|
||||
|
@ -111,7 +113,8 @@ class Settings {
|
|||
"overwriteDownload": overwriteDownload,
|
||||
"downloadLyrics": downloadLyrics,
|
||||
"trackCover": trackCover,
|
||||
"arl": arl
|
||||
"arl": arl,
|
||||
"albumCover": albumCover
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) {
|
|||
..playlistFolder = json['playlistFolder'] as bool ?? false
|
||||
..downloadLyrics = json['downloadLyrics'] as bool ?? true
|
||||
..trackCover = json['trackCover'] as bool ?? false
|
||||
..albumCover = json['albumCover'] as bool ?? true
|
||||
..theme =
|
||||
_$enumDecodeNullable(_$ThemesEnumMap, json['theme']) ?? Themes.Dark
|
||||
..useSystemTheme = json['useSystemTheme'] as bool ?? false
|
||||
|
@ -62,6 +63,7 @@ Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
|||
'playlistFolder': instance.playlistFolder,
|
||||
'downloadLyrics': instance.downloadLyrics,
|
||||
'trackCover': instance.trackCover,
|
||||
'albumCover': instance.albumCover,
|
||||
'theme': _$ThemesEnumMap[instance.theme],
|
||||
'useSystemTheme': instance.useSystemTheme,
|
||||
'primaryColor': Settings._colorToJson(instance.primaryColor),
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:freezer/languages/fil_ph.dart';
|
|||
import 'package:freezer/languages/fr_fr.dart';
|
||||
import 'package:freezer/languages/he_il.dart';
|
||||
import 'package:freezer/languages/hr_hr.dart';
|
||||
import 'package:freezer/languages/id_id.dart';
|
||||
import 'package:freezer/languages/it_it.dart';
|
||||
import 'package:freezer/languages/ko_ko.dart';
|
||||
import 'package:freezer/languages/pt_br.dart';
|
||||
|
@ -31,6 +32,7 @@ const supportedLocales = [
|
|||
const Locale('he', 'IL'),
|
||||
const Locale('tr', 'TR'),
|
||||
const Locale('ro', 'RO'),
|
||||
const Locale('id', 'ID'),
|
||||
const Locale('fil', 'PH')
|
||||
];
|
||||
|
||||
|
@ -38,7 +40,7 @@ extension Localization on String {
|
|||
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_fil_ph + language_es_es + language_el_gr + language_hr_hr + language_ko_ko + language_fr_fr +
|
||||
language_he_il + language_tr_tr + language_ro_ro;
|
||||
language_he_il + language_tr_tr + language_ro_ro + language_id_id;
|
||||
|
||||
String get i18n => localize(this, _t);
|
||||
}
|
||||
|
|
|
@ -676,7 +676,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
|
||||
//Load tracks from api
|
||||
void _load() async {
|
||||
if (playlist.tracks.length < playlist.trackCount && !_loading) {
|
||||
if (playlist.tracks.length < (playlist.trackCount??playlist.tracks.length) && !_loading) {
|
||||
setState(() => _loading = true);
|
||||
int pos = playlist.tracks.length;
|
||||
//Get another page of tracks
|
||||
|
@ -794,7 +794,7 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
|
|||
size: 32.0,
|
||||
),
|
||||
Container(width: 8.0,),
|
||||
Text(playlist.trackCount.toString(), style: TextStyle(fontSize: 16),)
|
||||
Text((playlist.trackCount??playlist.tracks.length).toString(), style: TextStyle(fontSize: 16),)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
|
|
|
@ -208,9 +208,27 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
List<Track> tracks = [];
|
||||
List<Track> allTracks = [];
|
||||
int trackCount;
|
||||
SortType _sort = SortType.DEFAULT;
|
||||
|
||||
Playlist get _playlist => Playlist(id: deezerAPI.favoritesPlaylistId);
|
||||
|
||||
List<Track> get _sorted {
|
||||
List<Track> tcopy = List.from(tracks);
|
||||
switch (_sort) {
|
||||
case SortType.ALPHABETIC:
|
||||
tcopy.sort((a, b) => a.title.compareTo(b.title));
|
||||
return tcopy;
|
||||
case SortType.ARTIST:
|
||||
tcopy.sort((a, b) => a.artists[0].name.toLowerCase().compareTo(b.artists[0].name.toLowerCase()));
|
||||
return tcopy;
|
||||
case SortType.REVERSE:
|
||||
return tcopy.reversed.toList();
|
||||
case SortType.DEFAULT:
|
||||
default:
|
||||
return tcopy;
|
||||
}
|
||||
}
|
||||
|
||||
Future _load() async {
|
||||
//Already loaded
|
||||
if (trackCount != null && tracks.length >= trackCount) {
|
||||
|
@ -273,6 +291,22 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
}
|
||||
}
|
||||
|
||||
//Load all tracks
|
||||
Future _loadFull() async {
|
||||
if (tracks.length < (trackCount??0)) {
|
||||
Playlist p;
|
||||
try {
|
||||
p = await deezerAPI.fullPlaylist(deezerAPI.favoritesPlaylistId);
|
||||
} catch (e) {}
|
||||
if (p != null) {
|
||||
setState(() {
|
||||
tracks = p.tracks;
|
||||
trackCount = p.trackCount;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future _loadOffline() async {
|
||||
Playlist p = await downloadManager.getPlaylist(deezerAPI.favoritesPlaylistId);
|
||||
if (p != null) setState(() {
|
||||
|
@ -280,7 +314,7 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
});
|
||||
}
|
||||
|
||||
Future _loadAll() async {
|
||||
Future _loadAllOffline() async {
|
||||
List tracks = await downloadManager.allOfflineTracks();
|
||||
setState(() {
|
||||
allTracks = tracks;
|
||||
|
@ -301,10 +335,14 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
if (_scrollController.position.pixels > off) _load();
|
||||
});
|
||||
|
||||
_load();
|
||||
_sort = cache.trackSort??SortType.DEFAULT;
|
||||
|
||||
//Load all tracks
|
||||
_loadAll();
|
||||
_load();
|
||||
//Load all offline tracks
|
||||
_loadAllOffline();
|
||||
|
||||
if (_sort != SortType.DEFAULT)
|
||||
_loadFull();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
@ -312,7 +350,41 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Tracks'.i18n),),
|
||||
appBar: AppBar(
|
||||
title: Text('Tracks'.i18n),
|
||||
actions: [
|
||||
PopupMenuButton(
|
||||
child: Icon(Icons.sort, size: 32.0),
|
||||
onSelected: (SortType s) async {
|
||||
//Preload for sorting
|
||||
if (tracks.length < (trackCount??0))
|
||||
await _loadFull();
|
||||
|
||||
setState(() => _sort = s);
|
||||
cache.trackSort = s;
|
||||
await cache.save();
|
||||
},
|
||||
itemBuilder: (context) => <PopupMenuEntry<SortType>>[
|
||||
PopupMenuItem(
|
||||
value: SortType.DEFAULT,
|
||||
child: Text('Default'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.REVERSE,
|
||||
child: Text('Reverse'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.ALPHABETIC,
|
||||
child: Text('Alphabetic'.i18n),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SortType.ARTIST,
|
||||
child: Text('Artist'.i18n),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
controller: _scrollController,
|
||||
children: <Widget>[
|
||||
|
@ -352,11 +424,11 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
|||
),
|
||||
//Loved tracks
|
||||
...List.generate(tracks.length, (i) {
|
||||
Track t = tracks[i];
|
||||
Track t = (tracks.length == (trackCount??0))?_sorted[i]:tracks[i];
|
||||
return TrackTile(
|
||||
t,
|
||||
onTap: () {
|
||||
playerHelper.playFromTrackList(tracks, t.id, QueueSource(
|
||||
playerHelper.playFromTrackList((tracks.length == (trackCount??0))?_sorted:tracks, t.id, QueueSource(
|
||||
id: deezerAPI.favoritesPlaylistId,
|
||||
text: 'Favorites'.i18n,
|
||||
source: 'playlist'
|
||||
|
@ -613,6 +685,7 @@ class _LibraryArtistsState extends State<LibraryArtists> {
|
|||
artists.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
|
||||
return artists;
|
||||
}
|
||||
return artists;
|
||||
}
|
||||
|
||||
//Load data
|
||||
|
@ -750,6 +823,7 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
|||
playlists.sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()));
|
||||
return playlists;
|
||||
}
|
||||
return playlists;
|
||||
}
|
||||
|
||||
Future _load() async {
|
||||
|
@ -862,7 +936,7 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
|||
|
||||
if (_playlists != null)
|
||||
...List.generate(_playlists.length, (int i) {
|
||||
Playlist p = _sorted[i];
|
||||
Playlist p = (_sorted??[])[i];
|
||||
return PlaylistTile(
|
||||
p,
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:ffi';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
|
@ -10,6 +8,7 @@ import 'package:freezer/api/download.dart';
|
|||
import 'package:freezer/ui/details_screens.dart';
|
||||
import 'package:freezer/ui/error.dart';
|
||||
import 'package:freezer/translations.i18n.dart';
|
||||
import 'package:share/share.dart';
|
||||
|
||||
import '../api/definitions.dart';
|
||||
import 'cached_image.dart';
|
||||
|
@ -129,6 +128,7 @@ class MenuSheet {
|
|||
(cache.checkTrackFavorite(track))?removeFavoriteTrack(track, onUpdate: onRemove):addTrackFavorite(track),
|
||||
addToPlaylist(track),
|
||||
downloadTrack(track),
|
||||
shareTile('track', track.id),
|
||||
showAlbum(track.album),
|
||||
...List.generate(track.artists.length, (i) => showArtist(track.artists[i])),
|
||||
...options
|
||||
|
@ -297,6 +297,7 @@ class MenuSheet {
|
|||
album.library?removeAlbum(album, onRemove: onRemove):libraryAlbum(album),
|
||||
downloadAlbum(album),
|
||||
offlineAlbum(album),
|
||||
shareTile('album', album.id),
|
||||
...options
|
||||
]);
|
||||
}
|
||||
|
@ -363,6 +364,7 @@ class MenuSheet {
|
|||
void defaultArtistMenu(Artist artist, {List<Widget> options = const [], Function onRemove}) {
|
||||
show([
|
||||
artist.library?removeArtist(artist, onRemove: onRemove):favoriteArtist(artist),
|
||||
shareTile('artist', artist.id),
|
||||
...options
|
||||
]);
|
||||
}
|
||||
|
@ -409,6 +411,7 @@ class MenuSheet {
|
|||
playlist.library?removePlaylistLibrary(playlist, onRemove: onRemove):addPlaylistLibrary(playlist),
|
||||
addPlaylistOffline(playlist),
|
||||
downloadPlaylist(playlist),
|
||||
shareTile('playlist', playlist.id),
|
||||
if (playlist.user.id == deezerAPI.userId)
|
||||
editPlaylist(playlist, onUpdate: onUpdate),
|
||||
...options
|
||||
|
@ -508,6 +511,14 @@ class MenuSheet {
|
|||
);
|
||||
}
|
||||
|
||||
Widget shareTile(String type, String id) => ListTile(
|
||||
title: Text('Share'.i18n),
|
||||
leading: Icon(Icons.share),
|
||||
onTap: () async {
|
||||
Share.share('https://deezer.com/$type/$id');
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
void _close() => Navigator.of(context).pop();
|
||||
}
|
||||
|
|
|
@ -15,55 +15,73 @@ class PlayerBar extends StatelessWidget {
|
|||
}
|
||||
|
||||
double iconSize = 32;
|
||||
bool _gestureRegistered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder(
|
||||
stream: Stream.periodic(Duration(milliseconds: 250)),
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (AudioService.currentMediaItem == null) return Container(width: 0, height: 0,);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
color: Theme.of(context).bottomAppBarColor,
|
||||
child: ListTile(
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => PlayerScreen())),
|
||||
leading: CachedImage(
|
||||
width: 50,
|
||||
height: 50,
|
||||
url: AudioService.currentMediaItem.extras['thumb'] ?? AudioService.currentMediaItem.artUri,
|
||||
),
|
||||
title: Text(
|
||||
AudioService.currentMediaItem.displayTitle,
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
subtitle: Text(
|
||||
AudioService.currentMediaItem.displaySubtitle,
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
PrevNextButton(iconSize, prev: true, hidePrev: true,),
|
||||
PlayPauseButton(iconSize),
|
||||
PrevNextButton(iconSize)
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 3.0,
|
||||
child: LinearProgressIndicator(
|
||||
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1),
|
||||
value: progress,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
return GestureDetector(
|
||||
onHorizontalDragUpdate: (details) async {
|
||||
if (_gestureRegistered) return;
|
||||
final double sensitivity = 12.69;
|
||||
//Right swipe
|
||||
_gestureRegistered = true;
|
||||
if (details.delta.dx > sensitivity) {
|
||||
await AudioService.skipToPrevious();
|
||||
}
|
||||
//Left
|
||||
if (details.delta.dx < -sensitivity) {
|
||||
await AudioService.skipToNext();
|
||||
}
|
||||
_gestureRegistered = false;
|
||||
return;
|
||||
},
|
||||
child: StreamBuilder(
|
||||
stream: Stream.periodic(Duration(milliseconds: 250)),
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (AudioService.currentMediaItem == null) return Container(width: 0, height: 0,);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
color: Theme.of(context).bottomAppBarColor,
|
||||
child: ListTile(
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => PlayerScreen())),
|
||||
leading: CachedImage(
|
||||
width: 50,
|
||||
height: 50,
|
||||
url: AudioService.currentMediaItem.extras['thumb'] ?? AudioService.currentMediaItem.artUri,
|
||||
),
|
||||
title: Text(
|
||||
AudioService.currentMediaItem.displayTitle,
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
subtitle: Text(
|
||||
AudioService.currentMediaItem.displaySubtitle,
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 1,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
PrevNextButton(iconSize, prev: true, hidePrev: true,),
|
||||
PlayPauseButton(iconSize),
|
||||
PrevNextButton(iconSize)
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 3.0,
|
||||
child: LinearProgressIndicator(
|
||||
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1),
|
||||
value: progress,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,9 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
|||
|
||||
double iconSize = ScreenUtil().setWidth(64);
|
||||
bool _lyrics = false;
|
||||
PageController _pageController = PageController(
|
||||
initialPage: playerHelper.queueIndex,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -79,14 +82,20 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: Container(
|
||||
width: ScreenUtil().setWidth(500),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
CachedImage(
|
||||
url: AudioService.currentMediaItem.artUri,
|
||||
fullThumb: true,
|
||||
PageView(
|
||||
controller: _pageController,
|
||||
onPageChanged: (int index) {
|
||||
AudioService.skipToQueueItem(AudioService.queue[index].id);
|
||||
},
|
||||
children: List.generate(AudioService.queue.length, (i) => CachedImage(
|
||||
url: AudioService.queue[i].artUri,
|
||||
fullThumb: true,
|
||||
)),
|
||||
),
|
||||
if (_lyrics) LyricsWidget(
|
||||
artUri: AudioService.currentMediaItem.extras['thumb'],
|
||||
|
@ -96,7 +105,7 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
|||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
//Right side
|
||||
SizedBox(
|
||||
|
@ -226,6 +235,9 @@ class PlayerScreenVertical extends StatefulWidget {
|
|||
class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
||||
double iconSize = ScreenUtil().setWidth(100);
|
||||
bool _lyrics = false;
|
||||
PageController _pageController = PageController(
|
||||
initialPage: playerHelper.queueIndex,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -243,9 +255,15 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
|||
height: ScreenUtil().setHeight(1050),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
CachedImage(
|
||||
url: AudioService.currentMediaItem.artUri,
|
||||
fullThumb: true,
|
||||
PageView(
|
||||
controller: _pageController,
|
||||
onPageChanged: (int index) {
|
||||
AudioService.skipToQueueItem(AudioService.queue[index].id);
|
||||
},
|
||||
children: List.generate(AudioService.queue.length, (i) => CachedImage(
|
||||
url: AudioService.queue[i].artUri,
|
||||
fullThumb: true,
|
||||
)),
|
||||
),
|
||||
if (_lyrics) LyricsWidget(
|
||||
artUri: AudioService.currentMediaItem.extras['thumb'],
|
||||
|
@ -255,7 +273,7 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
|||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
|
||||
import 'package:fluttericon/font_awesome5_icons.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/download.dart';
|
||||
import 'package:freezer/ui/downloads_screen.dart';
|
||||
|
@ -671,8 +672,31 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
_downloadThreads = settings.downloadThreads.toDouble();
|
||||
});
|
||||
await settings.save();
|
||||
|
||||
//Prevent null
|
||||
if (val > 8 && cache.threadsWarning != true) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text('Warning'.i18n),
|
||||
content: Text('Using too many concurrent downloads on older/weaker devices might cause crashes!'.i18n),
|
||||
actions: [
|
||||
FlatButton(
|
||||
child: Text('Dismiss'.i18n),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
cache.threadsWarning = true;
|
||||
await cache.save();
|
||||
}
|
||||
}
|
||||
),
|
||||
Divider(),
|
||||
ListTile(
|
||||
title: Text('Create folders for artist'.i18n),
|
||||
leading: Container(
|
||||
|
@ -699,6 +723,20 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Create folder for playlist'.i18n),
|
||||
leading: Container(
|
||||
width: 30.0,
|
||||
child: Checkbox(
|
||||
value: settings.playlistFolder,
|
||||
onChanged: (v) {
|
||||
setState(() => settings.playlistFolder = v);
|
||||
settings.save();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
ListTile(
|
||||
title: Text('Separate albums by discs'.i18n),
|
||||
leading: Container(
|
||||
|
@ -725,19 +763,6 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Create folder for playlist'.i18n),
|
||||
leading: Container(
|
||||
width: 30.0,
|
||||
child: Checkbox(
|
||||
value: settings.playlistFolder,
|
||||
onChanged: (v) {
|
||||
setState(() => settings.playlistFolder = v);
|
||||
settings.save();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Download .LRC lyrics'.i18n),
|
||||
leading: Container(
|
||||
|
@ -751,6 +776,7 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
ListTile(
|
||||
title: Text('Save cover file for every track'.i18n),
|
||||
leading: Container(
|
||||
|
@ -764,6 +790,20 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
|||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Save album cover'.i18n),
|
||||
leading: Container(
|
||||
width: 30.0,
|
||||
child: Checkbox(
|
||||
value: settings.albumCover,
|
||||
onChanged: (v) {
|
||||
setState(() => settings.albumCover = v);
|
||||
settings.save();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
ListTile(
|
||||
title: Text('Download Log'.i18n),
|
||||
leading: Icon(Icons.sticky_note_2),
|
||||
|
@ -1055,7 +1095,8 @@ class _CreditsScreenState extends State<CreditsScreen> {
|
|||
['Fwwwwwwwwwweze', 'French'],
|
||||
['kobyrevah', 'Hebrew'],
|
||||
['HoScHaKaL', 'Turkish'],
|
||||
['MicroMihai', 'Romanian']
|
||||
['MicroMihai', 'Romanian'],
|
||||
['LenteraMalam', 'Indonesian']
|
||||
];
|
||||
|
||||
@override
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue