Skip to content

Commit

Permalink
feat(playback): integrate android, ios, macos with JustAudio
Browse files Browse the repository at this point in the history
  • Loading branch information
Kingkor Roy Tirtho committed May 5, 2023
1 parent cdb3268 commit d487fe5
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 113 deletions.
169 changes: 92 additions & 77 deletions lib/services/audio_player.dart
Original file line number Diff line number Diff line change
@@ -1,66 +1,83 @@
import 'dart:async';

import 'package:audioplayers/audioplayers.dart' as ap;
import 'package:assets_audio_player/assets_audio_player.dart' as aap;
import 'package:just_audio/just_audio.dart' as ja;
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';

final audioPlayer = SpotubeAudioPlayer();

enum PlayerState {
enum AudioPlaybackState {
playing,
paused,
completed,
buffering,
stopped;

static PlayerState fromApPlayerState(ap.PlayerState state) {
static AudioPlaybackState fromApPlayerState(ap.PlayerState state) {
switch (state) {
case ap.PlayerState.playing:
return PlayerState.playing;
return AudioPlaybackState.playing;
case ap.PlayerState.paused:
return PlayerState.paused;
return AudioPlaybackState.paused;
case ap.PlayerState.stopped:
return PlayerState.stopped;
return AudioPlaybackState.stopped;
case ap.PlayerState.completed:
return PlayerState.completed;
return AudioPlaybackState.completed;
}
}

static PlayerState fromAapPlayerState(aap.PlayerState state) {
switch (state) {
case aap.PlayerState.play:
return PlayerState.playing;
case aap.PlayerState.pause:
return PlayerState.paused;
case aap.PlayerState.stop:
return PlayerState.stopped;
static AudioPlaybackState fromJaPlayerState(ja.PlayerState state) {
if (state.playing) {
return AudioPlaybackState.playing;
}

switch (state.processingState) {
case ja.ProcessingState.idle:
return AudioPlaybackState.stopped;
case ja.ProcessingState.ready:
return AudioPlaybackState.paused;
case ja.ProcessingState.completed:
return AudioPlaybackState.completed;
case ja.ProcessingState.loading:
case ja.ProcessingState.buffering:
return AudioPlaybackState.buffering;
}
}

// static PlayerState fromAapPlayerState(aap.PlayerState state) {
// switch (state) {
// case aap.PlayerState.play:
// return PlayerState.playing;
// case aap.PlayerState.pause:
// return PlayerState.paused;
// case aap.PlayerState.stop:
// return PlayerState.stopped;
// }
// }

ap.PlayerState get asAudioPlayerPlayerState {
switch (this) {
case PlayerState.playing:
case AudioPlaybackState.playing:
return ap.PlayerState.playing;
case PlayerState.paused:
case AudioPlaybackState.paused:
return ap.PlayerState.paused;
case PlayerState.stopped:
case AudioPlaybackState.stopped:
return ap.PlayerState.stopped;
case PlayerState.completed:
case AudioPlaybackState.completed:
return ap.PlayerState.completed;
case PlayerState.buffering:
case AudioPlaybackState.buffering:
return ap.PlayerState.paused;
}
}
}

class SpotubeAudioPlayer {
final ap.AudioPlayer? _audioPlayer;
final aap.AssetsAudioPlayer? _assetsAudioPlayer;
final ja.AudioPlayer? _justAudio;

SpotubeAudioPlayer()
: _audioPlayer = apSupportedPlatform ? ap.AudioPlayer() : null,
_assetsAudioPlayer =
!apSupportedPlatform ? aap.AssetsAudioPlayer.newPlayer() : null;
_justAudio = !apSupportedPlatform ? ja.AudioPlayer() : null;

/// Whether the current platform supports the audioplayers plugin
static final bool apSupportedPlatform =
Expand All @@ -71,9 +88,9 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) {
return _audioPlayer!.onDurationChanged.asBroadcastStream();
} else {
return _assetsAudioPlayer!.onReadyToPlay
return _justAudio!.durationStream
.where((event) => event != null)
.map((event) => event!.duration)
.map((event) => event!)
.asBroadcastStream();
}
}
Expand All @@ -82,7 +99,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) {
return _audioPlayer!.onPositionChanged.asBroadcastStream();
} else {
return _assetsAudioPlayer!.currentPosition.asBroadcastStream();
return _justAudio!.positionStream.asBroadcastStream();
}
}

Expand All @@ -91,28 +108,18 @@ class SpotubeAudioPlayer {
// audioplayers doesn't have the capability to get buffered position
return const Stream<Duration>.empty().asBroadcastStream();
} else {
return const Stream<Duration>.empty().asBroadcastStream();
return _justAudio!.bufferedPositionStream.asBroadcastStream();
}
}

