0.5.1 - Download fixes

This commit is contained in:
exttex 2020-10-10 22:51:20 +02:00
parent 8db1223805
commit 22ceca2d9c
16 changed files with 437 additions and 91 deletions

View file

@ -103,7 +103,7 @@ class Track {
}
List<String> playbackDetails;
if (mi.extras['playbackDetails'] != null)
playbackDetails = jsonDecode(mi.extras['playbackDetails']).map<String>((e) => e.toString()).toList();
playbackDetails = (jsonDecode(mi.extras['playbackDetails'])??[]).map<String>((e) => e.toString()).toList();
return Track(
title: mi.title??mi.displayTitle,

View file

@ -1,8 +1,10 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:disk_space/disk_space.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/definitions.dart';
@ -117,6 +119,10 @@ class DownloadManager {
Batch b = db.batch();
b = await _addTrackToDB(b, track, true);
await b.commit();
//Cache art
DefaultCacheManager().getSingleFile(track.albumArt.thumb);
DefaultCacheManager().getSingleFile(track.albumArt.full);
}
//Get path
@ -136,6 +142,10 @@ class DownloadManager {
//Add to DB
if (private) {
//Cache art
DefaultCacheManager().getSingleFile(album.art.thumb);
DefaultCacheManager().getSingleFile(album.art.full);
Batch b = db.batch();
b.insert('Albums', album.toSQL(off: true), conflictAlgorithm: ConflictAlgorithm.replace);
for (Track t in album.tracks) {
@ -168,6 +178,9 @@ class DownloadManager {
b.insert('Playlists', playlist.toSQL(), conflictAlgorithm: ConflictAlgorithm.replace);
for (Track t in playlist.tracks) {
b = await _addTrackToDB(b, t, false);
//Cache art
DefaultCacheManager().getSingleFile(t.albumArt.thumb);
DefaultCacheManager().getSingleFile(t.albumArt.full);
}
await b.commit();
}
@ -410,14 +423,14 @@ class DownloadManager {
path = p.join(path, sanitize(playlistName));
if (settings.artistFolder)
path = p.join(path, sanitize(track.artistString));
path = p.join(path, '%artist%');
//Album folder / with disk number
if (settings.albumFolder) {
if (settings.albumDiscFolder) {
path = p.join(path, sanitize(track.album.title) + ' - Disk ' + track.diskNumber.toString());
path = p.join(path, '%album%' + ' - Disk ' + track.diskNumber.toString());
} else {
path = p.join(path, sanitize(track.album.title));
path = p.join(path, '%album%');
}
}
//Final path

View file

@ -166,9 +166,9 @@ const language_ar_ar = {
"Language": "اللغة",
"Language changed, please restart Freezer to apply!": "تم تغيير اللغة، الرجاء إعادة تشغيل فريزر لتطبيق!",
"Importing...": "جار الاستيراد...",
"Radio": "راديو"
//0.5.0 Strings:
"Radio": "راديو",
//0.5.0 Strings:
"Storage permission denied!": "رفض إذن التخزين!",
"Failed": "فشل",
"Queued": "في قائمة الانتظار",
@ -189,7 +189,7 @@ const language_ar_ar = {
"To get latest releases": "لتنزيل اخر اصدارات البرنامج",
"Official chat": "الدردشة الرسمية",
"Telegram Group": "مجموعة التلكرام",
"Huge thanks to all the contributors! <3": "شكرا جزيلا لجميع المساهمين! <3",
"Huge thanks to all the contributors! <3": "<3 !شكرا جزيلا لجميع المساهمين",
"Edit playlist": "تعديل قائمة التشغيل",
"Update": "تحديث",
"Playlist updated!": "تم تحديث قائمة التشغيل!",

View file

@ -188,7 +188,9 @@ const language_en_us = {
"Storage permission denied!": "Storage permission denied!",
"Failed": "Failed",
"Queued": "Queued",
"External": "External",
//Updated in 0.5.1 - used in context of download:
"External": "Storage",
//0.5.0
"Restart failed downloads": "Restart failed downloads",
"Clear failed": "Clear failed",
"Download Settings": "Download Settings",
@ -198,7 +200,9 @@ const language_en_us = {
"Not set": "Not set",
"Search or paste URL": "Search or paste URL",
"History": "History",
"Download threads": "Download threads",
//Updated 0.5.1
"Download threads": "Concurrent downloads",
//0.5.0
"Lyrics unavailable, empty or failed to load!": "Lyrics unavailable, empty or failed to load!",
"About": "About",
"Telegram Channel": "Telegram Channel",
@ -209,6 +213,12 @@ const language_en_us = {
"Edit playlist": "Edit playlist",
"Update": "Update",
"Playlist updated!": "Playlist updated!",
"Downloads added!": "Downloads added!"
"Downloads added!": "Downloads added!",
//0.5.1 Strings:
"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."
}
};

View file

@ -187,6 +187,33 @@ const language_it_it = {
"Language changed, please restart Freezer to apply!":
"Lingua cambiata, riavvia Freezer per applicare la modifica!",
"Importing...": "Importando...",
"Radio": "Radio"
"Radio": "Radio",
//0.5.0 Strings:
"Storage permission denied!": "Autorizzazione di archiviazione negata!",
"Failed": "Fallito",
"Queued": "In coda",
"External": "Esterno",
"Restart failed downloads": "Riavvia download non riusciti",
"Clear failed": "Pulisci fallito",
"Download Settings": "Scarica le impostazioni",
"Create folder for playlist": "Crea cartella per playlist",
"Download .LRC lyrics": "Scarica testi .LRC",
"Proxy": "Proxy",
"Not set": "Non impostato",
"Search or paste URL": "Cerca o incolla l'URL",
"History": "Storia",
"Download threads": "Scarica threads",
"Lyrics unavailable, empty or failed to load!": "Testi non disponibili, vuoti o caricamento non riuscito!",
"About": "Info",
"Telegram Channel": "Canale Telegram",
"To get latest releases": "Per ottenere le ultime versioni",
"Official chat": "Chat ufficiale",
"Telegram Group": "Gruppo Telegram",
"Huge thanks to all the contributors! <3": "Un enorme grazie a tutti i collaboratori! <3",
"Edit playlist": "Modifica playlist",
"Update": "Aggiorna",
"Playlist updated!": "Playlist aggiornata!",
"Downloads added!": "Download aggiunti!"
}
};

View file

@ -58,7 +58,8 @@ class Settings {
bool playlistFolder;
@JsonKey(defaultValue: true)
bool downloadLyrics;
@JsonKey(defaultValue: false)
bool trackCover;
//Appearance
@JsonKey(defaultValue: Themes.Light)
@ -152,7 +153,8 @@ class Settings {
return {
"downloadThreads": downloadThreads,
"overwriteDownload": overwriteDownload,
"downloadLyrics": downloadLyrics
"downloadLyrics": downloadLyrics,
"trackCover": trackCover
};
}

View file

@ -33,6 +33,7 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) {
..downloadThreads = json['downloadThreads'] as int ?? 2
..playlistFolder = json['playlistFolder'] as bool ?? false
..downloadLyrics = json['downloadLyrics'] as bool ?? true
..trackCover = json['trackCover'] as bool ?? false
..theme =
_$enumDecodeNullable(_$ThemesEnumMap, json['theme']) ?? Themes.Light
..primaryColor = Settings._colorFromJson(json['primaryColor'] as int)
@ -59,6 +60,7 @@ Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
'downloadThreads': instance.downloadThreads,
'playlistFolder': instance.playlistFolder,
'downloadLyrics': instance.downloadLyrics,
'trackCover': instance.trackCover,
'theme': _$ThemesEnumMap[instance.theme],
'primaryColor': Settings._colorToJson(instance.primaryColor),
'useArtColor': instance.useArtColor,

View file

@ -1,12 +1,16 @@
import 'dart:async';
import 'dart:io';
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import 'package:freezer/api/download.dart';
import 'package:freezer/translations.i18n.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'cached_image.dart';
import 'dart:async';
class DownloadsScreen extends StatefulWidget {
@override
_DownloadsScreenState createState() => _DownloadsScreenState();
@ -189,15 +193,30 @@ class DownloadTile extends StatelessWidget {
String subtitle() {
String out = '';
//Download type
if (download.private) out += 'Offline'.i18n;
else out += 'External'.i18n;
out += ' | ';
if (download.state != DownloadState.DOWNLOADING && download.state != DownloadState.POST) {
//Download type
if (download.private) out += 'Offline'.i18n;
else out += 'External'.i18n;
out += ' | ';
}
if (download.state == DownloadState.POST) {
return 'Post processing...'.i18n;
}
//Quality
if (download.quality == 9) out += 'FLAC';
if (download.quality == 3) out += 'MP3 320kbps';
if (download.quality == 1) out += 'MP3 128kbps';
//Downloading show progress
if (download.state == DownloadState.DOWNLOADING) {
out += ' | ${filesize(download.received, 2)} / ${filesize(download.filesize, 2)}';
double progress = download.received.toDouble() / download.filesize.toDouble();
out += ' ${(progress*100.0).toStringAsFixed(2)}%';
}
return out;
}
@ -281,4 +300,63 @@ class DownloadTile extends StatelessWidget {
],
);
}
}
}
class DownloadLogViewer extends StatefulWidget {
@override
_DownloadLogViewerState createState() => _DownloadLogViewerState();
}
class _DownloadLogViewerState extends State<DownloadLogViewer> {
List<String> data = [];
//Load log from file
Future _load() async {
String path = p.join((await getExternalStorageDirectory()).path, 'download.log');
File file = File(path);
if (await file.exists()) {
String _d = await file.readAsString();
setState(() {
data = _d.replaceAll("\r", "").split("\n");
});
}
}
//Get color by log type
Color color(String line) {
if (line.startsWith('E:')) return Colors.red;
if (line.startsWith('W:')) return Colors.orange[600];
return null;
}
@override
void initState() {
_load();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Download Log'.i18n),
),
body: ListView.builder(
itemCount: data.length,
itemBuilder: (context, i) {
return Padding(
padding: EdgeInsets.all(8.0),
child: Text(
data[i],
style: TextStyle(
fontSize: 14.0,
color: color(data[i])
),
),
);
},
)
);
}
}

View file

@ -203,6 +203,7 @@ class LibraryTracks extends StatefulWidget {
class _LibraryTracksState extends State<LibraryTracks> {
bool _loading = false;
bool _loadingTracks = false;
ScrollController _scrollController = ScrollController();
List<Track> tracks = [];
List<Track> allTracks = [];
@ -250,6 +251,9 @@ class _LibraryTracksState extends State<LibraryTracks> {
}
//Load another page of tracks from deezer
if (_loadingTracks) return;
_loadingTracks = true;
List<Track> _t;
try {
_t = await deezerAPI.playlistTracksPage(deezerAPI.favoritesPlaylistId, pos);
@ -263,6 +267,7 @@ class _LibraryTracksState extends State<LibraryTracks> {
tracks.addAll(_t);
_makeFavorite();
_loading = false;
_loadingTracks = false;
});
}

View file

@ -88,8 +88,13 @@ class _SearchScreenState extends State<SearchScreen> {
await Future.delayed(Duration(milliseconds: 300));
if (q != _query) return null;
//Load
List sugg = await deezerAPI.searchSuggestions(_query);
setState(() => _suggestions = sugg);
List sugg;
try {
sugg = await deezerAPI.searchSuggestions(_query);
} catch (e) {}
if (sugg != null)
setState(() => _suggestions = sugg);
}
@override

View file

@ -10,6 +10,7 @@ import 'package:fluttericon/font_awesome5_icons.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/download.dart';
import 'package:freezer/ui/downloads_screen.dart';
import 'package:freezer/ui/error.dart';
import 'package:freezer/ui/home_screen.dart';
import 'package:i18n_extension/i18n_widget.dart';
@ -352,7 +353,7 @@ class _QualityPickerState extends State<QualityPicker> {
}
//Update quality in settings
void _updateQuality(AudioQuality q) {
void _updateQuality(AudioQuality q) async {
setState(() {
_quality = q;
});
@ -370,15 +371,8 @@ class _QualityPickerState extends State<QualityPicker> {
case 'offline':
settings.offlineQuality = _quality; break;
}
settings.updateAudioServiceQuality();
}
@override
void dispose() {
//Save
settings.updateAudioServiceQuality();
settings.save();
super.dispose();
await settings.save();
await settings.updateAudioServiceQuality();
}
@override
@ -558,8 +552,9 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
if (!(await Permission.storage.request().isGranted)) return;
//Navigate
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => DirectoryPicker(settings.downloadPath, onSelect: (String p) {
builder: (context) => DirectoryPicker(settings.downloadPath, onSelect: (String p) async {
setState(() => settings.downloadPath = p);
await settings.save();
},)
));
},
@ -590,7 +585,7 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
),
Container(height: 8.0),
Text(
'Valid variables are'.i18n + ': %artists%, %artist%, %title%, %album%, %trackNumber%, %0trackNumber%, %feats%, %playlistTrackNumber%, %0playlistTrackNumber%, %year%',
'Valid variables are'.i18n + ': %artists%, %artist%, %title%, %album%, %trackNumber%, %0trackNumber%, %feats%, %playlistTrackNumber%, %0playlistTrackNumber%, %year%, %date%',
style: TextStyle(
fontSize: 12.0,
),
@ -734,6 +729,26 @@ class _DownloadsSettingsState extends State<DownloadsSettings> {
),
),
),
ListTile(
title: Text('Save cover file for every track'.i18n),
leading: Container(
width: 30.0,
child: Checkbox(
value: settings.trackCover,
onChanged: (v) {
setState(() => settings.trackCover = v);
settings.save();
},
),
),
),
ListTile(
title: Text('Download Log'.i18n),
leading: Icon(Icons.sticky_note_2),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => DownloadLogViewer())
),
)
],
),
);
@ -1071,6 +1086,14 @@ class _CreditsScreenState extends State<CreditsScreen> {
launch('https://t.me/freezerandroid');
},
),
ListTile(
title: Text('Repository'.i18n),
subtitle: Text('Source code, report issues there.'),
leading: Icon(Icons.code, color: Colors.green, size: 36.0),
onTap: () {
launch('https://notabug.org/exttex/freezer');
},
),
Divider(),
...List.generate(credits.length, (i) => ListTile(
title: Text(credits[i][0]),