diff --git a/lib/main.dart b/lib/main.dart index 48fa43f..57d70f8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -50,10 +50,11 @@ class MyApp extends StatelessWidget { void _initializeProviders(BuildContext context) async { Get.lazyPut(() => SpotifyProvider()); - Get.lazyPut(() => AudioPlayerProvider()); Get.lazyPut(() => ActiveSourcedTrackProvider()); Get.lazyPut(() => SourcedTrackProvider()); - Get.lazyPut(() => ServerPlaybackRoutesProvider()); - Get.lazyPut(() => PlaybackServerProvider()); + + Get.put(AudioPlayerProvider()); + Get.put(ServerPlaybackRoutesProvider()); + Get.put(PlaybackServerProvider()); } } diff --git a/lib/providers/audio_player.dart b/lib/providers/audio_player.dart index 79b4231..6960c34 100644 --- a/lib/providers/audio_player.dart +++ b/lib/providers/audio_player.dart @@ -1,10 +1,11 @@ +import 'dart:async'; import 'dart:math'; import 'package:get/get.dart'; import 'package:media_kit/media_kit.dart' hide Track; import 'package:rhythm_box/services/audio_player/state.dart'; import 'package:rhythm_box/services/local_track.dart'; -import 'package:rhythm_box/services/sourced_track/sourced_track.dart'; +import 'package:rhythm_box/services/server/sourced_track.dart'; import 'package:spotify/spotify.dart' hide Playlist; import 'package:rhythm_box/services/audio_player/audio_player.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -20,10 +21,49 @@ class AudioPlayerProvider extends GetxController { collections: [], )); - AudioPlayerProvider() { + List>? _subscriptions; + + @override + void onInit() { SharedPreferences.getInstance().then((ins) { _prefs = ins; + _syncSavedState(); }); + + _subscriptions = [ + audioPlayer.playingStream.listen((playing) async { + state.value = state.value.copyWith(playing: playing); + }), + audioPlayer.loopModeStream.listen((loopMode) async { + state.value = state.value.copyWith(loopMode: loopMode); + }), + audioPlayer.shuffledStream.listen((shuffled) async { + state.value = state.value.copyWith(shuffled: shuffled); + }), + audioPlayer.playlistStream.listen((playlist) async { + state.value = state.value.copyWith(playlist: playlist); + }), + ]; + + state.value = AudioPlayerState( + loopMode: audioPlayer.loopMode, + playing: audioPlayer.isPlaying, + playlist: audioPlayer.playlist, + shuffled: audioPlayer.isShuffled, + collections: [], + ); + + super.onInit(); + } + + @override + void dispose() { + if (_subscriptions != null) { + for (final subscription in _subscriptions!) { + subscription.cancel(); + } + } + super.dispose(); } Future _syncSavedState() async { @@ -35,6 +75,29 @@ class AudioPlayerProvider extends GetxController { // TODO Sync saved playlist } + Future addCollections(List collectionIds) async { + state.value = state.value.copyWith(collections: [ + ...state.value.collections, + ...collectionIds, + ]); + } + + Future addCollection(String collectionId) async { + await addCollections([collectionId]); + } + + Future removeCollections(List collectionIds) async { + state.value = state.value.copyWith( + collections: state.value.collections + .where((element) => !collectionIds.contains(element)) + .toList(), + ); + } + + Future removeCollection(String collectionId) async { + await removeCollections([collectionId]); + } + Future load( List tracks, { int initialIndex = 0, @@ -46,11 +109,14 @@ class AudioPlayerProvider extends GetxController { // because of timeout final intendedActiveTrack = medias.elementAt(initialIndex); if (intendedActiveTrack.track is! LocalTrack) { - await SourcedTrack.fetchFromTrack(track: intendedActiveTrack.track); + await Get.find() + .fetch(RhythmMedia(intendedActiveTrack.track)); } if (medias.isEmpty) return; + await removeCollections(state.value.collections); + await audioPlayer.openPlaylist( medias.map((s) => s as Media).toList(), initialIndex: initialIndex, diff --git a/lib/services/audio_player/audio_player.dart b/lib/services/audio_player/audio_player.dart index 7ddda6a..cd41b55 100755 --- a/lib/services/audio_player/audio_player.dart +++ b/lib/services/audio_player/audio_player.dart @@ -1,10 +1,12 @@ import 'dart:developer'; import 'dart:io'; +import 'package:get/get.dart'; import 'package:media_kit/media_kit.dart' hide Track; import 'package:flutter/foundation.dart'; import 'package:rhythm_box/platform.dart'; import 'package:rhythm_box/services/local_track.dart'; +import 'package:rhythm_box/services/server/server.dart'; import 'package:spotify/spotify.dart' hide Playlist; import 'package:rhythm_box/services/audio_player/custom_player.dart'; import 'dart:async'; @@ -20,7 +22,7 @@ part 'audio_player_impl.dart'; class RhythmMedia extends mk.Media { final Track track; - static int serverPort = 0; + static int get serverPort => Get.find().port; RhythmMedia( this.track, { diff --git a/lib/services/server/routes/playback.dart b/lib/services/server/routes/playback.dart index fa3e03f..e96460c 100755 --- a/lib/services/server/routes/playback.dart +++ b/lib/services/server/routes/playback.dart @@ -58,8 +58,8 @@ class ServerPlaybackRoutesProvider { }, headers: res.headers.map, ); - } catch (e) { - log('[PlaybackSever] Error: $e'); + } catch (e, stackTrace) { + log('[PlaybackSever] Error: $e; Trace:\n $stackTrace'); return Response.internalServerError(); } } diff --git a/lib/services/server/server.dart b/lib/services/server/server.dart index 9dfec04..9a0684f 100755 --- a/lib/services/server/server.dart +++ b/lib/services/server/server.dart @@ -11,6 +11,8 @@ import 'package:shelf/shelf_io.dart'; import 'package:shelf_router/shelf_router.dart'; class PlaybackServerProvider extends GetxController { + final int port = Random().nextInt(17500) + 5000; + HttpServer? _server; Router? _router; @@ -26,8 +28,6 @@ class PlaybackServerProvider extends GetxController { pipeline.addMiddleware(logRequests()); } - final port = Random().nextInt(17500) + 5000; - RhythmMedia.serverPort = port; _router = Router(); diff --git a/lib/services/server/sourced_track.dart b/lib/services/server/sourced_track.dart index 136ed07..820e2aa 100755 --- a/lib/services/server/sourced_track.dart +++ b/lib/services/server/sourced_track.dart @@ -1,17 +1,36 @@ +import 'package:collection/collection.dart'; import 'package:get/get.dart'; +import 'package:rhythm_box/providers/audio_player.dart'; import 'package:rhythm_box/services/audio_player/audio_player.dart'; import 'package:rhythm_box/services/local_track.dart'; import 'package:rhythm_box/services/sourced_track/sourced_track.dart'; +import 'package:spotify/spotify.dart'; class SourcedTrackProvider extends GetxController { + Rx sourcedTrack = Rx(null); + Future fetch(RhythmMedia? media) async { final track = media?.track; if (track == null || track is LocalTrack) { + sourcedTrack.value = null; return null; } - final sourcedTrack = await SourcedTrack.fetchFromTrack(track: track); + final AudioPlayerProvider playback = Get.find(); - return sourcedTrack; + ever(playback.state.value.tracks.obs, (List tracks) { + if (tracks.isEmpty || tracks.none((element) => element.id == track.id)) { + invalidate(); + } + }); + + sourcedTrack.value = await SourcedTrack.fetchFromTrack(track: track); + + return sourcedTrack.value; + } + + void invalidate() { + sourcedTrack.value = null; + fetch(null); } }