0.5.6 - Android Auto updates, option to disable nomedia, shuffle fix, minor fixes
This commit is contained in:
parent
11d93482ff
commit
e775e74d8e
@ -306,12 +306,15 @@ public class Deezer {
|
||||
tag.setField(FieldKey.DISC_NO, Integer.toString(publicTrack.getInt("disk_number")));
|
||||
tag.setField(FieldKey.ALBUM_ARTIST, publicAlbum.getJSONObject("artist").getString("name"));
|
||||
tag.setField(FieldKey.YEAR, publicTrack.getString("release_date").substring(0, 4));
|
||||
tag.setField(FieldKey.BPM, Integer.toString((int)publicTrack.getDouble("bpm")));
|
||||
tag.setField(FieldKey.RECORD_LABEL, publicAlbum.getString("label"));
|
||||
tag.setField(FieldKey.ISRC, publicTrack.getString("isrc"));
|
||||
tag.setField(FieldKey.BARCODE, publicAlbum.getString("upc"));
|
||||
tag.setField(FieldKey.TRACK_TOTAL, Integer.toString(publicAlbum.getInt("nb_tracks")));
|
||||
|
||||
//BPM
|
||||
if (publicTrack.has("bpm") && (int)publicTrack.getDouble("bpm") > 0)
|
||||
tag.setField(FieldKey.BPM, Integer.toString((int)publicTrack.getDouble("bpm")));
|
||||
|
||||
//Unsynced lyrics
|
||||
if (lyricsData != null) {
|
||||
try {
|
||||
|
@ -610,7 +610,8 @@ public class DownloadService extends Service {
|
||||
connection.disconnect();
|
||||
} catch (Exception ignored) {}
|
||||
//Create .nomedia to not spam gallery
|
||||
new File(parentDir, ".nomedia").createNewFile();
|
||||
if (settings.nomediaFiles)
|
||||
new File(parentDir, ".nomedia").createNewFile();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error downloading album cover! " + e.toString(), download);
|
||||
coverFile.delete();
|
||||
@ -826,19 +827,21 @@ public class DownloadService extends Service {
|
||||
boolean trackCover;
|
||||
String arl;
|
||||
boolean albumCover;
|
||||
boolean nomediaFiles;
|
||||
|
||||
private DownloadSettings(int downloadThreads, boolean overwriteDownload, boolean downloadLyrics, boolean trackCover, String arl, boolean albumCover) {
|
||||
private DownloadSettings(int downloadThreads, boolean overwriteDownload, boolean downloadLyrics, boolean trackCover, String arl, boolean albumCover, boolean nomediaFiles) {
|
||||
this.downloadThreads = downloadThreads;
|
||||
this.overwriteDownload = overwriteDownload;
|
||||
this.downloadLyrics = downloadLyrics;
|
||||
this.trackCover = trackCover;
|
||||
this.arl = arl;
|
||||
this.albumCover = albumCover;
|
||||
this.nomediaFiles = nomediaFiles;
|
||||
}
|
||||
|
||||
//Parse settings from bundle sent from UI
|
||||
static DownloadSettings fromBundle(Bundle b) {
|
||||
return new DownloadSettings(b.getInt("downloadThreads"), b.getBoolean("overwriteDownload"), b.getBoolean("downloadLyrics"), b.getBoolean("trackCover"), b.getString("arl"), b.getBoolean("albumCover"));
|
||||
return new DownloadSettings(b.getInt("downloadThreads"), b.getBoolean("overwriteDownload"), b.getBoolean("downloadLyrics"), b.getBoolean("trackCover"), b.getString("arl"), b.getBoolean("albumCover"), b.getBoolean("nomediaFiles"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,16 @@ public class MainActivity extends FlutterActivity {
|
||||
Messenger activityMessenger;
|
||||
SQLiteDatabase db;
|
||||
|
||||
//Data if started from intent
|
||||
String intentPreload;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Intent intent = getIntent();
|
||||
intentPreload = intent.getStringExtra("preload");
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||
@ -118,6 +128,7 @@ public class MainActivity extends FlutterActivity {
|
||||
bundle.putBoolean("trackCover", (boolean)call.argument("trackCover"));
|
||||
bundle.putString("arl", (String)call.argument("arl"));
|
||||
bundle.putBoolean("albumCover", (boolean)call.argument("albumCover"));
|
||||
bundle.putBoolean("nomediaFiles", (boolean)call.argument("nomediaFiles"));
|
||||
sendMessage(DownloadService.SERVICE_SETTINGS_UPDATE, bundle);
|
||||
|
||||
result.success(null);
|
||||
@ -163,6 +174,12 @@ public class MainActivity extends FlutterActivity {
|
||||
result.success(null);
|
||||
return;
|
||||
}
|
||||
//If app was started with preload info (Android Auto)
|
||||
if (call.method.equals("getPreloadInfo")) {
|
||||
result.success(intentPreload);
|
||||
intentPreload = null;
|
||||
return;
|
||||
}
|
||||
|
||||
result.error("0", "Not implemented!", "Not implemented!");
|
||||
})));
|
||||
|
@ -35,12 +35,11 @@ class DeezerAPI {
|
||||
"Connection": "keep-alive"
|
||||
};
|
||||
Future _authorizing;
|
||||
|
||||
Dio dio = Dio();
|
||||
CookieJar _cookieJar = new CookieJar();
|
||||
|
||||
//Call private api
|
||||
Future<Map<dynamic, dynamic>> callApi(String method, {Map<dynamic, dynamic> params, String gatewayInput}) async {
|
||||
Dio dio = Dio();
|
||||
|
||||
//Add headers
|
||||
dio.interceptors.add(InterceptorsWrapper(
|
||||
@ -70,7 +69,6 @@ class DeezerAPI {
|
||||
'api_token': this.token,
|
||||
'input': '3',
|
||||
'method': method,
|
||||
|
||||
//Used for homepage
|
||||
if (gatewayInput != null)
|
||||
'gateway_input': gatewayInput
|
||||
|
@ -387,17 +387,17 @@ class DownloadManager {
|
||||
Future<SearchResults> search(String query) async {
|
||||
SearchResults results = SearchResults(tracks: [], albums: [], artists: [], playlists: []);
|
||||
//Tracks
|
||||
List tracksData = await db.rawQuery('SELECT * FROM tracks WHERE offline == 1 AND title like "%$query%"');
|
||||
List tracksData = await db.rawQuery('SELECT * FROM Tracks WHERE offline == 1 AND title like "%$query%"');
|
||||
for (Map trackData in tracksData) {
|
||||
results.tracks.add(await getOfflineTrack(trackData['id']));
|
||||
}
|
||||
//Albums
|
||||
List albumsData = await db.rawQuery('SELECT (id) FROM albums WHERE offline == 1 AND title like "%$query%"');
|
||||
List albumsData = await db.rawQuery('SELECT (id) FROM Albums WHERE offline == 1 AND title like "%$query%"');
|
||||
for (Map rawAlbum in albumsData) {
|
||||
results.albums.add(await getOfflineAlbum(rawAlbum['id']));
|
||||
}
|
||||
//Playlists
|
||||
List playlists = await db.rawQuery('SELECT * FROM playlists WHERE title like "%$query%"');
|
||||
List playlists = await db.rawQuery('SELECT * FROM Playlists WHERE title like "%$query%"');
|
||||
for (Map playlist in playlists) {
|
||||
results.playlists.add(await getPlaylist(playlist['id']));
|
||||
}
|
||||
@ -428,7 +428,7 @@ class DownloadManager {
|
||||
//Album folder / with disk number
|
||||
if (settings.albumFolder) {
|
||||
if (settings.albumDiscFolder) {
|
||||
path = p.join(path, '%album%' + ' - Disk ' + (track.diskNumber??null).toString());
|
||||
path = p.join(path, '%album%' + ' - Disk ' + (track.diskNumber??1).toString());
|
||||
} else {
|
||||
path = p.join(path, '%album%');
|
||||
}
|
||||
@ -450,9 +450,9 @@ class DownloadManager {
|
||||
//Get stats for library screen
|
||||
Future<List<String>> getStats() async {
|
||||
//Get offline counts
|
||||
int trackCount = (await db.rawQuery('SELECT COUNT(*) FROM tracks WHERE offline == 1'))[0]['COUNT(*)'];
|
||||
int albumCount = (await db.rawQuery('SELECT COUNT(*) FROM albums WHERE offline == 1'))[0]['COUNT(*)'];
|
||||
int playlistCount = (await db.rawQuery('SELECT COUNT(*) FROM albums WHERE offline == 1'))[0]['COUNT(*)'];
|
||||
int trackCount = (await db.rawQuery('SELECT COUNT(*) FROM Tracks WHERE offline == 1'))[0]['COUNT(*)'];
|
||||
int albumCount = (await db.rawQuery('SELECT COUNT(*) FROM Albums WHERE offline == 1'))[0]['COUNT(*)'];
|
||||
int playlistCount = (await db.rawQuery('SELECT COUNT(*) FROM Playlists'))[0]['COUNT(*)'];
|
||||
//Free space
|
||||
double diskSpace = await DiskSpace.getFreeDiskSpace;
|
||||
//Used space
|
||||
|
@ -28,8 +28,6 @@ class PlayerHelper {
|
||||
StreamSubscription _playbackStateStreamSubscription;
|
||||
QueueSource queueSource;
|
||||
LoopMode repeatType = LoopMode.off;
|
||||
bool shuffle = false;
|
||||
|
||||
//Find queue index by id
|
||||
int get queueIndex => AudioService.queue.indexWhere((mi) => mi.id == AudioService.currentMediaItem?.id??'Random string so it returns -1');
|
||||
|
||||
@ -49,8 +47,8 @@ class PlayerHelper {
|
||||
}
|
||||
if (event['action'] == 'queueEnd') {
|
||||
//If last song is played, load more queue
|
||||
onQueueEnd();
|
||||
this.queueSource = QueueSource.fromJson(event['queueSource']);
|
||||
onQueueEnd();
|
||||
return;
|
||||
}
|
||||
//Android auto get screen
|
||||
@ -110,8 +108,7 @@ class PlayerHelper {
|
||||
}
|
||||
|
||||
Future toggleShuffle() async {
|
||||
this.shuffle = !this.shuffle;
|
||||
await AudioService.customAction('shuffle', this.shuffle);
|
||||
await AudioService.customAction('shuffle');
|
||||
}
|
||||
|
||||
//Repeat toggle
|
||||
@ -148,6 +145,7 @@ class PlayerHelper {
|
||||
Future onQueueEnd() async {
|
||||
//Flow
|
||||
if (queueSource == null) return;
|
||||
print('test');
|
||||
if (queueSource.id == 'flow') {
|
||||
List<Track> tracks = await deezerAPI.flow();
|
||||
List<MediaItem> mi = tracks.map<MediaItem>((t) => t.toMediaItem()).toList();
|
||||
@ -246,7 +244,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
|
||||
//Queue
|
||||
List<MediaItem> _queue = <MediaItem>[];
|
||||
List<int> _shuffleHistory = [];
|
||||
int _queueIndex = 0;
|
||||
ConcatenatingAudioSource _audioSource;
|
||||
|
||||
@ -294,10 +291,11 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
switch(state) {
|
||||
case ProcessingState.completed:
|
||||
//Player ended, get more songs
|
||||
AudioServiceBackground.sendCustomEvent({
|
||||
'action': 'queueEnd',
|
||||
'queueSource': (queueSource??QueueSource()).toJson()
|
||||
});
|
||||
if (_queueIndex == _queue.length - 1)
|
||||
AudioServiceBackground.sendCustomEvent({
|
||||
'action': 'queueEnd',
|
||||
'queueSource': (queueSource??QueueSource()).toJson()
|
||||
});
|
||||
break;
|
||||
case ProcessingState.ready:
|
||||
//Ready to play
|
||||
@ -320,7 +318,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
//Calculate new index
|
||||
final newIndex = _queue.indexWhere((i) => i.id == mediaId);
|
||||
if (newIndex == -1) return;
|
||||
|
||||
//Update buffering state
|
||||
_skipState = newIndex > _queueIndex
|
||||
? AudioProcessingState.skippingToNext
|
||||
@ -362,22 +359,8 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
|
||||
@override
|
||||
Future<void> onSkipToNext() async {
|
||||
//Shuffle
|
||||
if (_player.shuffleModeEnabled??false) {
|
||||
int newIndex = Random().nextInt(_queue.length-1);
|
||||
//Update state
|
||||
_skipState = newIndex > _queueIndex
|
||||
? AudioProcessingState.skippingToNext
|
||||
: AudioProcessingState.skippingToPrevious;
|
||||
|
||||
if (_shuffleHistory.length == 0) _shuffleHistory.add(_queueIndex);
|
||||
_queueIndex = newIndex;
|
||||
_shuffleHistory.add(newIndex);
|
||||
await _player.seek(Duration.zero, index: _queueIndex);
|
||||
_skipState = null;
|
||||
return;
|
||||
}
|
||||
|
||||
print('skipping');
|
||||
if (_queueIndex == _queue.length-1) return;
|
||||
//Update buffering state
|
||||
_skipState = AudioProcessingState.skippingToNext;
|
||||
_queueIndex++;
|
||||
@ -388,23 +371,10 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
|
||||
@override
|
||||
Future<void> onSkipToPrevious() async {
|
||||
if (_queueIndex == 0 && !(_player.shuffleModeEnabled??false)) return;
|
||||
if (_queueIndex == 0) return;
|
||||
//Update buffering state
|
||||
_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--;
|
||||
await _player.seekToPrevious();
|
||||
@ -582,7 +552,10 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
await this._loadQueueFile();
|
||||
//Shuffle
|
||||
if (name == 'shuffle') {
|
||||
await _player.setShuffleModeEnabled(args);
|
||||
_queue.shuffle();
|
||||
AudioServiceBackground.setQueue(_queue);
|
||||
_queueIndex = 0;
|
||||
await _loadQueue();
|
||||
}
|
||||
//Android auto callback
|
||||
if (name == 'screenAndroidAuto' && _androidAutoCallback != null) {
|
||||
|
1
lib/languages/crowdin.dart
Normal file
1
lib/languages/crowdin.dart
Normal file
File diff suppressed because one or more lines are too long
@ -234,6 +234,10 @@ const language_en_us = {
|
||||
"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!"
|
||||
"Using too many concurrent downloads on older/weaker devices might cause crashes!": "Using too many concurrent downloads on older/weaker devices might cause crashes!",
|
||||
|
||||
//0.5.6 Strings:
|
||||
"Create .nomedia files": "Create .nomedia files",
|
||||
"To prevent gallery being filled with album art": "To prevent gallery being filled with album art"
|
||||
}
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:freezer/api/cache.dart';
|
||||
import 'package:freezer/api/definitions.dart';
|
||||
import 'package:freezer/ui/library.dart';
|
||||
import 'package:freezer/ui/login_screen.dart';
|
||||
import 'package:freezer/ui/search.dart';
|
||||
@ -160,9 +161,28 @@ class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateM
|
||||
//Setup URLs
|
||||
setupUniLinks();
|
||||
|
||||
_loadPreloadInfo();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _loadPreloadInfo() async {
|
||||
String info = await DownloadManager.platform.invokeMethod('getPreloadInfo');
|
||||
if (info != null) {
|
||||
//Used if started from android auto
|
||||
|
||||
await deezerAPI.authorize();
|
||||
if (info == 'flow') {
|
||||
await playerHelper.playFromSmartTrackList(SmartTrackList(id: 'flow'));
|
||||
return;
|
||||
}
|
||||
if (info == 'favorites') {
|
||||
Playlist p = await deezerAPI.fullPlaylist(deezerAPI.favoritesPlaylistId);
|
||||
playerHelper.playFromPlaylist(p, p.tracks[0].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_urlLinkStream != null)
|
||||
|
@ -62,6 +62,8 @@ class Settings {
|
||||
bool trackCover;
|
||||
@JsonKey(defaultValue: true)
|
||||
bool albumCover;
|
||||
@JsonKey(defaultValue: false)
|
||||
bool nomediaFiles;
|
||||
|
||||
//Appearance
|
||||
@JsonKey(defaultValue: Themes.Dark)
|
||||
@ -114,7 +116,8 @@ class Settings {
|
||||
"downloadLyrics": downloadLyrics,
|
||||
"trackCover": trackCover,
|
||||
"arl": arl,
|
||||
"albumCover": albumCover
|
||||
"albumCover": albumCover,
|
||||
"nomediaFiles": nomediaFiles
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) {
|
||||
..downloadLyrics = json['downloadLyrics'] as bool ?? true
|
||||
..trackCover = json['trackCover'] as bool ?? false
|
||||
..albumCover = json['albumCover'] as bool ?? true
|
||||
..nomediaFiles = json['nomediaFiles'] as bool ?? false
|
||||
..theme =
|
||||
_$enumDecodeNullable(_$ThemesEnumMap, json['theme']) ?? Themes.Dark
|
||||
..useSystemTheme = json['useSystemTheme'] as bool ?? false
|
||||
@ -64,6 +65,7 @@ Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
||||
'downloadLyrics': instance.downloadLyrics,
|
||||
'trackCover': instance.trackCover,
|
||||
'albumCover': instance.albumCover,
|
||||
'nomediaFiles': instance.nomediaFiles,
|
||||
'theme': _$ThemesEnumMap[instance.theme],
|
||||
'useSystemTheme': instance.useSystemTheme,
|
||||
'primaryColor': Settings._colorToJson(instance.primaryColor),
|
||||
|
@ -1,20 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezer/languages/ar_ar.dart';
|
||||
import 'package:freezer/languages/de_de.dart';
|
||||
import 'package:freezer/languages/el_gr.dart';
|
||||
import 'package:freezer/languages/crowdin.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/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';
|
||||
import 'package:freezer/languages/ro_ro.dart';
|
||||
import 'package:freezer/languages/ru_ru.dart';
|
||||
import 'package:freezer/languages/tr_tr.dart';
|
||||
import 'package:i18n_extension/i18n_extension.dart';
|
||||
|
||||
const supportedLocales = [
|
||||
@ -33,14 +19,13 @@ const supportedLocales = [
|
||||
const Locale('tr', 'TR'),
|
||||
const Locale('ro', 'RO'),
|
||||
const Locale('id', 'ID'),
|
||||
const Locale('fa', 'IR'),
|
||||
const Locale('pl', 'PL'),
|
||||
const Locale('fil', 'PH')
|
||||
];
|
||||
|
||||
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_id_id;
|
||||
static var _t = Translations.byLocale("en_US") + language_en_us + crowdin;
|
||||
|
||||
String get i18n => localize(this, _t);
|
||||
}
|
||||
|
@ -72,6 +72,15 @@ class _DownloadsScreenState extends State<DownloadsScreen> {
|
||||
appBar: AppBar(
|
||||
title: Text('Downloads'.i18n),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete_sweep),
|
||||
onPressed: () async {
|
||||
await downloadManager.removeDownloads(DownloadState.ERROR);
|
||||
await downloadManager.removeDownloads(DownloadState.DEEZER_ERROR);
|
||||
await downloadManager.removeDownloads(DownloadState.DONE);
|
||||
await _load();
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon:
|
||||
Icon(downloadManager.running ? Icons.stop : Icons.play_arrow),
|
||||
|
@ -383,6 +383,7 @@ class _LibraryTracksState extends State<LibraryTracks> {
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(width: 8.0),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
@ -573,6 +574,7 @@ class _LibraryAlbumsState extends State<LibraryAlbums> {
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(width: 8.0),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
@ -745,7 +747,8 @@ class _LibraryArtistsState extends State<LibraryArtists> {
|
||||
child: Text('Popularity'.i18n),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
Container(width: 8.0),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
@ -888,7 +891,8 @@ class _LibraryPlaylistsState extends State<LibraryPlaylists> {
|
||||
child: Text('Alphabetic'.i18n),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
Container(width: 8.0),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
|
@ -30,6 +30,7 @@ class PlayerBar extends StatelessWidget {
|
||||
}
|
||||
//Left
|
||||
if (details.delta.dx < -sensitivity) {
|
||||
|
||||
await AudioService.skipToNext();
|
||||
}
|
||||
_gestureRegistered = false;
|
||||
|
@ -71,25 +71,6 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
||||
|
||||
double iconSize = ScreenUtil().setWidth(64);
|
||||
bool _lyrics = false;
|
||||
PageController _pageController = PageController(
|
||||
initialPage: playerHelper.queueIndex,
|
||||
);
|
||||
StreamSubscription _currentItemSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_currentItemSub = AudioService.currentMediaItemStream.listen((event) {
|
||||
_pageController.animateToPage(playerHelper.queueIndex, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_currentItemSub != null)
|
||||
_currentItemSub.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -103,16 +84,7 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
||||
width: ScreenUtil().setWidth(500),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
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,
|
||||
)),
|
||||
),
|
||||
BigAlbumArt(),
|
||||
if (_lyrics) LyricsWidget(
|
||||
artUri: AudioService.currentMediaItem.extras['thumb'],
|
||||
trackId: AudioService.currentMediaItem.id,
|
||||
@ -251,25 +223,6 @@ class PlayerScreenVertical extends StatefulWidget {
|
||||
class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
||||
double iconSize = ScreenUtil().setWidth(100);
|
||||
bool _lyrics = false;
|
||||
PageController _pageController = PageController(
|
||||
initialPage: playerHelper.queueIndex,
|
||||
);
|
||||
StreamSubscription _currentItemSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_currentItemSub = AudioService.currentMediaItemStream.listen((event) {
|
||||
_pageController.animateToPage(playerHelper.queueIndex, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_currentItemSub != null)
|
||||
_currentItemSub.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -287,16 +240,7 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
||||
height: ScreenUtil().setHeight(1050),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
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,
|
||||
)),
|
||||
),
|
||||
BigAlbumArt(),
|
||||
if (_lyrics) LyricsWidget(
|
||||
artUri: AudioService.currentMediaItem.extras['thumb'],
|
||||
trackId: AudioService.currentMediaItem.id,
|
||||
@ -398,6 +342,51 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
||||
}
|
||||
}
|
||||
|
||||
class BigAlbumArt extends StatefulWidget {
|
||||
@override
|
||||
_BigAlbumArtState createState() => _BigAlbumArtState();
|
||||
}
|
||||
|
||||
class _BigAlbumArtState extends State<BigAlbumArt> {
|
||||
|
||||
PageController _pageController = PageController(
|
||||
initialPage: playerHelper.queueIndex,
|
||||
);
|
||||
StreamSubscription _currentItemSub;
|
||||
bool _animationLock = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_currentItemSub = AudioService.currentMediaItemStream.listen((event) async {
|
||||
_animationLock = true;
|
||||
await _pageController.animateToPage(playerHelper.queueIndex, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
_animationLock = false;
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_currentItemSub != null)
|
||||
_currentItemSub.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PageView(
|
||||
controller: _pageController,
|
||||
onPageChanged: (int index) {
|
||||
if (_animationLock) return;
|
||||
AudioService.skipToQueueItem(AudioService.queue[index].id);
|
||||
},
|
||||
children: List.generate(AudioService.queue.length, (i) => CachedImage(
|
||||
url: AudioService.queue[i].artUri,
|
||||
fullThumb: true,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LyricsWidget extends StatefulWidget {
|
||||
@ -773,17 +762,6 @@ class QueueScreen extends StatefulWidget {
|
||||
|
||||
class _QueueScreenState extends State<QueueScreen> {
|
||||
|
||||
//Get proper icon color by theme
|
||||
Color get shuffleIconColor {
|
||||
Color og = Theme.of(context).primaryColor;
|
||||
if (og.computeLuminance() > 0.5) {
|
||||
if (playerHelper.shuffle) return Theme.of(context).primaryColorLight;
|
||||
return Colors.black;
|
||||
}
|
||||
if (playerHelper.shuffle) return Theme.of(context).primaryColorDark;
|
||||
return Colors.white;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -793,7 +771,6 @@ class _QueueScreenState extends State<QueueScreen> {
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.shuffle,
|
||||
color: shuffleIconColor
|
||||
),
|
||||
onPressed: () async {
|
||||
await playerHelper.toggleShuffle();
|
||||
|
@ -803,6 +803,20 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Create .nomedia files'.i18n),
|
||||
subtitle: Text('To prevent gallery being filled with album art'.i18n),
|
||||
leading: Container(
|
||||
width: 30.0,
|
||||
child: Checkbox(
|
||||
value: settings.nomediaFiles,
|
||||
onChanged: (v) {
|
||||
setState(() => settings.nomediaFiles = v);
|
||||
settings.save();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
ListTile(
|
||||
title: Text('Download Log'.i18n),
|
||||
|
@ -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.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 0.5.5+1
|
||||
version: 0.5.6+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.8.0 <3.0.0"
|
||||
@ -27,11 +27,11 @@ dependencies:
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
|
||||
dio: ^3.0.9
|
||||
dio: ^3.0.10
|
||||
dio_cookie_manager: ^1.0.0
|
||||
cookie_jar: ^1.0.1
|
||||
json_annotation: ^3.0.1
|
||||
path_provider: ^1.6.9
|
||||
path_provider: 1.6.10
|
||||
path: ^1.6.4
|
||||
sqflite: ^1.3.0+1
|
||||
crypto: ^2.1.4
|
||||
@ -69,7 +69,7 @@ dependencies:
|
||||
uni_links: ^0.4.0
|
||||
share: ^0.6.5+2
|
||||
|
||||
audio_session: ^0.0.7
|
||||
audio_session: ^0.0.9
|
||||
audio_service:
|
||||
path: ./audio_service
|
||||
just_audio:
|
||||
|
40
translations/crowdin.py
Normal file
40
translations/crowdin.py
Normal file
@ -0,0 +1,40 @@
|
||||
import zipfile
|
||||
import json
|
||||
|
||||
lang_crowdin = {
|
||||
'ar': 'ar_ar',
|
||||
'de': 'de_de',
|
||||
'el': 'el_gr',
|
||||
'es-ES': 'es_es',
|
||||
'fa': 'fa_ir',
|
||||
'fil': 'fil_ph',
|
||||
'fr': 'fr_fr',
|
||||
'he': 'he_il',
|
||||
'hr': 'hr_hr',
|
||||
'id': 'id_id',
|
||||
'it': 'it_id',
|
||||
'ko': 'ko_ko',
|
||||
'pt-BR': 'pt_br',
|
||||
'ro': 'ro_ro',
|
||||
'ru': 'ru_ru',
|
||||
'tr': 'tr_tr',
|
||||
'pl': 'pl_pl'
|
||||
}
|
||||
|
||||
def generate_dart():
|
||||
out = {}
|
||||
with zipfile.ZipFile('translations.zip') as zip:
|
||||
for file in zip.namelist():
|
||||
if 'freezer.json' in file:
|
||||
data = zip.open(file).read()
|
||||
lang = file.split('/')[0]
|
||||
out[lang_crowdin[lang]] = json.loads(data)
|
||||
|
||||
with open('crowdin.dart', 'w') as f:
|
||||
data = json.dumps(out, ensure_ascii=False).replace('$', '\\$')
|
||||
out = f'const crowdin = {data};'
|
||||
f.write(out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
generate_dart()
|
226
translations/freezer.json
Normal file
226
translations/freezer.json
Normal file
@ -0,0 +1,226 @@
|
||||
{
|
||||
"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",
|
||||
"Offline": "Offline",
|
||||
"Top Tracks": "Top Tracks",
|
||||
"Show more tracks": "Show more tracks",
|
||||
"Top": "Top",
|
||||
"Top Albums": "Top Albums",
|
||||
"Show all albums": "Show all albums",
|
||||
"Discography": "Discography",
|
||||
"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",
|
||||
"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",
|
||||
"Due to API limitations": "Due to API limitations",
|
||||
"Enter your playlist link below": "Enter your playlist link below",
|
||||
"Error loading URL!": "Error loading 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 playlists from Spotify": "Import playlists from Spotify",
|
||||
"Statistics": "Statistics",
|
||||
"Offline tracks": "Offline tracks",
|
||||
"Offline albums": "Offline albums",
|
||||
"Offline playlists": "Offline playlists",
|
||||
"Offline size": "Offline size",
|
||||
"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.",
|
||||
"Dismiss": "Dismiss",
|
||||
"Welcome to": "Welcome to",
|
||||
"Please login using your Deezer account.":
|
||||
"Please login using your Deezer account.",
|
||||
"Login using browser": "Login using browser",
|
||||
"Login using token": "Login using token",
|
||||
"Enter ARL": "Enter 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.",
|
||||
"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",
|
||||
"Play next": "Play next",
|
||||
"Add to queue": "Add to queue",
|
||||
"Add track to favorites": "Add track to favorites",
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Select playlist": "Select playlist",
|
||||
"Track added to": "Track added to",
|
||||
"Remove from playlist": "Remove from playlist",
|
||||
"Track removed from": "Track removed from",
|
||||
"Remove favorite": "Remove favorite",
|
||||
"Track removed from library": "Track removed from library",
|
||||
"Go to": "Go to",
|
||||
"Make offline": "Make offline",
|
||||
"Add to library": "Add to library",
|
||||
"Remove album": "Remove album",
|
||||
"Album removed": "Album removed",
|
||||
"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",
|
||||
"Added playlist to library": "Added playlist to library",
|
||||
"Make playlist offline": "Make playlist offline",
|
||||
"Download playlist": "Download playlist",
|
||||
"Create playlist": "Create playlist",
|
||||
"Title": "Title",
|
||||
"Description": "Description",
|
||||
"Private": "Private",
|
||||
"Collaborative": "Collaborative",
|
||||
"Create": "Create",
|
||||
"Playlist created!": "Playlist created!",
|
||||
"Playing from:": "Playing from:",
|
||||
"Queue": "Queue",
|
||||
"Offline search": "Offline search",
|
||||
"Search Results": "Search Results",
|
||||
"No results!": "No results!",
|
||||
"Show all tracks": "Show all tracks",
|
||||
"Show all playlists": "Show all playlists",
|
||||
"Settings": "Settings",
|
||||
"General": "General",
|
||||
"Appearance": "Appearance",
|
||||
"Quality": "Quality",
|
||||
"Deezer": "Deezer",
|
||||
"Theme": "Theme",
|
||||
"Currently": "Currently",
|
||||
"Select theme": "Select theme",
|
||||
"Dark": "Dark",
|
||||
"Black (AMOLED)": "Black (AMOLED)",
|
||||
"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 streaming",
|
||||
"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",
|
||||
"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",
|
||||
"Clear": "Clear",
|
||||
"Create folders for artist": "Create folders for artist",
|
||||
"Create folders for albums": "Create folders for albums",
|
||||
"Separate albums by discs": "Separate albums by disks",
|
||||
"Overwrite already downloaded files": "Overwrite already downloaded files",
|
||||
"Copy ARL": "Copy ARL",
|
||||
"Copy userToken/ARL Cookie for use in other apps.":
|
||||
"Copy userToken/ARL Cookie for use in other apps.",
|
||||
"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 ONLY) Continue",
|
||||
"Log out & Exit": "Log out & Exit",
|
||||
"Pick-a-Path": "Pick-a-Path",
|
||||
"Select storage": "Select storage",
|
||||
"Go up": "Go up",
|
||||
"Permission denied": "Permission denied",
|
||||
"Language": "Language",
|
||||
"Language changed, please restart Freezer to apply!":
|
||||
"Language changed, please restart Freezer to apply!",
|
||||
"Importing...": "Importing...",
|
||||
"Radio": "Radio",
|
||||
"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.",
|
||||
"Storage permission denied!": "Storage permission denied!",
|
||||
"Failed": "Failed",
|
||||
"Queued": "Queued",
|
||||
"External": "Storage",
|
||||
"Restart failed downloads": "Restart failed downloads",
|
||||
"Clear failed": "Clear failed",
|
||||
"Download Settings": "Download Settings",
|
||||
"Create folder for playlist": "Create folder for playlist",
|
||||
"Download .LRC lyrics": "Download .LRC lyrics",
|
||||
"Proxy": "Proxy",
|
||||
"Not set": "Not set",
|
||||
"Search or paste URL": "Search or paste URL",
|
||||
"History": "History",
|
||||
"Download threads": "Concurrent downloads",
|
||||
"Lyrics unavailable, empty or failed to load!": "Lyrics unavailable, empty or failed to load!",
|
||||
"About": "About",
|
||||
"Telegram Channel": "Telegram Channel",
|
||||
"To get latest releases": "To get latest releases",
|
||||
"Official chat": "Official chat",
|
||||
"Telegram Group": "Telegram Group",
|
||||
"Huge thanks to all the contributors! <3": "Huge thanks to all the contributors! <3",
|
||||
"Edit playlist": "Edit playlist",
|
||||
"Update": "Update",
|
||||
"Playlist updated!": "Playlist updated!",
|
||||
"Downloads added!": "Downloads added!",
|
||||
"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.",
|
||||
"Use system theme": "Use system theme",
|
||||
"Light": "Light",
|
||||
"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.",
|
||||
"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!",
|
||||
"Create .nomedia files": "Create .nomedia files",
|
||||
"To prevent gallery being filled with album art": "To prevent gallery being filled with album art"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user