first step to loop mode implementation

This commit is contained in:
Moritz Weber 2020-12-19 12:22:57 +01:00
parent 9e4459bcc3
commit f6e025c017
19 changed files with 352 additions and 21 deletions

View file

@ -0,0 +1,5 @@
enum LoopMode {
off,
one,
all
}

View file

@ -1,6 +1,7 @@
import 'package:dartz/dartz.dart';
import '../../core/error/failures.dart';
import '../entities/loop_mode.dart';
import '../entities/playback_state.dart';
import '../entities/shuffle_mode.dart';
import '../entities/song.dart';
@ -19,6 +20,9 @@ abstract class AudioRepository {
Future<Either<Failure, void>> skipToNext();
Future<Either<Failure, void>> skipToPrevious();
Future<Either<Failure, void>> setShuffleMode(ShuffleMode shuffleMode);
Future<void> setLoopMode(LoopMode loopMode);
Future<Either<Failure, void>> shuffleAll();
Future<Either<Failure, void>> addToQueue(Song song);
Future<Either<Failure, void>> moveQueueItem(int oldIndex, int newIndex);

View file

@ -3,6 +3,7 @@ import 'package:dartz/dartz.dart';
import '../../core/error/failures.dart';
import '../entities/album.dart';
import '../entities/artist.dart';
import '../entities/loop_mode.dart';
import '../entities/song.dart';
abstract class MusicDataRepository {
@ -10,6 +11,8 @@ abstract class MusicDataRepository {
Stream<List<Song>> getAlbumSongStream(Album album);
Stream<List<Song>> get queueStream;
Stream<int> get currentIndexStream;
Stream<LoopMode> get loopModeStream;
Future<Either<Failure, List<Song>>> getSongs();
Future<Either<Failure, List<Song>>> getSongsFromAlbum(Album album);

View file

@ -1,11 +1,12 @@
import 'package:meta/meta.dart';
import 'package:mobx/mobx.dart';
import 'package:mucke/domain/repositories/music_data_repository.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../../domain/entities/song.dart';
import '../../domain/repositories/audio_repository.dart';
import '../../domain/repositories/music_data_repository.dart';
part 'audio_store.g.dart';
@ -30,6 +31,8 @@ abstract class _AudioStore with Store {
shuffleModeStream =
_audioRepository.shuffleModeStream.asObservable(initialValue: ShuffleMode.none);
loopModeStream = _musicDataRepository.loopModeStream.asObservable();
playbackStateStream = _audioRepository.playbackStateStream.asObservable();
}
@ -70,6 +73,9 @@ abstract class _AudioStore with Store {
@observable
ObservableStream<ShuffleMode> shuffleModeStream;
@observable
ObservableStream<LoopMode> loopModeStream;
@action
Future<void> playSong(int index, List<Song> songList) async {
_audioRepository.playSong(index, songList);
@ -99,6 +105,11 @@ abstract class _AudioStore with Store {
_audioRepository.setShuffleMode(shuffleMode);
}
Future<void> setLoopMode(LoopMode loopMode) async {
print('setLoopMode');
_audioRepository.setLoopMode(loopMode);
}
Future<void> shuffleAll() async {
_audioRepository.shuffleAll();
}

View file

@ -110,6 +110,21 @@ mixin _$AudioStore on _AudioStore, Store {
});
}
final _$loopModeStreamAtom = Atom(name: '_AudioStore.loopModeStream');
@override
ObservableStream<LoopMode> get loopModeStream {
_$loopModeStreamAtom.reportRead();
return super.loopModeStream;
}
@override
set loopModeStream(ObservableStream<LoopMode> value) {
_$loopModeStreamAtom.reportWrite(value, super.loopModeStream, () {
super.loopModeStream = value;
});
}
final _$playSongAsyncAction = AsyncAction('_AudioStore.playSong');
@override
@ -154,6 +169,7 @@ currentPositionStream: ${currentPositionStream},
queueStream: ${queueStream},
queueIndexStream: ${queueIndexStream},
shuffleModeStream: ${shuffleModeStream},
loopModeStream: ${loopModeStream},
currentSong: ${currentSong}
''';
}

View file

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import '../../domain/entities/loop_mode.dart';
import '../state/audio_store.dart';
class LoopButton extends StatelessWidget {
const LoopButton({Key key, this.iconSize = 20.0}) : super(key: key);
final double iconSize;
@override
Widget build(BuildContext context) {
final AudioStore audioStore = Provider.of<AudioStore>(context);
return Observer(
builder: (BuildContext context) {
if (audioStore.loopModeStream != null) {
switch (audioStore.loopModeStream.value) {
case LoopMode.off:
return IconButton(
icon: const Icon(
Icons.repeat_rounded,
color: Colors.white30,
),
iconSize: iconSize,
onPressed: () {
audioStore.setLoopMode(LoopMode.all);
},
);
case LoopMode.one:
return IconButton(
icon: const Icon(
Icons.repeat_one_rounded,
color: Colors.white,
),
iconSize: iconSize,
onPressed: () {
audioStore.setLoopMode(LoopMode.off);
},
);
case LoopMode.all:
return IconButton(
icon: const Icon(
Icons.repeat_rounded,
color: Colors.white,
),
iconSize: iconSize,
onPressed: () {
audioStore.setLoopMode(LoopMode.one);
},
);
}
}
return Container();
},
);
}
}

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'loop_button.dart';
import 'next_button.dart';
import 'play_pause_button.dart';
import 'previous_button.dart';
@ -12,14 +13,7 @@ class PlaybackControl extends StatelessWidget {
Widget build(BuildContext context) {
return Row(
children: const [
IconButton(
icon: Icon(
Icons.repeat,
size: 20.0,
color: Colors.white10,
),
onPressed: null,
),
LoopButton(iconSize: 20.0),
PreviousButton(iconSize: 32.0),
PlayPauseButton(
circle: true,

View file

@ -3,6 +3,7 @@ import 'dart:math';
import 'package:audio_service/audio_service.dart';
import 'package:logging/logging.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/player_state.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../datasources/music_data_source_contract.dart';
@ -31,14 +32,20 @@ class MyAudioHandler extends BaseAudioHandler {
customEventSubject.add({SHUFFLE_MODE: shuffleMode});
});
_audioPlayer.loopModeStream.listen((event) {
_musicDataSource.setLoopMode(event);
});
_initAudioPlayer();
}
Future<void> _initAudioPlayer() async {
_audioPlayer.loadQueue(
queue: await _musicDataSource.queueStream.first,
startIndex: await _musicDataSource.currentIndexStream.first,
);
if (_musicDataSource.queueStream != null && _musicDataSource.currentIndexStream != null) {
_audioPlayer.loadQueue(
queue: await _musicDataSource.queueStream.first,
startIndex: await _musicDataSource.currentIndexStream.first,
);
}
}
final AudioPlayer _audioPlayer;
@ -89,6 +96,8 @@ class MyAudioHandler extends BaseAudioHandler {
return onAppLifecycleResumed();
case SET_SHUFFLE_MODE:
return setCustomShuffleMode(arguments['SHUFFLE_MODE'] as ShuffleMode);
case SET_LOOP_MODE:
return setCustomLoopMode(arguments['LOOP_MODE'] as LoopMode);
case SHUFFLE_ALL:
return shuffleAll();
case MOVE_QUEUE_ITEM:
@ -118,6 +127,11 @@ class MyAudioHandler extends BaseAudioHandler {
_audioPlayer.setShuffleMode(mode, true);
}
Future<void> setCustomLoopMode(LoopMode mode) async {
_audioPlayer.setLoopMode(mode);
}
Future<void> shuffleAll() async {
_audioPlayer.setShuffleMode(ShuffleMode.plus, false);

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:audio_service/audio_service.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart' as entity;
import '../../domain/entities/shuffle_mode.dart';
import '../models/playback_state_model.dart';
@ -115,6 +116,12 @@ class AudioManagerImpl implements AudioManager {
await _audioHandler.customAction(SET_SHUFFLE_MODE, {'SHUFFLE_MODE': shuffleMode});
}
@override
Future<void> setLoopMode(LoopMode loopMode) async {
print('setLoopMode!!');
await _audioHandler.customAction(SET_LOOP_MODE, {'LOOP_MODE': loopMode});
}
Stream<T> _filterStream<S, T>(Stream<S> stream, Conversion<S, T> fn) async* {
T lastItem;

View file

@ -1,3 +1,4 @@
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../models/song_model.dart';
@ -17,6 +18,7 @@ abstract class AudioManager {
Future<void> skipToNext();
Future<void> skipToPrevious();
Future<void> setShuffleMode(ShuffleMode shuffleMode);
Future<void> setLoopMode(LoopMode loopMode);
Future<void> shuffleAll();
Future<void> addToQueue(SongModel songModel);
Future<void> moveQueueItem(int oldIndex, int newIndex);

View file

@ -1,5 +1,6 @@
import 'package:rxdart/rxdart.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../models/player_state_model.dart';
import '../models/queue_item_model.dart';
@ -12,6 +13,7 @@ abstract class AudioPlayer {
ValueStream<Duration> get positionStream;
ValueStream<List<QueueItemModel>> get queueStream;
ValueStream<ShuffleMode> get shuffleModeStream;
ValueStream<LoopMode> get loopModeStream;
Future<void> play();
Future<void> pause();
@ -28,6 +30,7 @@ abstract class AudioPlayer {
Future<void> setShuffleMode(ShuffleMode shuffleMode, bool updateQueue);
Future<void> setLoopMode(LoopMode loopMode);
Future<void> playSongList(List<SongModel> songs, int startIndex);
}

View file

@ -1,8 +1,10 @@
import 'package:just_audio/just_audio.dart' as ja;
import 'package:rxdart/rxdart.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/queue_item.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../models/loop_mode_model.dart';
import '../models/player_state_model.dart';
import '../models/queue_item_model.dart';
import '../models/song_model.dart';
@ -24,6 +26,10 @@ class AudioPlayerImpl implements AudioPlayer {
_playerStateSubject.add(PlayerStateModel.fromJAPlayerState(event));
});
_audioPlayer.loopModeStream.listen((event) {
_loopModeSubject.add(event.toEntity());
});
_queueSubject.listen((event) {
_currentSongSubject.add(event[_currentIndexSubject.value].song);
});
@ -42,6 +48,7 @@ class AudioPlayerImpl implements AudioPlayer {
final BehaviorSubject<Duration> _positionSubject = BehaviorSubject();
final BehaviorSubject<List<QueueItemModel>> _queueSubject = BehaviorSubject();
final BehaviorSubject<ShuffleMode> _shuffleModeSubject = BehaviorSubject.seeded(ShuffleMode.none);
final BehaviorSubject<LoopMode> _loopModeSubject = BehaviorSubject();
@override
ValueStream<int> get currentIndexStream => _currentIndexSubject.stream;
@ -61,8 +68,18 @@ class AudioPlayerImpl implements AudioPlayer {
@override
ValueStream<ShuffleMode> get shuffleModeStream => _shuffleModeSubject.stream;
@override
ValueStream<LoopMode> get loopModeStream => _loopModeSubject.stream;
@override
Future<void> dispose() async {
await _currentIndexSubject.close();
await _currentSongSubject.close();
await _playerStateSubject.close();
await _positionSubject.close();
await _queueSubject.close();
await _shuffleModeSubject.close();
await _loopModeSubject.close();
await _audioPlayer.dispose();
}
@ -174,6 +191,12 @@ class AudioPlayerImpl implements AudioPlayer {
}
}
@override
Future<void> setLoopMode(LoopMode loopMode) async {
print('ap loopmode');
await _audioPlayer.setLoopMode(loopMode.toJA());
}
void _updateQueue(ja.ConcatenatingAudioSource newQueue, QueueItem currentQueueItem) {
final int index = currentQueueItem.originalIndex;

View file

@ -5,5 +5,6 @@ const String PLAY_WITH_CONTEXT = 'PLAY_WITH_CONTEXT';
const String APP_LIFECYCLE_RESUMED = 'APP_LIFECYCLE_RESUMED';
const String SHUFFLE_ALL = 'SHUFFLE_ALL';
const String SET_SHUFFLE_MODE = 'SET_SHUFFLE_MODE';
const String SET_LOOP_MODE = 'SET_LOOP_MODE';
const String MOVE_QUEUE_ITEM = 'MOVE_QUEUE_ITEM';
const String REMOVE_QUEUE_ITEM = 'REMOVE_QUEUE_ITEM';

View file

@ -7,8 +7,11 @@ import 'package:moor/moor.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../models/album_model.dart';
import '../models/artist_model.dart';
import '../models/loop_mode_model.dart';
import '../models/queue_item_model.dart';
import '../models/song_model.dart';
import 'music_data_source_contract.dart';
@ -69,6 +72,8 @@ class QueueEntries extends Table {
@DataClassName('PersistentPlayerState')
class PlayerState extends Table {
IntColumn get index => integer()();
IntColumn get shuffleMode => integer().withDefault(const Constant(0))();
IntColumn get loopMode => integer().withDefault(const Constant(0))();
}
@UseMoor(tables: [Artists, Albums, Songs, QueueEntries, PlayerState])
@ -293,6 +298,32 @@ class MoorMusicDataSource extends _$MoorMusicDataSource implements MusicDataSour
into(playerState).insert(PlayerStateCompanion(index: Value(index)));
}
}
@override
Stream<LoopMode> get loopModeStream {
return select(playerState).watchSingle().map((event) => event.loopMode.toLoopMode());
}
@override
Future<void> setLoopMode(LoopMode loopMode) async {
print('setLoopMode!!!');
final currentState = await select(playerState).getSingle();
if (currentState != null) {
update(playerState).write(PlayerStateCompanion(loopMode: Value(loopMode.toInt())));
} else {
into(playerState).insert(PlayerStateCompanion(loopMode: Value(loopMode.toInt())));
}
}
@override
Future<void> setShuffleMode(ShuffleMode shuffleMode) {
// TODO: implement setShuffleMode
throw UnimplementedError();
}
@override
// TODO: implement shuffleModeStream
Stream<ShuffleMode> get shuffleModeStream => throw UnimplementedError();
}
LazyDatabase _openConnection() {

View file

@ -1508,7 +1508,12 @@ class $QueueEntriesTable extends QueueEntries
class PersistentPlayerState extends DataClass
implements Insertable<PersistentPlayerState> {
final int index;
PersistentPlayerState({@required this.index});
final int shuffleMode;
final int loopMode;
PersistentPlayerState(
{@required this.index,
@required this.shuffleMode,
@required this.loopMode});
factory PersistentPlayerState.fromData(
Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
@ -1516,6 +1521,10 @@ class PersistentPlayerState extends DataClass
final intType = db.typeSystem.forDartType<int>();
return PersistentPlayerState(
index: intType.mapFromDatabaseResponse(data['${effectivePrefix}index']),
shuffleMode: intType
.mapFromDatabaseResponse(data['${effectivePrefix}shuffle_mode']),
loopMode:
intType.mapFromDatabaseResponse(data['${effectivePrefix}loop_mode']),
);
}
@override
@ -1524,6 +1533,12 @@ class PersistentPlayerState extends DataClass
if (!nullToAbsent || index != null) {
map['index'] = Variable<int>(index);
}
if (!nullToAbsent || shuffleMode != null) {
map['shuffle_mode'] = Variable<int>(shuffleMode);
}
if (!nullToAbsent || loopMode != null) {
map['loop_mode'] = Variable<int>(loopMode);
}
return map;
}
@ -1531,6 +1546,12 @@ class PersistentPlayerState extends DataClass
return PlayerStateCompanion(
index:
index == null && nullToAbsent ? const Value.absent() : Value(index),
shuffleMode: shuffleMode == null && nullToAbsent
? const Value.absent()
: Value(shuffleMode),
loopMode: loopMode == null && nullToAbsent
? const Value.absent()
: Value(loopMode),
);
}
@ -1539,6 +1560,8 @@ class PersistentPlayerState extends DataClass
serializer ??= moorRuntimeOptions.defaultSerializer;
return PersistentPlayerState(
index: serializer.fromJson<int>(json['index']),
shuffleMode: serializer.fromJson<int>(json['shuffleMode']),
loopMode: serializer.fromJson<int>(json['loopMode']),
);
}
@override
@ -1546,47 +1569,71 @@ class PersistentPlayerState extends DataClass
serializer ??= moorRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'index': serializer.toJson<int>(index),
'shuffleMode': serializer.toJson<int>(shuffleMode),
'loopMode': serializer.toJson<int>(loopMode),
};
}
PersistentPlayerState copyWith({int index}) => PersistentPlayerState(
PersistentPlayerState copyWith({int index, int shuffleMode, int loopMode}) =>
PersistentPlayerState(
index: index ?? this.index,
shuffleMode: shuffleMode ?? this.shuffleMode,
loopMode: loopMode ?? this.loopMode,
);
@override
String toString() {
return (StringBuffer('PersistentPlayerState(')
..write('index: $index')
..write('index: $index, ')
..write('shuffleMode: $shuffleMode, ')
..write('loopMode: $loopMode')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf(index.hashCode);
int get hashCode => $mrjf(
$mrjc(index.hashCode, $mrjc(shuffleMode.hashCode, loopMode.hashCode)));
@override
bool operator ==(dynamic other) =>
identical(this, other) ||
(other is PersistentPlayerState && other.index == this.index);
(other is PersistentPlayerState &&
other.index == this.index &&
other.shuffleMode == this.shuffleMode &&
other.loopMode == this.loopMode);
}
class PlayerStateCompanion extends UpdateCompanion<PersistentPlayerState> {
final Value<int> index;
final Value<int> shuffleMode;
final Value<int> loopMode;
const PlayerStateCompanion({
this.index = const Value.absent(),
this.shuffleMode = const Value.absent(),
this.loopMode = const Value.absent(),
});
PlayerStateCompanion.insert({
@required int index,
this.shuffleMode = const Value.absent(),
this.loopMode = const Value.absent(),
}) : index = Value(index);
static Insertable<PersistentPlayerState> custom({
Expression<int> index,
Expression<int> shuffleMode,
Expression<int> loopMode,
}) {
return RawValuesInsertable({
if (index != null) 'index': index,
if (shuffleMode != null) 'shuffle_mode': shuffleMode,
if (loopMode != null) 'loop_mode': loopMode,
});
}
PlayerStateCompanion copyWith({Value<int> index}) {
PlayerStateCompanion copyWith(
{Value<int> index, Value<int> shuffleMode, Value<int> loopMode}) {
return PlayerStateCompanion(
index: index ?? this.index,
shuffleMode: shuffleMode ?? this.shuffleMode,
loopMode: loopMode ?? this.loopMode,
);
}
@ -1596,13 +1643,21 @@ class PlayerStateCompanion extends UpdateCompanion<PersistentPlayerState> {
if (index.present) {
map['index'] = Variable<int>(index.value);
}
if (shuffleMode.present) {
map['shuffle_mode'] = Variable<int>(shuffleMode.value);
}
if (loopMode.present) {
map['loop_mode'] = Variable<int>(loopMode.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('PlayerStateCompanion(')
..write('index: $index')
..write('index: $index, ')
..write('shuffleMode: $shuffleMode, ')
..write('loopMode: $loopMode')
..write(')'))
.toString();
}
@ -1625,8 +1680,28 @@ class $PlayerStateTable extends PlayerState
);
}
final VerificationMeta _shuffleModeMeta =
const VerificationMeta('shuffleMode');
GeneratedIntColumn _shuffleMode;
@override
List<GeneratedColumn> get $columns => [index];
GeneratedIntColumn get shuffleMode =>
_shuffleMode ??= _constructShuffleMode();
GeneratedIntColumn _constructShuffleMode() {
return GeneratedIntColumn('shuffle_mode', $tableName, false,
defaultValue: const Constant(0));
}
final VerificationMeta _loopModeMeta = const VerificationMeta('loopMode');
GeneratedIntColumn _loopMode;
@override
GeneratedIntColumn get loopMode => _loopMode ??= _constructLoopMode();
GeneratedIntColumn _constructLoopMode() {
return GeneratedIntColumn('loop_mode', $tableName, false,
defaultValue: const Constant(0));
}
@override
List<GeneratedColumn> get $columns => [index, shuffleMode, loopMode];
@override
$PlayerStateTable get asDslTable => this;
@override
@ -1645,6 +1720,16 @@ class $PlayerStateTable extends PlayerState
} else if (isInserting) {
context.missing(_indexMeta);
}
if (data.containsKey('shuffle_mode')) {
context.handle(
_shuffleModeMeta,
shuffleMode.isAcceptableOrUnknown(
data['shuffle_mode'], _shuffleModeMeta));
}
if (data.containsKey('loop_mode')) {
context.handle(_loopModeMeta,
loopMode.isAcceptableOrUnknown(data['loop_mode'], _loopModeMeta));
}
return context;
}

View file

@ -1,3 +1,5 @@
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../models/album_model.dart';
import '../models/artist_model.dart';
import '../models/queue_item_model.dart';
@ -14,6 +16,10 @@ abstract class MusicDataSource {
Stream<List<QueueItemModel>> get queueStream;
Future<void> setCurrentIndex(int index);
Stream<int> get currentIndexStream;
Future<void> setShuffleMode(ShuffleMode shuffleMode);
Stream<ShuffleMode> get shuffleModeStream;
Future<void> setLoopMode(LoopMode loopMode);
Stream<LoopMode> get loopModeStream;
/// Insert album into the database. Return the ID of the inserted album.
Future<int> insertAlbum(AlbumModel albumModel);

View file

@ -0,0 +1,55 @@
import 'package:just_audio/just_audio.dart' as ja;
import '../../domain/entities/loop_mode.dart';
extension LoopModeToJA on LoopMode {
ja.LoopMode toJA() {
switch(this) {
case LoopMode.one:
return ja.LoopMode.one;
case LoopMode.all:
return ja.LoopMode.all;
default:
return ja.LoopMode.off;
}
}
}
extension JALoopModeToEntity on ja.LoopMode {
LoopMode toEntity() {
switch(this) {
case ja.LoopMode.one:
return LoopMode.one;
case ja.LoopMode.all:
return LoopMode.all;
default:
return LoopMode.off;
}
}
}
extension LoopModeToInt on LoopMode {
int toInt() {
switch(this) {
case LoopMode.one:
return 1;
case LoopMode.all:
return 2;
default:
return 0;
}
}
}
extension IntToLoopMode on int {
LoopMode toLoopMode() {
switch(this) {
case 1:
return LoopMode.one;
case 2:
return LoopMode.all;
default:
return LoopMode.off;
}
}
}

View file

@ -1,6 +1,7 @@
import 'package:dartz/dartz.dart';
import '../../core/error/failures.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../../domain/entities/song.dart';
@ -97,4 +98,10 @@ class AudioRepositoryImpl implements AudioRepository {
await _audioManager.removeQueueIndex(index);
return const Right(null);
}
@override
Future<void> setLoopMode(LoopMode loopMode) async {
print('setLoopMode!');
await _audioManager.setLoopMode(loopMode);
}
}

View file

@ -8,6 +8,7 @@ import 'package:path_provider/path_provider.dart';
import '../../core/error/failures.dart';
import '../../domain/entities/album.dart';
import '../../domain/entities/artist.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/song.dart';
import '../../domain/repositories/music_data_repository.dart';
import '../datasources/local_music_fetcher_contract.dart';
@ -139,4 +140,7 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
@override
Stream<int> get currentIndexStream => musicDataSource.currentIndexStream;
@override
Stream<LoopMode> get loopModeStream => musicDataSource.loopModeStream;
}