Stream<void> get completedStream {
if (apSupportedPlatform) {
return _audioPlayer!.onPlayerComplete.asBroadcastStream();
} else {
int lastValue = 0;
return positionStream.where(
(pos) {
final posS = pos.inSeconds;
final duration = _assetsAudioPlayer
?.current.valueOrNull?.audio.duration.inSeconds ??
0;
final isComplete =
posS > 0 && duration > 0 && posS == duration && posS != lastValue;

if (isComplete) lastValue = posS;
return isComplete;
},
).asBroadcastStream();
return _justAudio!.playerStateStream
.where(
(event) => event.processingState == ja.ProcessingState.completed)
.asBroadcastStream();
}
}

Expand All @@ -122,26 +129,32 @@ class SpotubeAudioPlayer {
return state == ap.PlayerState.playing;
}).asBroadcastStream();
} else {
return _assetsAudioPlayer!.isPlaying.asBroadcastStream();
return _justAudio!.playingStream;
}
}

Stream<bool> get bufferingStream {
if (apSupportedPlatform) {
return Stream.value(false).asBroadcastStream();
} else {
return _assetsAudioPlayer!.isBuffering.asBroadcastStream();
return _justAudio!.playerStateStream
.map(
(event) =>
event.processingState == ja.ProcessingState.buffering ||
event.processingState == ja.ProcessingState.loading,
)
.asBroadcastStream();
}
}

Stream<PlayerState> get playerStateStream {
Stream<AudioPlaybackState> get playerStateStream {
if (apSupportedPlatform) {
return _audioPlayer!.onPlayerStateChanged
.map((state) => PlayerState.fromApPlayerState(state))
.map((state) => AudioPlaybackState.fromApPlayerState(state))
.asBroadcastStream();
} else {
return _assetsAudioPlayer!.playerState
.map(PlayerState.fromAapPlayerState)
return _justAudio!.playerStateStream
.map(AudioPlaybackState.fromJaPlayerState)
.asBroadcastStream();
}
}
Expand All @@ -152,15 +165,15 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) {
return await _audioPlayer!.getDuration();
} else {
return _assetsAudioPlayer!.current.valueOrNull?.audio.duration;
return _justAudio!.duration;
}
}

Future<Duration?> get position async {
if (apSupportedPlatform) {
return await _audioPlayer!.getCurrentPosition();
} else {
return _assetsAudioPlayer!.currentPosition.valueOrNull;
return _justAudio!.position;
}
}

Expand All @@ -177,7 +190,7 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) {
return _audioPlayer!.source != null;
} else {
return _assetsAudioPlayer!.current.valueOrNull != null;
return _justAudio!.audioSource != null;
}
}

Expand All @@ -186,31 +199,31 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.playing;
} else {
return _assetsAudioPlayer!.isPlaying.valueOrNull ?? false;
return _justAudio!.playing;
}
}

bool get isPaused {
if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.paused;
} else {
return !isPlaying && hasSource;
return !isPlaying;
}
}

bool get isStopped {
if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.stopped;
} else {
return !isPlaying && !hasSource;
return _justAudio!.processingState == ja.ProcessingState.idle;
}
}

