0.5.1 - Download fixes
This commit is contained in:
parent
8db1223805
commit
22ceca2d9c
16 changed files with 437 additions and 91 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!": "تم تحديث قائمة التشغيل!",
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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!"
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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])
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue