v2.48.1 — Repeat-Modus (Aus/Titel/Playlist)
## PlayerService
- Enum Wiederholmodus { aus, titel, playlist } + Getter/Setter
- naechstes(): titel → gleicher Song von vorn (seek 0 + play)
- naechsterIndex(): zentrale Berechnung des nächsten Index
- playlist: nach letztem Song wieder von vorne (auch bei Shuffle: neu mischen)
- aus: Ende der Warteschlange stoppt
- Auto-Next (ProcessingState.completed) läuft über naechstes() → Repeat wirkt auch automatisch
## MiniPlayer
- Repeat-Icon mit 3 Zuständen (repeat / repeat_one), rot wenn aktiv, Wechsel bei Tipp
Keine neuen Packages, flutter analyze 0 Issues, Tests 4/4
This commit is contained in:
@@ -7,6 +7,9 @@ import '../models/song.dart';
|
|||||||
import '../database/db_helper.dart';
|
import '../database/db_helper.dart';
|
||||||
import 'melo_logger.dart';
|
import 'melo_logger.dart';
|
||||||
|
|
||||||
|
/// Wiederhol-Modi für die Warteschlange.
|
||||||
|
enum Wiederholmodus { aus, titel, playlist }
|
||||||
|
|
||||||
class PlayerService extends ChangeNotifier {
|
class PlayerService extends ChangeNotifier {
|
||||||
static final PlayerService _instanz = PlayerService._();
|
static final PlayerService _instanz = PlayerService._();
|
||||||
factory PlayerService() => _instanz;
|
factory PlayerService() => _instanz;
|
||||||
@@ -27,6 +30,9 @@ class PlayerService extends ChangeNotifier {
|
|||||||
/// Position des zuletzt gespielten Songs in [_shuffleReihenfolge] (-1 = noch nichts).
|
/// Position des zuletzt gespielten Songs in [_shuffleReihenfolge] (-1 = noch nichts).
|
||||||
int _shufflePos = -1;
|
int _shufflePos = -1;
|
||||||
|
|
||||||
|
// ─── Wiederholmodus (Repeat) ───
|
||||||
|
Wiederholmodus _wiederholmodus = Wiederholmodus.aus;
|
||||||
|
|
||||||
AudioPlayer get _p {
|
AudioPlayer get _p {
|
||||||
if (_player == null) {
|
if (_player == null) {
|
||||||
_player = AudioPlayer();
|
_player = AudioPlayer();
|
||||||
@@ -69,6 +75,16 @@ class PlayerService extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Wiederholmodus (Repeat) ───
|
||||||
|
|
||||||
|
Wiederholmodus get wiederholmodus => _wiederholmodus;
|
||||||
|
|
||||||
|
void setWiederholmodus(Wiederholmodus modus) {
|
||||||
|
if (_wiederholmodus == modus) return;
|
||||||
|
_wiederholmodus = modus;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
/// Baut die Zufallsreihenfolge neu: alle Indizes außer dem aktuellen Song, gemischt.
|
/// Baut die Zufallsreihenfolge neu: alle Indizes außer dem aktuellen Song, gemischt.
|
||||||
void _baueShuffleReihenfolge() {
|
void _baueShuffleReihenfolge() {
|
||||||
_shuffleReihenfolge.clear();
|
_shuffleReihenfolge.clear();
|
||||||
@@ -86,16 +102,6 @@ class PlayerService extends ChangeNotifier {
|
|||||||
_shuffleReihenfolge.addAll(indices);
|
_shuffleReihenfolge.addAll(indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nächster Index in der Zufallsreihenfolge (null = alle durchgespielt).
|
|
||||||
int? _naechsterShuffleIndex() {
|
|
||||||
_shufflePos++;
|
|
||||||
if (_shufflePos < 0 || _shufflePos >= _shuffleReihenfolge.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final idx = _shuffleReihenfolge[_shufflePos];
|
|
||||||
return (idx >= 0 && idx < _warteschlange.length) ? idx : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> spiele(Song song, {int position = 0}) async {
|
Future<void> spiele(Song song, {int position = 0}) async {
|
||||||
_aktuellerIndex = _warteschlange.indexWhere((s) => s.id == song.id);
|
_aktuellerIndex = _warteschlange.indexWhere((s) => s.id == song.id);
|
||||||
if (_aktuellerIndex < 0) {
|
if (_aktuellerIndex < 0) {
|
||||||
@@ -195,16 +201,49 @@ class PlayerService extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> naechstes() async {
|
Future<void> naechstes() async {
|
||||||
if (_player == null || aktuellerSong == null) return;
|
if (_player == null || aktuellerSong == null) return;
|
||||||
if (_zufallsmodus) {
|
if (_wiederholmodus == Wiederholmodus.titel) {
|
||||||
final idx = _naechsterShuffleIndex();
|
// Gleichen Song von vorn wiederholen
|
||||||
if (idx == null) return;
|
await _p.seek(Duration.zero);
|
||||||
await spiele(_warteschlange[idx]);
|
await _p.play();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final neuerIndex = _aktuellerIndex + 1;
|
final idx = naechsterIndex();
|
||||||
if (neuerIndex < _warteschlange.length) {
|
if (idx == null) return;
|
||||||
await spiele(_warteschlange[neuerIndex]);
|
await spiele(_warteschlange[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Berechnet den Index des nächsten Songs in der Warteschlange —
|
||||||
|
/// berücksichtigt Zufallsmodus und Wiederholmodus. Gibt `null` zurück,
|
||||||
|
/// wenn die Warteschlange zu Ende ist (Repeat aus).
|
||||||
|
int? naechsterIndex() {
|
||||||
|
if (_aktuellerIndex < 0 || _warteschlange.isEmpty) return null;
|
||||||
|
if (_wiederholmodus == Wiederholmodus.titel) return _aktuellerIndex;
|
||||||
|
if (_zufallsmodus) {
|
||||||
|
_shufflePos++;
|
||||||
|
if (_shufflePos >= _shuffleReihenfolge.length) {
|
||||||
|
if (_wiederholmodus == Wiederholmodus.playlist) {
|
||||||
|
// Alle durchgespielt → neu mischen und weiter
|
||||||
|
_baueShuffleReihenfolge();
|
||||||
|
_shufflePos++;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_shufflePos < 0 || _shufflePos >= _shuffleReihenfolge.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final idx = _shuffleReihenfolge[_shufflePos];
|
||||||
|
return (idx >= 0 && idx < _warteschlange.length) ? idx : null;
|
||||||
}
|
}
|
||||||
|
var neuerIndex = _aktuellerIndex + 1;
|
||||||
|
if (neuerIndex >= _warteschlange.length) {
|
||||||
|
if (_wiederholmodus == Wiederholmodus.playlist) {
|
||||||
|
neuerIndex = 0;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return neuerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWarteschlange(List<Song> songs, {int startIndex = 0}) {
|
void setWarteschlange(List<Song> songs, {int startIndex = 0}) {
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
|||||||
),
|
),
|
||||||
// Shuffle
|
// Shuffle
|
||||||
_shuffleBtn(),
|
_shuffleBtn(),
|
||||||
|
// Repeat (3 Zustände)
|
||||||
|
_repeatBtn(),
|
||||||
// Steuerung
|
// Steuerung
|
||||||
_btn(Icons.skip_previous, _player.vorheriges),
|
_btn(Icons.skip_previous, _player.vorheriges),
|
||||||
_playBtn(),
|
_playBtn(),
|
||||||
@@ -142,6 +144,26 @@ class _MiniPlayerState extends State<MiniPlayer> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Repeat-Icon — 3 Zustände (aus → Titel → Playlist), Wechsel bei Tipp
|
||||||
|
Widget _repeatBtn() {
|
||||||
|
final modus = _player.wiederholmodus;
|
||||||
|
final (icon, farbe) = switch (modus) {
|
||||||
|
Wiederholmodus.aus => (Icons.repeat, Colors.white),
|
||||||
|
Wiederholmodus.titel => (Icons.repeat_one, MeloTheme.rot),
|
||||||
|
Wiederholmodus.playlist => (Icons.repeat, MeloTheme.rot),
|
||||||
|
};
|
||||||
|
return _btn(icon, _naechsterWiederholmodus, farbe: farbe);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _naechsterWiederholmodus() {
|
||||||
|
final modus = _player.wiederholmodus;
|
||||||
|
_player.setWiederholmodus(switch (modus) {
|
||||||
|
Wiederholmodus.aus => Wiederholmodus.titel,
|
||||||
|
Wiederholmodus.titel => Wiederholmodus.playlist,
|
||||||
|
Wiederholmodus.playlist => Wiederholmodus.aus,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Widget _btn(IconData icon, VoidCallback onTap, {Color farbe = Colors.white}) {
|
Widget _btn(IconData icon, VoidCallback onTap, {Color farbe = Colors.white}) {
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
|
|||||||
Reference in New Issue
Block a user