Einzelne Server-Titel offline nehmen und wieder entfernen
This commit is contained in:
@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
||||
import '../library/database.dart';
|
||||
import '../library/song_media.dart';
|
||||
import '../player/audio_handler.dart';
|
||||
import '../services/download_service.dart';
|
||||
import '../services/navidrome_service.dart';
|
||||
import '../shared/cover.dart';
|
||||
import '../shared/lauf_balken.dart';
|
||||
@@ -69,6 +70,45 @@ class _ServerTitelScreenState extends State<ServerTitelScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Nach Laden oder Entfernen ist der Einmal-Schnappschuss [_geladen] veraltet
|
||||
/// — hier wird er nachgezogen.
|
||||
Future<void> _aktualisiereGeladen() async {
|
||||
final geladen = await context.read<MeloDb>().downloadIds();
|
||||
if (mounted) setState(() => _geladen = geladen);
|
||||
}
|
||||
|
||||
Future<void> _offlineNehmen(SubsonicSong song) async {
|
||||
final dienst = context.read<DownloadService>();
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
if (dienst.laeuft) {
|
||||
messenger.showSnackBar(
|
||||
const SnackBar(content: Text('Es läuft schon ein Download')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final neu = await dienst.ladeEinzelnenTitel(song);
|
||||
await _aktualisiereGeladen();
|
||||
if (!mounted) return;
|
||||
messenger.showSnackBar(SnackBar(
|
||||
content: Text(neu
|
||||
? 'Offline: ${song.titel}'
|
||||
: (dienst.fehler ?? 'War schon heruntergeladen')),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _offlineEntfernen(SubsonicSong song) async {
|
||||
final dienst = context.read<DownloadService>();
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
final weg = await dienst.entferne(song.id);
|
||||
await _aktualisiereGeladen();
|
||||
if (!mounted) return;
|
||||
messenger.showSnackBar(SnackBar(
|
||||
content: Text(weg
|
||||
? 'Vom Gerät entfernt: ${song.titel}'
|
||||
: (dienst.fehler ?? 'Ließ sich nicht entfernen')),
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _spiele(List<SubsonicSong> songs, int ab) async {
|
||||
final handler = context.read<MeloAudioHandler>();
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
@@ -130,6 +170,8 @@ class _ServerTitelScreenState extends State<ServerTitelScreen> {
|
||||
nummer: i,
|
||||
geladen: _geladen.contains(song.id),
|
||||
onTap: () => _spiele(songs, i - 1),
|
||||
onOffline: () => _offlineNehmen(song),
|
||||
onEntfernen: () => _offlineEntfernen(song),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -230,12 +272,16 @@ class _Zeile extends StatelessWidget {
|
||||
required this.nummer,
|
||||
required this.geladen,
|
||||
required this.onTap,
|
||||
required this.onOffline,
|
||||
required this.onEntfernen,
|
||||
});
|
||||
|
||||
final SubsonicSong song;
|
||||
final int nummer;
|
||||
final bool geladen;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback onOffline;
|
||||
final VoidCallback onEntfernen;
|
||||
|
||||
static String _dauer(int sekunden) {
|
||||
final m = (sekunden ~/ 60).toString();
|
||||
@@ -286,12 +332,21 @@ class _Zeile extends StatelessWidget {
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (geladen)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: MeloSpace.sm),
|
||||
child: Icon(Icons.download_done,
|
||||
size: 16, color: MeloTheme.text3),
|
||||
),
|
||||
// Bisher gab es den Lade-Knopf nur je Album — einen einzelnen
|
||||
// Titel mitzunehmen ging gar nicht.
|
||||
geladen
|
||||
? IconButton(
|
||||
tooltip: 'Vom Gerät entfernen',
|
||||
icon: const Icon(Icons.download_done,
|
||||
size: 20, color: MeloTheme.red),
|
||||
onPressed: onEntfernen,
|
||||
)
|
||||
: IconButton(
|
||||
tooltip: 'Offline nehmen',
|
||||
icon: const Icon(Icons.download_outlined,
|
||||
size: 20, color: MeloTheme.text3),
|
||||
onPressed: onOffline,
|
||||
),
|
||||
Text(_dauer(song.dauerSekunden),
|
||||
style: const TextStyle(color: MeloTheme.text3)),
|
||||
],
|
||||
|
||||
@@ -110,6 +110,20 @@ class DownloadService extends ChangeNotifier {
|
||||
return neu;
|
||||
}
|
||||
|
||||
/// Nimmt einen einzelnen Server-Titel offline.
|
||||
///
|
||||
/// Bewusst über [lade]: das bringt Doppel-Lauf-Schutz, Verbindungsprüfung
|
||||
/// und Fortschritts-Buchführung mit. Gibt zurück, ob der Titel neu
|
||||
/// dazugekommen ist.
|
||||
///
|
||||
/// **Bekannte Einschränkung:** Liegt der Titel schon im Abspiel-Zwischen-
|
||||
/// speicher (weil er gerade gehört wurde), wird er trotzdem neu geladen —
|
||||
/// bis die Verdrängung greift, belegt er doppelten Platz. Der Gegenschutz
|
||||
/// kostete einen eigenen Cache-Zugang in diesem Dienst; für drei Nutzer ist
|
||||
/// das der schlechtere Tausch.
|
||||
Future<bool> ladeEinzelnenTitel(SubsonicSong song) async =>
|
||||
await lade([song]) > 0;
|
||||
|
||||
Future<_Ergebnis> _ladeEinen(SubsonicSong song) async {
|
||||
try {
|
||||
// Datei und Buchführung entstehen nacheinander. Bricht die App
|
||||
|
||||
Reference in New Issue
Block a user