ocarina2/lib/api/audio/audioplayer_service.dart

207 lines
5.9 KiB
Dart

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:just_audio_background/just_audio_background.dart';
import 'package:ocarina/api/subsonic/song.dart';
import 'package:ocarina/main.dart';
import 'package:ocarina/util/util.dart';
/// Service used to control the audio player
class AudioPlayerService {
/// Service used to control the audio player
factory AudioPlayerService() {
return _audioPlayerService;
}
AudioPlayerService._internal();
static final AudioPlayerService _audioPlayerService =
AudioPlayerService._internal();
/// The [AudioPlayer] instance
final _player = AudioPlayer();
/// Check if listeners are set up
var _setUp = false;
/// Registers listeners on the player
void setup() {
if (_setUp) return;
_player.createPositionStream().listen((d) {
if (!isPlaying) return;
progressNotifier.value = [
d.inSeconds,
_songList[_player.currentIndex!].duration,
];
});
_player.currentIndexStream.listen((index) {
logger.d("Done fired");
songNotifier.value = index == null ? null : _songList[index];
_setColorScheme();
});
_setUp = true;
}
/// True if [AudioPlayer] instance is playing
bool get isPlaying => _player.playing;
/// Currently playing song
///
/// Null if no song is loaded
Song? get song =>
_player.currentIndex == null ? null : _songList[_player.currentIndex!];
final _queue = ConcatenatingAudioSource(
children: [],
shuffleOrder: DefaultShuffleOrder(),
);
/// Seeks to the next song
Future<void> next() async {
await _player.seekToNext();
}
/// Previous
Future<void> previous() async {
await _player.seekToPrevious();
}
/// Pauses playback
Future<void> pause() async {
await _player.pause();
logger.d("Paused");
}
/// Resumes playback
void resume() {
_player.play();
logger.d("Playing ${_player.currentIndex}");
}
/// Moves to the specified number of seconds
Future<void> seek(double position) async {
await _player.seek(Duration(seconds: position.toInt()));
}
/// Sets color scheme from image
Future<void> _setColorScheme() async {
if (AudioPlayerService().song == null) {
themeNotifier.value = ColorScheme.fromSeed(seedColor: Colors.deepPurple);
return;
}
themeNotifier.value = await ColorScheme.fromImageProvider(
provider: CachedNetworkImageProvider(
AudioPlayerService().song!.coverArtUrl,
cacheKey: AudioPlayerService().song!.coverArtId,
),
);
logger.d(AudioPlayerService().song!.coverArtUrl);
}
final List<Song> _songList = [];
/// Adds a selected song to the queue
Future<void> addToQueue(Song song) async {
final doCache = sp.getBool("doCache") ?? true;
_songList.add(song);
await _queue.add(
doCache
? LockCachingAudioSource(
Uri.parse(song.streamUrl),
tag: MediaItem(
id: song.id,
title: song.title,
album: song.albumName,
artist: song.artistName,
artUri: Uri.parse(song.coverArtUrl),
),
)
: AudioSource.uri(
Uri.parse(song.streamUrl),
tag: MediaItem(
id: song.id,
title: song.title,
album: song.albumName,
artist: song.artistName,
artUri: Uri.parse(song.coverArtUrl),
),
),
);
}
/// Plays the passed [Song], clearing the queue
Future<void> playNow({
List<Song> queueNext = const <Song>[],
List<Song> queuePast = const <Song>[],
}) async {
if (queueNext.isEmpty) return;
final doCache = sp.getBool("doCache") ?? true;
await _queue.clear();
_songList
..clear()
..addAll(queuePast)
..addAll(queueNext);
final q = List<AudioSource>.generate(
queuePast.length,
(i) => doCache
? LockCachingAudioSource(
Uri.parse(queuePast[i].streamUrl),
tag: MediaItem(
id: queuePast[i].id,
title: queuePast[i].title,
album: queuePast[i].albumName,
artist: queuePast[i].artistName,
artUri: Uri.parse(queuePast[i].coverArtUrl),
),
)
: AudioSource.uri(
Uri.parse(queuePast[i].streamUrl),
tag: MediaItem(
id: queuePast[i].id,
title: queuePast[i].title,
album: queuePast[i].albumName,
artist: queuePast[i].artistName,
artUri: Uri.parse(queuePast[i].coverArtUrl),
),
),
)..addAll(
List<AudioSource>.generate(
queueNext.length,
(i) => doCache
? LockCachingAudioSource(
Uri.parse(queueNext[i].streamUrl),
tag: MediaItem(
id: queueNext[i].id,
title: queueNext[i].title,
album: queueNext[i].albumName,
artist: queueNext[i].artistName,
artUri: Uri.parse(queueNext[i].coverArtUrl),
),
)
: AudioSource.uri(
Uri.parse(queueNext[i].streamUrl),
tag: MediaItem(
id: queueNext[i].id,
title: queueNext[i].title,
album: queueNext[i].albumName,
artist: queueNext[i].artistName,
artUri: Uri.parse(queueNext[i].coverArtUrl),
),
),
),
);
await _queue.addAll(
q,
);
await _player.setAudioSource(
_queue,
initialIndex: queuePast.length,
initialPosition: Duration.zero,
);
playerKey.currentState?.update();
resume();
}
}