RhythmBox/lib/providers/audio_player_stream.dart

168 lines
5.2 KiB
Dart
Raw Permalink Normal View History

import 'dart:async';
import 'dart:developer';
import 'package:get/get.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:rhythm_box/providers/audio_player.dart';
2024-09-02 13:20:30 +00:00
import 'package:rhythm_box/providers/error_notifier.dart';
import 'package:rhythm_box/providers/history.dart';
import 'package:rhythm_box/providers/palette.dart';
import 'package:rhythm_box/providers/scrobbler.dart';
import 'package:rhythm_box/providers/skip_segments.dart';
import 'package:rhythm_box/providers/user_preferences.dart';
import 'package:rhythm_box/services/audio_player/audio_player.dart';
import 'package:rhythm_box/services/audio_services/audio_services.dart';
import 'package:rhythm_box/services/audio_services/image.dart';
import 'package:rhythm_box/services/local_track.dart';
import 'package:rhythm_box/services/server/sourced_track.dart';
import 'package:rhythm_box/widgets/auto_cache_image.dart';
class AudioPlayerStreamProvider extends GetxController {
late final AudioServices notificationService;
List<StreamSubscription>? _subscriptions;
2024-08-27 12:49:48 +00:00
AudioPlayerStreamProvider() {
_initNotificationService();
}
Future<void> _initNotificationService() async {
try {
final res = await AudioServices.create();
notificationService = res;
} catch (err) {
log('[AudioService] Unable to init audio service: $err');
}
2024-08-27 12:49:48 +00:00
}
@override
void onInit() {
super.onInit();
_subscriptions = [
subscribeToPlaylist(),
subscribeToSkipSponsor(),
subscribeToScrobbleChanged(),
subscribeToPosition(),
subscribeToPlayerError(),
];
}
@override
void dispose() {
if (_subscriptions != null) {
for (final subscription in _subscriptions!) {
subscription.cancel();
}
}
super.dispose();
}
Future<void> updatePalette() async {
if (!Get.find<UserPreferencesProvider>().state.value.albumColorSync) {
Get.find<PaletteProvider>().clear();
return;
}
final AudioPlayerProvider playback = Get.find();
final activeTrack = playback.state.value.activeTrack;
if (activeTrack == null) return;
if (activeTrack.album?.images != null) {
final newPalette = await PaletteGenerator.fromImageProvider(
AutoCacheImage.provider(
(activeTrack.album?.images).asUrlString()!,
),
);
Get.find<PaletteProvider>().updatePalette(newPalette);
}
}
StreamSubscription subscribeToPlaylist() {
final AudioPlayerProvider playback = Get.find();
return audioPlayer.playlistStream.listen((mpvPlaylist) {
final activeTrack = playback.state.value.activeTrack;
if (activeTrack != null) {
notificationService.addTrack(activeTrack);
updatePalette();
}
});
}
StreamSubscription subscribeToSkipSponsor() {
return audioPlayer.positionStream.listen((position) async {
final currentSegments =
await Get.find<SegmentsProvider>().fetchSegments();
if (currentSegments?.segments.isNotEmpty != true ||
position < const Duration(seconds: 3)) return;
for (final segment in currentSegments!.segments) {
final seconds = position.inSeconds;
if (seconds < segment.start || seconds >= segment.end) continue;
await audioPlayer.seek(Duration(seconds: segment.end + 1));
}
});
}
StreamSubscription subscribeToScrobbleChanged() {
String? lastScrobbled;
return audioPlayer.positionStream.listen((position) {
try {
final AudioPlayerProvider playback = Get.find();
final uid = playback.state.value.activeTrack is LocalTrack
? (playback.state.value.activeTrack as LocalTrack).path
: playback.state.value.activeTrack?.id;
if (playback.state.value.activeTrack == null ||
lastScrobbled == uid ||
position.inSeconds < 30) {
return;
}
Get.find<ScrobblerProvider>()
.scrobble(playback.state.value.activeTrack!);
Get.find<PlaybackHistoryProvider>()
.addTrack(playback.state.value.activeTrack!);
lastScrobbled = uid;
} catch (e, stack) {
2024-09-02 13:20:30 +00:00
Get.find<ErrorNotifier>()
.logError('[Scrobbler] Error: $e', trace: stack);
}
});
}
StreamSubscription subscribeToPosition() {
String lastTrack = ''; // used to prevent multiple calls to the same track
final AudioPlayerProvider playback = Get.find();
return audioPlayer.positionStream.listen((event) async {
if (event < const Duration(seconds: 3) ||
audioPlayer.playlist.index == -1 ||
audioPlayer.playlist.index ==
playback.state.value.tracks.length - 1) {
return;
}
final nextTrack = RhythmMedia.fromMedia(
audioPlayer.playlist.medias.elementAt(audioPlayer.playlist.index + 1),
);
if (lastTrack == nextTrack.track.id || nextTrack.track is LocalTrack) {
return;
}
try {
await Get.find<SourcedTrackProvider>().fetch(nextTrack);
} finally {
lastTrack = nextTrack.track.id!;
}
});
}
StreamSubscription subscribeToPlayerError() {
return audioPlayer.errorStream.listen((event) {
// Handle player error events here
});
}
}