import 'package:audio_service/audio_service.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/collections/env.dart'; import 'package:spotube/extensions/artist_simple.dart'; import 'package:spotube/extensions/image.dart'; import 'package:spotube/provider/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_services/mobile_audio_service.dart'; import 'package:spotube/services/audio_services/windows_audio_service.dart'; import 'package:spotube/services/sourced_track/sourced_track.dart'; import 'package:spotube/utils/platform.dart'; class AudioServices with WidgetsBindingObserver { final MobileAudioService? mobile; final WindowsAudioService? smtc; AudioServices(this.mobile, this.smtc) { WidgetsBinding.instance.addObserver(this); } static Future create( Ref ref, AudioPlayerNotifier playback, ) async { final mobile = kIsMobile || kIsMacOS || kIsLinux ? await AudioService.init( builder: () => MobileAudioService(playback), config: AudioServiceConfig( androidNotificationChannelId: switch (( kIsLinux, Env.releaseChannel )) { (true, _) => "spotube", (_, ReleaseChannel.stable) => "com.krtirtho.Spotube", (_, ReleaseChannel.nightly) => "com.krtirtho.Spotube.nightly", }, androidNotificationChannelName: 'Spotube', androidNotificationOngoing: false, androidStopForegroundOnPause: false, androidNotificationIcon: "drawable/ic_launcher_monochrome", androidNotificationChannelDescription: "Spotube Media Controls", ), ) : null; final smtc = kIsWindows ? WindowsAudioService(ref, playback) : null; return AudioServices(mobile, smtc); } Future addTrack(Track track) async { await smtc?.addTrack(track); mobile?.addItem(MediaItem( id: track.id!, album: track.album?.name ?? "", title: track.name!, artist: (track.artists)?.asString() ?? "", duration: track is SourcedTrack ? track.sourceInfo.duration : Duration(milliseconds: track.durationMs ?? 0), artUri: Uri.parse( (track.album?.images).asUrlString( placeholder: ImagePlaceholder.albumArt, ), ), playable: true, )); } void activateSession() { mobile?.session?.setActive(true); } void deactivateSession() { mobile?.session?.setActive(false); } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.detached: deactivateSession(); audioPlayer.pause(); break; default: break; } } void dispose() { smtc?.dispose(); WidgetsBinding.instance.removeObserver(this); } }