Future<bool> get isCompleted async {
if (apSupportedPlatform) {
return _audioPlayer!.state == ap.PlayerState.completed;
} else {
return !isPlaying && hasSource && await position == await duration;
return _justAudio!.processingState == ja.ProcessingState.completed;
}
}

Expand All @@ -219,7 +232,8 @@ class SpotubeAudioPlayer {
// audioplayers doesn't have the capability to get buffering state
return false;
} else {
return _assetsAudioPlayer!.isBuffering.valueOrNull ?? false;
return _justAudio!.processingState == ja.ProcessingState.buffering ||
_justAudio!.processingState == ja.ProcessingState.loading;
}
}

Expand All @@ -232,9 +246,9 @@ class SpotubeAudioPlayer {
}
} else {
if (url.startsWith("https")) {
return aap.Audio.network(url);
return ja.AudioSource.uri(Uri.parse(url));
} else {
return aap.Audio.file(url);
return ja.AudioSource.file(url);
}
}
}
Expand All @@ -254,54 +268,55 @@ class SpotubeAudioPlayer {
if (apSupportedPlatform && urlType is ap.Source) {
await _audioPlayer?.play(urlType);
} else {
await _assetsAudioPlayer?.stop();
await _assetsAudioPlayer?.open(
urlType as aap.Playable,
autoStart: true,
audioFocusStrategy: const aap.AudioFocusStrategy.request(
resumeAfterInterruption: true,
),
loopMode: aap.LoopMode.none,
playInBackground: aap.PlayInBackground.enabled,
headPhoneStrategy: aap.HeadPhoneStrategy.pauseOnUnplugPlayOnPlug,
showNotification: false,
respectSilentMode: true,
);
if (_justAudio?.audioSource is ja.ProgressiveAudioSource &&
(_justAudio?.audioSource as ja.ProgressiveAudioSource)
.uri
.toString() ==
url) {
await _justAudio?.play();
} else {
await _justAudio?.stop();
await _justAudio?.setAudioSource(
urlType as ja.AudioSource,
preload: true,
);
await _justAudio?.play();
}
}
}

Future<void> pause() async {
await _audioPlayer?.pause();
await _assetsAudioPlayer?.pause();
await _justAudio?.pause();
}

Future<void> resume() async {
await _audioPlayer?.resume();
await _assetsAudioPlayer?.play();
await _justAudio?.play();
}

Future<void> stop() async {
await _audioPlayer?.stop();
await _assetsAudioPlayer?.stop();
await _justAudio?.stop();
}

Future<void> seek(Duration position) async {
await _audioPlayer?.seek(position);
await _assetsAudioPlayer?.seek(position);
await _justAudio?.seek(position);
}

Future<void> setVolume(double volume) async {
await _audioPlayer?.setVolume(volume);
await _assetsAudioPlayer?.setVolume(volume);
await _justAudio?.setVolume(volume);
}

Future<void> setSpeed(double speed) async {
await _audioPlayer?.setPlaybackRate(speed);
await _assetsAudioPlayer?.setPlaySpeed(speed);
await _justAudio?.setSpeed(speed);
}

Future<void> dispose() async {
await _audioPlayer?.dispose();
await _assetsAudioPlayer?.dispose();
await _justAudio?.dispose();
}
}
10 changes: 5 additions & 5 deletions lib/services/audio_services/linux_audio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ class LinuxAudioService {
final playerStateStream =
audioPlayer.playerStateStream.listen((state) async {
switch (state) {
case PlayerState.buffering:
case PlayerState.playing:
case AudioPlaybackState.buffering:
case AudioPlaybackState.playing:
mpris.playbackStatus = MPRISPlaybackStatus.playing;
break;
case PlayerState.paused:
case AudioPlaybackState.paused:
mpris.playbackStatus = MPRISPlaybackStatus.paused;
break;
case PlayerState.stopped:
case PlayerState.completed:
case AudioPlaybackState.stopped:
case AudioPlaybackState.completed:
mpris.playbackStatus = MPRISPlaybackStatus.stopped;
break;
default:
Expand Down
Loading

0 comments on commit d487fe5

Please sign in to comment.