import 'dart:async'; import 'dart:io'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:mpris_service/mpris_service.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/models/spotube_track.dart'; import 'package:spotube/provider/playlist_queue_provider.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/loop_mode.dart'; import 'package:spotube/services/audio_player/playback_state.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; class LinuxAudioService { late final MPRIS mpris; final Ref ref; final PlaylistQueueNotifier playlistNotifier; final subscriptions = []; LinuxAudioService(this.ref, this.playlistNotifier) { MPRIS .create( busName: 'org.mpris.MediaPlayer2.spotube', identity: 'Spotube', desktopEntry: Platform.resolvedExecutable, ) .then((value) => mpris = value) .then((_) { mpris.playbackStatus = MPRISPlaybackStatus.stopped; mpris.setEventHandler(MPRISEventHandler( loopStatus: (value) async { playlistNotifier.setLoopMode( PlaybackLoopMode.fromMPRISLoopStatus(value), ); }, next: playlistNotifier.next, pause: playlistNotifier.pause, play: playlistNotifier.resume, playPause: () async { if (audioPlayer.isPlaying) { await playlistNotifier.pause(); } else { await playlistNotifier.resume(); } }, seek: playlistNotifier.seek, shuffle: playlistNotifier.setShuffle, stop: playlistNotifier.stop, volume: (value) async { await ref.read(VolumeProvider.provider.notifier).setVolume(value); }, previous: playlistNotifier.previous, )); final playerStateStream = audioPlayer.playerStateStream.listen((state) async { switch (state) { case AudioPlaybackState.buffering: case AudioPlaybackState.playing: mpris.playbackStatus = MPRISPlaybackStatus.playing; break; case AudioPlaybackState.paused: mpris.playbackStatus = MPRISPlaybackStatus.paused; break; case AudioPlaybackState.stopped: case AudioPlaybackState.completed: mpris.playbackStatus = MPRISPlaybackStatus.stopped; break; default: break; } }); final positionStream = audioPlayer.positionStream.listen((pos) async { mpris.position = pos; }); final durationStream = audioPlayer.durationStream.listen((duration) async { mpris.metadata = mpris.metadata.copyWith(length: duration); }); subscriptions.addAll([ playerStateStream, positionStream, durationStream, ]); }); } Future addTrack(Track track) async { mpris.metadata = MPRISMetadata( track is SpotubeTrack ? Uri.parse(track.ytUri) : Uri.parse(track.uri!), album: track.album?.name ?? "", albumArtist: [track.album?.artists?.first.name ?? ""], artUrl: Uri.parse(TypeConversionUtils.image_X_UrlString( track.album?.images ?? [], placeholder: ImagePlaceholder.albumArt, )), artist: track.artists?.map((e) => e.name!).toList(), contentCreated: DateTime.tryParse(track.album?.releaseDate ?? ""), discNumber: track.discNumber, length: track is SpotubeTrack ? track.ytTrack.duration! : Duration(milliseconds: track.durationMs!), title: track.name!, trackNumber: track.trackNumber, ); } void dispose() { mpris.dispose(); for (var element in subscriptions) { element.cancel(); } } }