2020-10-28 13:10:43 +01:00
|
|
|
import 'dart:async';
|
2021-05-01 08:17:56 +02:00
|
|
|
import 'dart:developer';
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
import 'package:audio_service/audio_service.dart';
|
|
|
|
import 'package:audio_session/audio_session.dart';
|
2021-04-09 16:23:15 +02:00
|
|
|
import 'package:rxdart/rxdart.dart';
|
2020-10-28 13:10:43 +01:00
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:just_audio/just_audio.dart';
|
|
|
|
|
|
|
|
import '../local_storage/key_value_storage.dart';
|
|
|
|
import '../local_storage/sqflite_localpodcast.dart';
|
|
|
|
import '../type/episodebrief.dart';
|
|
|
|
import '../type/play_histroy.dart';
|
|
|
|
import '../type/playlist.dart';
|
|
|
|
|
|
|
|
MediaControl playControl = MediaControl(
|
|
|
|
androidIcon: 'drawable/ic_stat_play_circle_filled',
|
|
|
|
label: 'Play',
|
|
|
|
action: MediaAction.play,
|
|
|
|
);
|
|
|
|
MediaControl pauseControl = MediaControl(
|
|
|
|
androidIcon: 'drawable/ic_stat_pause_circle_filled',
|
|
|
|
label: 'Pause',
|
|
|
|
action: MediaAction.pause,
|
|
|
|
);
|
2020-11-06 15:04:08 +01:00
|
|
|
MediaControl skipToNextControl = MediaControl(
|
2020-10-28 13:10:43 +01:00
|
|
|
androidIcon: 'drawable/baseline_skip_next_white_24',
|
|
|
|
label: 'Next',
|
|
|
|
action: MediaAction.skipToNext,
|
|
|
|
);
|
2020-11-06 15:04:08 +01:00
|
|
|
MediaControl skipToPreviousControl = MediaControl(
|
2020-10-28 13:10:43 +01:00
|
|
|
androidIcon: 'drawable/ic_action_skip_previous',
|
|
|
|
label: 'Previous',
|
|
|
|
action: MediaAction.skipToPrevious,
|
|
|
|
);
|
|
|
|
MediaControl stopControl = MediaControl(
|
|
|
|
androidIcon: 'drawable/baseline_close_white_24',
|
|
|
|
label: 'Stop',
|
|
|
|
action: MediaAction.stop,
|
|
|
|
);
|
2020-11-06 15:04:08 +01:00
|
|
|
MediaControl forwardControl = MediaControl(
|
2020-10-28 13:10:43 +01:00
|
|
|
androidIcon: 'drawable/baseline_fast_forward_white_24',
|
|
|
|
label: 'forward',
|
|
|
|
action: MediaAction.fastForward,
|
|
|
|
);
|
|
|
|
|
2020-11-06 15:04:08 +01:00
|
|
|
MediaControl rewindControl = MediaControl(
|
2020-10-28 13:10:43 +01:00
|
|
|
androidIcon: 'drawable/baseline_fast_rewind_white_24',
|
|
|
|
label: 'rewind',
|
|
|
|
action: MediaAction.rewind,
|
|
|
|
);
|
|
|
|
|
|
|
|
/// Sleep timer mode.
|
|
|
|
enum SleepTimerMode { endOfEpisode, timer, undefined }
|
|
|
|
enum PlayerHeight { short, mid, tall }
|
|
|
|
|
|
|
|
class AudioPlayerNotifier extends ChangeNotifier {
|
2020-11-03 18:55:29 +01:00
|
|
|
final _dbHelper = DBHelper();
|
2020-10-28 13:10:43 +01:00
|
|
|
final _positionStorage = KeyValueStorage(audioPositionKey);
|
|
|
|
final _autoPlayStorage = KeyValueStorage(autoPlayKey);
|
|
|
|
final _autoSleepTimerStorage = KeyValueStorage(autoSleepTimerKey);
|
|
|
|
final _defaultSleepTimerStorage = KeyValueStorage(defaultSleepTimerKey);
|
|
|
|
final _autoSleepTimerModeStorage = KeyValueStorage(autoSleepTimerModeKey);
|
|
|
|
final _autoSleepTimerStartStorage = KeyValueStorage(autoSleepTimerStartKey);
|
|
|
|
final _autoSleepTimerEndStorage = KeyValueStorage(autoSleepTimerEndKey);
|
|
|
|
final _fastForwardSecondsStorage = KeyValueStorage(fastForwardSecondsKey);
|
|
|
|
final _rewindSecondsStorage = KeyValueStorage(rewindSecondsKey);
|
|
|
|
final _playerHeightStorage = KeyValueStorage(playerHeightKey);
|
|
|
|
final _speedStorage = KeyValueStorage(speedKey);
|
|
|
|
final _skipSilenceStorage = KeyValueStorage(skipSilenceKey);
|
|
|
|
final _boostVolumeStorage = KeyValueStorage(boostVolumeKey);
|
|
|
|
final _volumeGainStorage = KeyValueStorage(volumeGainKey);
|
|
|
|
final _markListenedAfterSkipStorage =
|
|
|
|
KeyValueStorage(markListenedAfterSkipKey);
|
2020-12-20 10:35:39 +01:00
|
|
|
final _playlistsStorgae = KeyValueStorage(playlistsAllKey);
|
|
|
|
final _playerStateStorage = KeyValueStorage(playerStateKey);
|
2021-05-01 18:02:12 +02:00
|
|
|
final cacheStorage = KeyValueStorage(cacheMaxKey);
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
/// Playing episdoe.
|
2022-04-30 17:16:19 +02:00
|
|
|
EpisodeBrief? _episode;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
/// Playlists include queue and playlists created by user.
|
2021-01-06 17:28:53 +01:00
|
|
|
List<Playlist> _playlists = [];
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
/// Playing playlist.
|
2022-04-30 17:16:19 +02:00
|
|
|
Playlist? _playlist;
|
2020-12-20 10:35:39 +01:00
|
|
|
|
|
|
|
/// Queue is the first playlist by default.
|
|
|
|
Playlist get _queue => _playlists.first;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Player state.
|
2021-04-09 16:23:15 +02:00
|
|
|
AudioProcessingState _audioState = AudioProcessingState.loading;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Player playing.
|
|
|
|
bool _playing = false;
|
|
|
|
|
|
|
|
/// Fastforward second.
|
2022-04-30 17:16:19 +02:00
|
|
|
int? _fastForwardSeconds = 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Rewind seconds.
|
2022-04-30 17:16:19 +02:00
|
|
|
int? _rewindSeconds = 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// No slide, set true if slide on seekbar.
|
|
|
|
bool _noSlide = true;
|
|
|
|
|
|
|
|
/// Current episode duration.
|
|
|
|
int _backgroundAudioDuration = 0;
|
|
|
|
|
|
|
|
/// Current episode positin.
|
2022-04-30 17:16:19 +02:00
|
|
|
int? _backgroundAudioPosition = 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Erroe maeesage.
|
2022-04-30 17:16:19 +02:00
|
|
|
String? _remoteErrorMessage;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Seekbar value, min 0, max 1.0.
|
|
|
|
double _seekSliderValue = 0.0;
|
|
|
|
|
|
|
|
/// Record plyaer position.
|
2021-01-02 09:21:05 +01:00
|
|
|
int _lastPosition = 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Set true if sleep timer mode is end of episode.
|
|
|
|
bool _stopOnComplete = false;
|
|
|
|
|
|
|
|
/// Sleep timer timer.
|
2022-04-30 17:16:19 +02:00
|
|
|
late Timer _stopTimer;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Sleep timer time left.
|
|
|
|
int _timeLeft = 0;
|
|
|
|
|
|
|
|
/// Start sleep timer.
|
|
|
|
bool _startSleepTimer = false;
|
|
|
|
|
|
|
|
/// Control sleep timer anamation.
|
|
|
|
double _switchValue = 0;
|
|
|
|
|
|
|
|
/// Sleep timer mode.
|
|
|
|
SleepTimerMode _sleepTimerMode = SleepTimerMode.undefined;
|
|
|
|
|
|
|
|
//Auto stop at the end of episode when you start play at scheduled time.
|
2022-04-30 17:16:19 +02:00
|
|
|
bool? _autoSleepTimer;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
//set autoplay episode in playlist
|
2022-04-30 17:16:19 +02:00
|
|
|
late bool _autoPlay;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Datetime now.
|
2022-04-30 17:16:19 +02:00
|
|
|
DateTime? _current;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Current position.
|
2022-04-30 17:16:19 +02:00
|
|
|
late int _currentPosition;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Current speed.
|
2022-04-30 17:16:19 +02:00
|
|
|
double? _currentSpeed = 1;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
///Update episode card when setting changed
|
|
|
|
bool _episodeState = false;
|
|
|
|
|
|
|
|
/// Player height.
|
2022-04-30 17:16:19 +02:00
|
|
|
PlayerHeight? _playerHeight;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Player skip silence.
|
2022-04-30 17:16:19 +02:00
|
|
|
bool? _skipSilence;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Boost volumn
|
2022-04-30 17:16:19 +02:00
|
|
|
bool? _boostVolume;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
/// Boost volume gain.
|
2022-04-30 17:16:19 +02:00
|
|
|
int? _volumeGain;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
// ignore: prefer_final_fields
|
|
|
|
bool _playerRunning = false;
|
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
late bool _markListened;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2021-02-09 08:36:29 +01:00
|
|
|
// Tmep episode list, playing from search result
|
2022-04-30 17:16:19 +02:00
|
|
|
List<EpisodeBrief?> _playFromSearchList = [];
|
2021-02-09 08:36:29 +01:00
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
late AudioHandler _audioHandler;
|
2021-05-01 18:02:12 +02:00
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
StreamSubscription<MediaItem?>? _mediaitemSubscription;
|
|
|
|
StreamSubscription<PlaybackState>? _playbackStateSubscription;
|
|
|
|
StreamSubscription<dynamic>? _customEventSubscription;
|
2021-04-09 16:23:15 +02:00
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
@override
|
2021-04-09 16:23:15 +02:00
|
|
|
void addListener(VoidCallback listener) async {
|
2021-05-01 08:17:56 +02:00
|
|
|
await _initAudioData();
|
2021-05-01 18:02:12 +02:00
|
|
|
final cacheMax =
|
|
|
|
await cacheStorage.getInt(defaultValue: (1024 * 1024 * 200).toInt());
|
|
|
|
_audioHandler = await AudioService.init(
|
2022-04-30 17:16:19 +02:00
|
|
|
builder: () => CustomAudioHandler(cacheMax!), config: _config);
|
2021-05-01 08:17:56 +02:00
|
|
|
super.addListener(listener);
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_mediaitemSubscription?.cancel();
|
|
|
|
_playbackStateSubscription?.cancel();
|
|
|
|
_customEventSubscription?.cancel();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
/// Audio service config
|
|
|
|
AudioServiceConfig get _config => AudioServiceConfig(
|
|
|
|
androidNotificationChannelName: 'Tsacdop',
|
|
|
|
androidNotificationIcon: 'drawable/ic_notification',
|
2021-10-06 12:47:09 +02:00
|
|
|
// androidEnableQueue: true,
|
2021-09-05 08:57:02 +02:00
|
|
|
androidStopForegroundOnPause: true,
|
|
|
|
preloadArtwork: false,
|
2022-04-30 17:16:19 +02:00
|
|
|
fastForwardInterval: Duration(seconds: _fastForwardSeconds!),
|
|
|
|
rewindInterval: Duration(seconds: _rewindSeconds!),
|
2021-09-05 08:57:02 +02:00
|
|
|
);
|
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
/// Audio playing state.
|
2020-10-28 13:10:43 +01:00
|
|
|
AudioProcessingState get audioState => _audioState;
|
|
|
|
int get backgroundAudioDuration => _backgroundAudioDuration;
|
2022-04-30 17:16:19 +02:00
|
|
|
int? get backgroundAudioPosition => _backgroundAudioPosition;
|
2020-10-28 13:10:43 +01:00
|
|
|
double get seekSliderValue => _seekSliderValue;
|
2022-04-30 17:16:19 +02:00
|
|
|
String? get remoteErrorMessage => _remoteErrorMessage;
|
2020-10-28 13:10:43 +01:00
|
|
|
bool get playerRunning => _playerRunning;
|
|
|
|
bool get buffering => _audioState != AudioProcessingState.ready;
|
2022-04-30 17:16:19 +02:00
|
|
|
EpisodeBrief? get episode => _episode;
|
2020-12-20 10:35:39 +01:00
|
|
|
|
|
|
|
/// Playlist provider.
|
2021-01-02 09:21:05 +01:00
|
|
|
int get lastPosition => _lastPosition;
|
2020-12-20 10:35:39 +01:00
|
|
|
List<Playlist> get playlists => _playlists;
|
|
|
|
Playlist get queue => _playlists.first;
|
2020-10-28 13:10:43 +01:00
|
|
|
bool get playing => _playing;
|
2022-04-30 17:16:19 +02:00
|
|
|
Playlist? get playlist => _playlist;
|
2020-12-20 10:35:39 +01:00
|
|
|
|
|
|
|
/// Player control.
|
2020-10-28 13:10:43 +01:00
|
|
|
bool get stopOnComplete => _stopOnComplete;
|
|
|
|
bool get startSleepTimer => _startSleepTimer;
|
|
|
|
SleepTimerMode get sleepTimerMode => _sleepTimerMode;
|
|
|
|
int get timeLeft => _timeLeft;
|
|
|
|
double get switchValue => _switchValue;
|
2022-04-30 17:16:19 +02:00
|
|
|
double? get currentSpeed => _currentSpeed;
|
2020-10-28 13:10:43 +01:00
|
|
|
bool get episodeState => _episodeState;
|
2022-04-30 17:16:19 +02:00
|
|
|
bool? get autoSleepTimer => _autoSleepTimer;
|
|
|
|
int? get fastForwardSeconds => _fastForwardSeconds;
|
|
|
|
int? get rewindSeconds => _rewindSeconds;
|
|
|
|
PlayerHeight? get playerHeight => _playerHeight;
|
|
|
|
bool? get skipSilence => _skipSilence;
|
|
|
|
bool? get boostVolume => _boostVolume;
|
|
|
|
int? get volumeGain => _volumeGain;
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
set setSwitchValue(double value) {
|
|
|
|
_switchValue = value;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
set setEpisodeState(bool boo) {
|
|
|
|
_episodeState = !_episodeState;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
set setPlayerHeight(PlayerHeight mode) {
|
|
|
|
_playerHeight = mode;
|
|
|
|
notifyListeners();
|
|
|
|
_savePlayerHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _initAudioData() async {
|
2022-04-30 17:16:19 +02:00
|
|
|
var index = await (_playerHeightStorage.getInt(defaultValue: 0) as FutureOr<int>);
|
2020-10-28 13:10:43 +01:00
|
|
|
_playerHeight = PlayerHeight.values[index];
|
2020-12-20 10:35:39 +01:00
|
|
|
_currentSpeed = await _speedStorage.getDouble(defaultValue: 1.0);
|
2020-10-28 13:10:43 +01:00
|
|
|
_skipSilence = await _skipSilenceStorage.getBool(defaultValue: false);
|
|
|
|
_boostVolume = await _boostVolumeStorage.getBool(defaultValue: false);
|
|
|
|
_volumeGain = await _volumeGainStorage.getInt(defaultValue: 3000);
|
2021-05-01 08:17:56 +02:00
|
|
|
_fastForwardSeconds =
|
|
|
|
await _fastForwardSecondsStorage.getInt(defaultValue: 30);
|
|
|
|
_rewindSeconds = await _rewindSecondsStorage.getInt(defaultValue: 30);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future _savePlayerHeight() async {
|
2022-04-30 17:16:19 +02:00
|
|
|
await _playerHeightStorage.saveInt(_playerHeight!.index);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future _getAutoPlay() async {
|
|
|
|
var i = await _autoPlayStorage.getInt();
|
|
|
|
_autoPlay = i == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future _getAutoSleepTimer() async {
|
|
|
|
var i = await _autoSleepTimerStorage.getInt();
|
|
|
|
_autoSleepTimer = i == 1;
|
|
|
|
}
|
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
Future<void> initPlaylist() async {
|
2021-01-06 17:28:53 +01:00
|
|
|
if (_playlists.isEmpty) {
|
2022-04-30 17:16:19 +02:00
|
|
|
var playlistEntities = await (_playlistsStorgae.getPlaylists() as FutureOr<List<PlaylistEntity>>);
|
2021-01-06 17:28:53 +01:00
|
|
|
_playlists = [
|
|
|
|
for (var entity in playlistEntities) Playlist.fromEntity(entity)
|
|
|
|
];
|
|
|
|
await _playlists.first.getPlaylist();
|
|
|
|
await _getAutoPlay();
|
|
|
|
|
2021-01-24 09:49:14 +01:00
|
|
|
///Get playerstate saved in storage.
|
2022-04-30 17:16:19 +02:00
|
|
|
var state = await (_playerStateStorage.getPlayerState() as FutureOr<List<String>>);
|
2021-01-06 17:28:53 +01:00
|
|
|
var idList = [for (var p in _playlists) p.id];
|
2021-01-24 09:49:14 +01:00
|
|
|
if (idList.contains(state[0])) {
|
2021-01-06 17:28:53 +01:00
|
|
|
_playlist = _playlists.firstWhere(
|
|
|
|
(p) => p.id == state[0],
|
|
|
|
);
|
2022-04-30 17:16:19 +02:00
|
|
|
await _playlist!.getPlaylist();
|
2021-01-06 17:28:53 +01:00
|
|
|
if (state[1] != '') {
|
|
|
|
var episode = await _dbHelper.getRssItemWithUrl(state[1]);
|
2021-02-17 16:25:23 +01:00
|
|
|
if (episode != null &&
|
2022-04-30 17:16:19 +02:00
|
|
|
((!_playlist!.isQueue &&
|
2021-02-17 16:25:23 +01:00
|
|
|
episode != null &&
|
2022-04-30 17:16:19 +02:00
|
|
|
_playlist!.contains(episode)) ||
|
|
|
|
(_playlist!.isQueue &&
|
2021-02-17 16:25:23 +01:00
|
|
|
_queue.isNotEmpty &&
|
2022-04-30 17:16:19 +02:00
|
|
|
_queue.episodes.first!.title == episode.title))) {
|
2021-01-06 17:28:53 +01:00
|
|
|
_episode = episode;
|
|
|
|
_lastPosition = int.parse(state[2] ?? '0');
|
2021-01-24 09:49:14 +01:00
|
|
|
if (_lastPosition > 0) {
|
|
|
|
{
|
2022-04-30 17:16:19 +02:00
|
|
|
final duration = episode.duration! * 1000;
|
2021-01-24 09:49:14 +01:00
|
|
|
final seekValue =
|
|
|
|
duration != 0 ? _lastPosition / duration : 1.0;
|
|
|
|
final history = PlayHistory(episode.title, episode.enclosureUrl,
|
|
|
|
_lastPosition ~/ 1000, seekValue);
|
|
|
|
await _dbHelper.saveHistory(history);
|
|
|
|
}
|
|
|
|
}
|
2021-01-06 17:28:53 +01:00
|
|
|
} else {
|
2022-04-30 17:16:19 +02:00
|
|
|
_episode = _playlist!.isNotEmpty ? _playlist!.episodes.first : null;
|
2021-01-06 17:28:53 +01:00
|
|
|
_lastPosition = 0;
|
|
|
|
}
|
2021-01-02 09:21:05 +01:00
|
|
|
} else {
|
2022-04-30 17:16:19 +02:00
|
|
|
_episode = _playlist!.isNotEmpty ? _playlist!.episodes.first : null;
|
2021-01-02 09:21:05 +01:00
|
|
|
_lastPosition = 0;
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-06 17:28:53 +01:00
|
|
|
_playlist = _playlists.first;
|
2022-04-30 17:16:19 +02:00
|
|
|
_episode = _playlist!.isNotEmpty ? _playlist!.episodes?.first : null;
|
2021-01-02 09:21:05 +01:00
|
|
|
_lastPosition = 0;
|
|
|
|
}
|
2021-01-06 17:28:53 +01:00
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
await KeyValueStorage(lastWorkKey).saveInt(0);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
Future<void> playFromLastPosition() async {
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.episodes.isNotEmpty) {
|
|
|
|
await _playlist!.getPlaylist();
|
|
|
|
if (_episode == null || !_playlist!.episodes.contains(_episode)) {
|
|
|
|
_episode = _playlist!.isNotEmpty ? _playlist!.episodes.first : null;
|
2021-01-02 15:53:16 +01:00
|
|
|
}
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioState = AudioProcessingState.loading;
|
2021-01-01 16:42:59 +01:00
|
|
|
_backgroundAudioDuration = 0;
|
|
|
|
_backgroundAudioPosition = 0;
|
|
|
|
_seekSliderValue = 0;
|
|
|
|
_playerRunning = true;
|
|
|
|
notifyListeners();
|
2022-04-30 17:16:19 +02:00
|
|
|
_startAudioService(_playlist!,
|
2021-01-02 09:21:05 +01:00
|
|
|
position: _lastPosition ?? 0,
|
2022-04-30 17:16:19 +02:00
|
|
|
index: _playlist!.episodes.indexOf(_episode));
|
2021-10-06 12:47:09 +02:00
|
|
|
} else {
|
|
|
|
log('Playlist is empty');
|
2021-01-01 16:42:59 +01:00
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> playlistLoad(Playlist playlist) async {
|
|
|
|
var p = playlist;
|
2020-12-23 15:03:07 +01:00
|
|
|
if (playlist.name != 'Queue') {
|
2020-12-20 10:35:39 +01:00
|
|
|
await updatePlaylist(p, updateEpisodes: true);
|
|
|
|
}
|
|
|
|
_playlist = p;
|
|
|
|
notifyListeners();
|
2021-01-02 15:53:16 +01:00
|
|
|
if (playlist.isNotEmpty) {
|
2020-12-20 10:35:39 +01:00
|
|
|
if (playerRunning) {
|
2021-09-05 08:57:02 +02:00
|
|
|
_audioHandler.customAction('setIsQueue', {'isQueue': playlist.isQueue});
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioHandler.customAction('changeQueue', {
|
2022-04-30 17:16:19 +02:00
|
|
|
'queue': [for (var e in p.episodes) e!.toMediaItem()]
|
2021-04-09 16:23:15 +02:00
|
|
|
});
|
2020-12-20 10:35:39 +01:00
|
|
|
} else {
|
|
|
|
_backgroundAudioDuration = 0;
|
|
|
|
_backgroundAudioPosition = 0;
|
|
|
|
_seekSliderValue = 0;
|
|
|
|
_episode = playlist.episodes.first;
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioState = AudioProcessingState.loading;
|
2020-12-20 10:35:39 +01:00
|
|
|
_playerRunning = true;
|
|
|
|
notifyListeners();
|
2022-04-30 17:16:19 +02:00
|
|
|
_startAudioService(_playlist!, position: 0, index: 0);
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
Future<void> episodeLoad(EpisodeBrief? episode,
|
2021-02-09 08:36:29 +01:00
|
|
|
{int startPosition = 0, bool fromSearch = false}) async {
|
|
|
|
var episodeNew;
|
|
|
|
if (fromSearch) {
|
|
|
|
episodeNew = episode;
|
|
|
|
_playFromSearchList.add(episode);
|
|
|
|
} else {
|
2022-04-30 17:16:19 +02:00
|
|
|
episodeNew = await _dbHelper.getRssItemWithUrl(episode!.enclosureUrl);
|
2021-02-09 08:36:29 +01:00
|
|
|
}
|
2021-09-05 08:57:02 +02:00
|
|
|
// @TODO load episode from last position when player running
|
2020-10-28 13:10:43 +01:00
|
|
|
if (playerRunning) {
|
2021-02-09 17:40:04 +01:00
|
|
|
if (_playFromSearchList.contains(_episode)) {
|
|
|
|
_queue.delFromPlaylist(_episode);
|
|
|
|
} else {
|
2022-04-30 17:16:19 +02:00
|
|
|
final history = PlayHistory(_episode!.title, _episode!.enclosureUrl,
|
|
|
|
backgroundAudioPosition! ~/ 1000, seekSliderValue);
|
2021-02-09 17:40:04 +01:00
|
|
|
await _dbHelper.saveHistory(history);
|
|
|
|
}
|
2020-12-31 19:08:46 +01:00
|
|
|
_queue.addToPlayListAt(episodeNew, 0);
|
2021-02-09 08:36:29 +01:00
|
|
|
await updatePlaylist(_queue, updateEpisodes: !fromSearch);
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.isQueue) {
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioHandler.customAction('setIsQueue', {'isQueue': true});
|
|
|
|
_audioHandler.customAction('changeQueue', {
|
2022-04-30 17:16:19 +02:00
|
|
|
'queue': [for (var e in _queue.episodes) e!.toMediaItem()]
|
2021-04-09 16:23:15 +02:00
|
|
|
});
|
2020-12-31 19:08:46 +01:00
|
|
|
_playlist = _queue;
|
2020-12-23 15:03:07 +01:00
|
|
|
}
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.customAction('addQueueItemAt',
|
|
|
|
{'mediaItem': episodeNew.toMediaItem(), 'index': 0});
|
2020-10-28 13:10:43 +01:00
|
|
|
if (startPosition > 0) {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.seek(Duration(milliseconds: startPosition));
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
_remoteErrorMessage = null;
|
|
|
|
notifyListeners();
|
|
|
|
if (episodeNew.isNew == 1) {
|
2020-11-03 18:55:29 +01:00
|
|
|
await _dbHelper.removeEpisodeNewMark(episodeNew.enclosureUrl);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
await _queue.getPlaylist();
|
2020-12-31 19:08:46 +01:00
|
|
|
_queue.addToPlayListAt(episodeNew, 0);
|
2021-01-24 09:49:14 +01:00
|
|
|
updatePlaylist(_queue, updateEpisodes: false);
|
2020-10-28 13:10:43 +01:00
|
|
|
_backgroundAudioDuration = 0;
|
|
|
|
_backgroundAudioPosition = 0;
|
|
|
|
_seekSliderValue = 0;
|
|
|
|
_episode = episodeNew;
|
|
|
|
_playerRunning = true;
|
2021-03-09 17:23:23 +01:00
|
|
|
_playlist = _queue;
|
2020-10-28 13:10:43 +01:00
|
|
|
notifyListeners();
|
2020-12-20 10:35:39 +01:00
|
|
|
_startAudioService(_queue, position: startPosition);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
Future<void> loadEpisodeFromPlaylist(EpisodeBrief? episode) async {
|
|
|
|
if (_playlist!.episodes.contains(episode)) {
|
|
|
|
var index = _playlist!.episodes.indexOf(episode);
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.customAction('changeIndex', {'index': index});
|
2020-12-23 15:03:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-31 19:08:46 +01:00
|
|
|
Future<void> _startAudioService(Playlist playlist,
|
2020-12-20 10:35:39 +01:00
|
|
|
{int index = 0, int position = 0}) async {
|
2020-10-28 13:10:43 +01:00
|
|
|
_stopOnComplete = false;
|
|
|
|
_sleepTimerMode = SleepTimerMode.undefined;
|
|
|
|
_switchValue = 0;
|
|
|
|
|
|
|
|
/// Get fastword and rewind seconds.
|
|
|
|
_fastForwardSeconds =
|
|
|
|
await _fastForwardSecondsStorage.getInt(defaultValue: 30);
|
|
|
|
_rewindSeconds = await _rewindSecondsStorage.getInt(defaultValue: 10);
|
|
|
|
|
|
|
|
/// Get if auto mark listened after skip
|
|
|
|
_markListened =
|
|
|
|
await _markListenedAfterSkipStorage.getBool(defaultValue: false);
|
|
|
|
|
|
|
|
/// Start audio service.
|
|
|
|
|
|
|
|
//Check autoplay setting, if true only add one episode, else add playlist.
|
|
|
|
await _getAutoPlay();
|
|
|
|
if (_autoPlay) {
|
2021-05-01 08:17:56 +02:00
|
|
|
await _audioHandler.addQueueItems(
|
2022-04-30 17:16:19 +02:00
|
|
|
([for (var episode in playlist.episodes) episode!.toMediaItem()]));
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2021-05-01 08:17:56 +02:00
|
|
|
await _audioHandler
|
2022-04-30 17:16:19 +02:00
|
|
|
.addQueueItems([playlist.episodes[index]!.toMediaItem()]);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2021-09-05 08:57:02 +02:00
|
|
|
|
|
|
|
await _audioHandler.play();
|
2020-10-28 13:10:43 +01:00
|
|
|
//Check auto sleep timer setting
|
|
|
|
await _getAutoSleepTimer();
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_autoSleepTimer!) {
|
2020-10-28 13:10:43 +01:00
|
|
|
var startTime =
|
2022-04-30 17:16:19 +02:00
|
|
|
await (_autoSleepTimerStartStorage.getInt(defaultValue: 1380) as FutureOr<int>);
|
|
|
|
var endTime = await (_autoSleepTimerEndStorage.getInt(defaultValue: 360) as FutureOr<int>);
|
2020-10-28 13:10:43 +01:00
|
|
|
var currentTime = DateTime.now().hour * 60 + DateTime.now().minute;
|
|
|
|
if ((startTime > endTime &&
|
|
|
|
(currentTime > startTime || currentTime < endTime)) ||
|
|
|
|
((startTime < endTime) &&
|
|
|
|
(currentTime > startTime && currentTime < endTime))) {
|
2022-04-30 17:16:19 +02:00
|
|
|
var mode = await (_autoSleepTimerModeStorage.getInt() as FutureOr<int>);
|
2020-10-28 13:10:43 +01:00
|
|
|
_sleepTimerMode = SleepTimerMode.values[mode];
|
|
|
|
var defaultTimer =
|
|
|
|
await _defaultSleepTimerStorage.getInt(defaultValue: 30);
|
|
|
|
sleepTimer(defaultTimer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
/// Set if playlist is queue.
|
|
|
|
await _audioHandler
|
|
|
|
.customAction('setIsQueue', {'isQueue': playlist.isQueue});
|
|
|
|
|
2020-10-28 13:10:43 +01:00
|
|
|
/// Set player speed.
|
|
|
|
if (_currentSpeed != 1.0) {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.customAction('setSpeed', {'speed': _currentSpeed});
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set slipsilence.
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_skipSilence!) {
|
2021-05-01 18:02:12 +02:00
|
|
|
await _audioHandler
|
|
|
|
.customAction('setSkipSilence', {'skipSilence': skipSilence});
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set boostValome.
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_boostVolume!) {
|
2021-05-01 18:02:12 +02:00
|
|
|
await _audioHandler.customAction(
|
|
|
|
'setBoostVolume', {'boostVolume': _boostVolume, 'gain': _volumeGain});
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2021-05-01 08:17:56 +02:00
|
|
|
_audioHandler.play();
|
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
_mediaitemSubscription =
|
|
|
|
_audioHandler.mediaItem.where((event) => event != null).listen(
|
2021-05-01 08:17:56 +02:00
|
|
|
(item) async {
|
2022-04-30 17:16:19 +02:00
|
|
|
var episode = await _dbHelper.getRssItemWithMediaId(item!.id);
|
2021-05-01 08:17:56 +02:00
|
|
|
if (episode == null) {
|
2022-04-30 17:16:19 +02:00
|
|
|
episode = _playFromSearchList.firstWhere((e) => e!.mediaId == item.id,
|
2021-05-01 08:17:56 +02:00
|
|
|
orElse: () => null);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2021-05-01 08:17:56 +02:00
|
|
|
if (episode != null) {
|
|
|
|
_episode = episode;
|
2021-05-01 18:02:12 +02:00
|
|
|
_backgroundAudioDuration = item.duration?.inMilliseconds ?? 0;
|
2021-05-01 08:17:56 +02:00
|
|
|
if (position > 0 &&
|
|
|
|
_backgroundAudioDuration > 0 &&
|
2022-04-30 17:16:19 +02:00
|
|
|
_episode!.enclosureUrl == _playlist!.episodeList[index]) {
|
2021-05-01 08:17:56 +02:00
|
|
|
await _audioHandler.seek(Duration(milliseconds: position));
|
|
|
|
position = 0;
|
|
|
|
}
|
|
|
|
notifyListeners();
|
|
|
|
} else {
|
|
|
|
_audioHandler.skipToNext();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
_playbackStateSubscription = _audioHandler.playbackState
|
2020-10-28 13:10:43 +01:00
|
|
|
.listen((event) async {
|
|
|
|
_current = DateTime.now();
|
|
|
|
_audioState = event.processingState;
|
2022-04-30 17:16:19 +02:00
|
|
|
_playing = event.playing;
|
2020-10-28 13:10:43 +01:00
|
|
|
_currentSpeed = event.speed;
|
2021-05-01 08:17:56 +02:00
|
|
|
_currentPosition = event.updatePosition.inMilliseconds ?? 0;
|
2021-04-09 16:23:15 +02:00
|
|
|
if (_audioState == AudioProcessingState.completed) {
|
2020-10-28 13:10:43 +01:00
|
|
|
if (_switchValue > 0) _switchValue = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get error state.
|
|
|
|
if (_audioState == AudioProcessingState.error) {
|
|
|
|
_remoteErrorMessage = 'Network Error';
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reset error state.
|
|
|
|
if (_audioState != AudioProcessingState.error) {
|
|
|
|
_remoteErrorMessage = null;
|
|
|
|
}
|
|
|
|
notifyListeners();
|
|
|
|
});
|
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
_customEventSubscription =
|
|
|
|
_audioHandler.customEvent.distinct().listen((event) async {
|
|
|
|
if (event is Map && event['removePlayed'] != null) {
|
2021-09-05 08:57:02 +02:00
|
|
|
log(event.toString());
|
2022-04-30 17:16:19 +02:00
|
|
|
log(_queue.episodes.first!.title!);
|
|
|
|
if (_playlist!.isQueue &&
|
2021-01-02 09:21:05 +01:00
|
|
|
_queue.isNotEmpty &&
|
2022-04-30 17:16:19 +02:00
|
|
|
_queue.episodes.first!.title == event['removePlayed']) {
|
2021-09-05 08:57:02 +02:00
|
|
|
log(event['removePlayed']);
|
2020-12-20 10:35:39 +01:00
|
|
|
_queue.delFromPlaylist(_episode);
|
2021-01-02 09:21:05 +01:00
|
|
|
updatePlaylist(_queue, updateEpisodes: false);
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
2021-01-02 09:21:05 +01:00
|
|
|
_lastPosition = 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
notifyListeners();
|
2020-12-20 10:35:39 +01:00
|
|
|
// await _positionStorage.saveInt(_lastPostion);
|
|
|
|
await _playerStateStorage.savePlayerState(
|
2022-04-30 17:16:19 +02:00
|
|
|
_playlist!.id, _episode!.enclosureUrl, _lastPosition);
|
2020-10-28 13:10:43 +01:00
|
|
|
var history;
|
|
|
|
if (_markListened) {
|
2022-04-30 17:16:19 +02:00
|
|
|
history = PlayHistory(_episode!.title, _episode!.enclosureUrl,
|
|
|
|
_backgroundAudioPosition! ~/ 1000, 1);
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2022-04-30 17:16:19 +02:00
|
|
|
history = PlayHistory(_episode!.title, _episode!.enclosureUrl,
|
|
|
|
_backgroundAudioPosition! ~/ 1000, _seekSliderValue);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2020-11-03 18:55:29 +01:00
|
|
|
await _dbHelper.saveHistory(history);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
if (event is Map && event['playerRunning'] == false && _playerRunning) {
|
|
|
|
_playerRunning = false;
|
|
|
|
notifyListeners();
|
2021-01-02 09:21:05 +01:00
|
|
|
if (_lastPosition > 0) {
|
2022-04-30 17:16:19 +02:00
|
|
|
final history = PlayHistory(_episode!.title, _episode!.enclosureUrl,
|
2021-01-02 09:21:05 +01:00
|
|
|
_lastPosition ~/ 1000, _seekSliderValue);
|
2020-11-03 18:55:29 +01:00
|
|
|
await _dbHelper.saveHistory(history);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.isEmpty) {
|
2021-10-06 12:47:09 +02:00
|
|
|
_episode = null;
|
|
|
|
_backgroundAudioDuration = 0;
|
|
|
|
_backgroundAudioPosition = 0;
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2021-05-01 08:17:56 +02:00
|
|
|
if (event is Map && event['position'] != null) {
|
|
|
|
_backgroundAudioPosition = event['position'].inMilliseconds;
|
2020-10-28 13:10:43 +01:00
|
|
|
if (_backgroundAudioDuration != null &&
|
|
|
|
_backgroundAudioDuration != 0 &&
|
|
|
|
_backgroundAudioPosition != null) {
|
|
|
|
_seekSliderValue =
|
2022-04-30 17:16:19 +02:00
|
|
|
_backgroundAudioPosition! / _backgroundAudioDuration ?? 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
|
|
|
_seekSliderValue = 0;
|
|
|
|
}
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
/// Queue management
|
2020-10-28 13:10:43 +01:00
|
|
|
Future<void> addToPlaylist(EpisodeBrief episode) async {
|
2022-04-30 17:16:19 +02:00
|
|
|
var episodeNew = await (_dbHelper.getRssItemWithUrl(episode.enclosureUrl) as FutureOr<EpisodeBrief>);
|
2020-12-20 10:35:39 +01:00
|
|
|
if (episodeNew.isNew == 1) {
|
|
|
|
await _dbHelper.removeEpisodeNewMark(episodeNew.enclosureUrl);
|
|
|
|
}
|
|
|
|
if (!_queue.episodes.contains(episodeNew)) {
|
2022-04-30 17:16:19 +02:00
|
|
|
if (playerRunning && _playlist!.isQueue) {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.addQueueItem(episodeNew.toMediaItem());
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.isQueue && _queue.isEmpty) _episode = episodeNew;
|
2020-12-20 10:35:39 +01:00
|
|
|
_queue.addToPlayList(episodeNew);
|
|
|
|
await updatePlaylist(_queue, updateEpisodes: false);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> addToPlaylistAt(EpisodeBrief episode, int index) async {
|
2022-04-30 17:16:19 +02:00
|
|
|
var episodeNew = await (_dbHelper.getRssItemWithUrl(episode.enclosureUrl) as FutureOr<EpisodeBrief>);
|
2020-12-20 10:35:39 +01:00
|
|
|
if (episodeNew.isNew == 1) {
|
|
|
|
await _dbHelper.removeEpisodeNewMark(episodeNew.enclosureUrl);
|
|
|
|
}
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playerRunning && _playlist!.isQueue) {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.customAction('addQueueItemAt',
|
|
|
|
{'mediaItem': episodeNew.toMediaItem(), 'index': index});
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
_queue.addToPlayListAt(episodeNew, index);
|
|
|
|
await updatePlaylist(_queue, updateEpisodes: false);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> addNewEpisode(List<String> group) async {
|
|
|
|
var newEpisodes = <EpisodeBrief>[];
|
|
|
|
if (group.isEmpty) {
|
2020-11-03 18:55:29 +01:00
|
|
|
newEpisodes = await _dbHelper.getRecentNewRssItem();
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2020-11-03 18:55:29 +01:00
|
|
|
newEpisodes = await _dbHelper.getGroupNewRssItem(group);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
if (newEpisodes.length > 0 && newEpisodes.length < 100) {
|
|
|
|
for (var episode in newEpisodes) {
|
|
|
|
await addToPlaylist(episode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (group.isEmpty) {
|
2020-11-03 18:55:29 +01:00
|
|
|
await _dbHelper.removeAllNewMark();
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2020-11-03 18:55:29 +01:00
|
|
|
await _dbHelper.removeGroupNewMark(group);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> updateMediaItem(EpisodeBrief episode) async {
|
2021-01-02 09:21:05 +01:00
|
|
|
if (episode.enclosureUrl == episode.mediaId &&
|
|
|
|
_episode != episode &&
|
2022-04-30 17:16:19 +02:00
|
|
|
_playlist!.contains(episode)) {
|
2020-12-20 10:35:39 +01:00
|
|
|
var episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl);
|
2022-04-30 17:16:19 +02:00
|
|
|
_playlist!.updateEpisode(episodeNew);
|
2021-01-02 09:21:05 +01:00
|
|
|
if (_playerRunning) {
|
2022-04-30 17:16:19 +02:00
|
|
|
await _audioHandler.updateMediaItem(episodeNew!.toMediaItem());
|
2021-01-02 09:21:05 +01:00
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<int> delFromPlaylist(EpisodeBrief episode) async {
|
2020-11-03 18:55:29 +01:00
|
|
|
var episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl);
|
2022-04-30 17:16:19 +02:00
|
|
|
if (playerRunning && _playlist!.isQueue) {
|
|
|
|
await _audioHandler.removeQueueItem(episodeNew!.toMediaItem());
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
var index = _queue.delFromPlaylist(episodeNew);
|
2020-10-28 13:10:43 +01:00
|
|
|
if (index == 0) {
|
2021-01-02 09:21:05 +01:00
|
|
|
_lastPosition = 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
await _positionStorage.saveInt(0);
|
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
updatePlaylist(_queue, updateEpisodes: false);
|
2020-10-28 13:10:43 +01:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future reorderPlaylist(int oldIndex, int newIndex) async {
|
2021-01-02 09:21:05 +01:00
|
|
|
if (newIndex > oldIndex) {
|
|
|
|
newIndex -= 1;
|
|
|
|
}
|
2022-04-30 17:16:19 +02:00
|
|
|
var episode = _queue.episodes[oldIndex]!;
|
2021-01-02 11:49:17 +01:00
|
|
|
_queue.addToPlayListAt(episode, newIndex);
|
|
|
|
updatePlaylist(_queue, updateEpisodes: false);
|
2022-04-30 17:16:19 +02:00
|
|
|
if (playerRunning && _playlist!.name == 'Queue') {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.removeQueueItem(episode.toMediaItem());
|
|
|
|
await _audioHandler.customAction('addQueueItemAt',
|
|
|
|
{'mediaItem': episode.toMediaItem(), 'index': newIndex});
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
if (newIndex == 0) {
|
2021-01-02 09:21:05 +01:00
|
|
|
_lastPosition = 0;
|
2020-10-28 13:10:43 +01:00
|
|
|
await _positionStorage.saveInt(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> moveToTop(EpisodeBrief episode) async {
|
|
|
|
await delFromPlaylist(episode);
|
2021-01-02 09:21:05 +01:00
|
|
|
final episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl);
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playerRunning && _playlist!.isQueue) {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.customAction(
|
2022-04-30 17:16:19 +02:00
|
|
|
'', {'mediaItem': episodeNew!.toMediaItem(), 'index': 1});
|
2020-12-20 10:35:39 +01:00
|
|
|
_queue.addToPlayListAt(episode, 1, existed: false);
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2020-12-20 10:35:39 +01:00
|
|
|
_queue.addToPlayListAt(episode, 0, existed: false);
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.isQueue) {
|
2021-01-02 09:21:05 +01:00
|
|
|
_lastPosition = 0;
|
|
|
|
_positionStorage.saveInt(_lastPosition);
|
|
|
|
_episode = episodeNew;
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
updatePlaylist(_queue, updateEpisodes: false);
|
2020-10-28 13:10:43 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-02 09:21:05 +01:00
|
|
|
/// Custom playlist management.
|
2020-12-20 10:35:39 +01:00
|
|
|
void addPlaylist(Playlist playlist) {
|
|
|
|
_playlists = [..._playlists, playlist];
|
|
|
|
notifyListeners();
|
|
|
|
_savePlaylists();
|
|
|
|
}
|
|
|
|
|
|
|
|
void deletePlaylist(Playlist playlist) {
|
|
|
|
_playlists = [
|
|
|
|
for (var p in _playlists)
|
|
|
|
if (p != playlist) p
|
|
|
|
];
|
2021-01-01 16:42:59 +01:00
|
|
|
if (_playlist == playlist && !_playerRunning) {
|
|
|
|
_playlist = _queue;
|
2022-04-30 17:16:19 +02:00
|
|
|
_episode = _playlist!.isNotEmpty ? _queue.episodes.first : null;
|
2021-01-01 16:42:59 +01:00
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
notifyListeners();
|
|
|
|
_savePlaylists();
|
2022-04-30 17:16:19 +02:00
|
|
|
if (playlist.isLocal!) {
|
2021-02-17 16:25:23 +01:00
|
|
|
_dbHelper.deleteLocalEpisodes(playlist.episodeList);
|
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
void addEpisodesToPlaylist(Playlist playlist, {required List<EpisodeBrief> episodes}) {
|
2020-12-20 10:35:39 +01:00
|
|
|
for (var e in episodes) {
|
|
|
|
playlist.addToPlayList(e);
|
|
|
|
if (playerRunning && playlist == _playlist) {
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioHandler.addQueueItem(e.toMediaItem());
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
updatePlaylist(playlist, updateEpisodes: false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeEpisodeFromPlaylist(Playlist playlist,
|
2022-04-30 17:16:19 +02:00
|
|
|
{required List<EpisodeBrief> episodes}) {
|
2020-12-20 10:35:39 +01:00
|
|
|
for (var e in episodes) {
|
|
|
|
playlist.delFromPlaylist(e);
|
|
|
|
if (playerRunning && playlist == _playlist) {
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioHandler.removeQueueItem(e.toMediaItem());
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
updatePlaylist(playlist, updateEpisodes: false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void reorderEpisodesInPlaylist(Playlist playlist,
|
2022-04-30 17:16:19 +02:00
|
|
|
{required int oldIndex, required int newIndex}) async {
|
2020-12-20 10:35:39 +01:00
|
|
|
playlist.reorderPlaylist(oldIndex, newIndex);
|
|
|
|
|
|
|
|
if (playerRunning && playlist == _playlist) {
|
|
|
|
if (newIndex > oldIndex) {
|
|
|
|
newIndex -= 1;
|
|
|
|
}
|
2022-04-30 17:16:19 +02:00
|
|
|
await _audioHandler.removeQueueItem(episode!.toMediaItem());
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.customAction('addQueueItemAt',
|
2022-04-30 17:16:19 +02:00
|
|
|
{'mediaItem': episode!.toMediaItem(), 'index': newIndex});
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
updatePlaylist(playlist, updateEpisodes: false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clearPlaylist(Playlist playlist) {
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playerRunning && _playlist!.isQueue && playlist.isQueue) {
|
2021-01-02 09:21:05 +01:00
|
|
|
for (var e in playlist.episodes) {
|
|
|
|
if (e != _episode) {
|
2022-04-30 17:16:19 +02:00
|
|
|
delFromPlaylist(e!);
|
2021-01-02 09:21:05 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-02 15:53:16 +01:00
|
|
|
} else {
|
2021-01-02 09:21:05 +01:00
|
|
|
playlist.clear();
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.isQueue) _episode = null;
|
2021-01-02 15:53:16 +01:00
|
|
|
}
|
2020-12-20 10:35:39 +01:00
|
|
|
updatePlaylist(playlist, updateEpisodes: false);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> updatePlaylist(Playlist playlist,
|
|
|
|
{bool updateEpisodes = true}) async {
|
|
|
|
if (updateEpisodes) await playlist.getPlaylist();
|
|
|
|
_playlists = [for (var p in _playlists) p.id == playlist.id ? playlist : p];
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.id == playlist.id) {
|
2021-01-02 09:21:05 +01:00
|
|
|
if (playlist.isQueue) {
|
|
|
|
_playlist = _queue;
|
|
|
|
} else if (!_playerRunning) {
|
2022-04-30 17:16:19 +02:00
|
|
|
_playlist = _playlists.firstWhere((e) => e.id == _playlist!.id);
|
2021-01-02 09:21:05 +01:00
|
|
|
}
|
2021-01-01 16:42:59 +01:00
|
|
|
notifyListeners();
|
|
|
|
}
|
2021-01-02 09:21:05 +01:00
|
|
|
await _savePlaylists();
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
bool playlistExisted(String? name) {
|
2020-12-20 10:35:39 +01:00
|
|
|
for (var p in _playlists) {
|
|
|
|
if (p.name == name) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-31 05:34:19 +01:00
|
|
|
// void _updateAllPlaylists() {
|
|
|
|
// _playlists = [..._playlists];
|
|
|
|
// notifyListeners();
|
|
|
|
// _savePlaylists();
|
|
|
|
// }
|
2020-12-20 10:35:39 +01:00
|
|
|
|
|
|
|
Future<void> _savePlaylists() async {
|
|
|
|
await _playlistsStorgae
|
|
|
|
.savePlaylists([for (var p in _playlists) p.toEntity()]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Audio control.
|
2020-10-28 13:10:43 +01:00
|
|
|
Future<void> pauseAduio() async {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> resumeAudio() async {
|
|
|
|
_remoteErrorMessage = null;
|
|
|
|
notifyListeners();
|
2021-04-09 16:23:15 +02:00
|
|
|
if (_audioState != AudioProcessingState.loading) {
|
|
|
|
_audioHandler.play();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
Future<void> playNext() async {
|
|
|
|
_remoteErrorMessage = null;
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playlist!.isQueue && _queue.isNotEmpty) {
|
2021-09-05 08:57:02 +02:00
|
|
|
_queue.delFromPlaylist(_episode);
|
|
|
|
updatePlaylist(_queue, updateEpisodes: false);
|
|
|
|
}
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.skipToNext();
|
2020-12-20 10:35:39 +01:00
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
2020-10-28 13:10:43 +01:00
|
|
|
Future<void> forwardAudio(int s) async {
|
2022-04-30 17:16:19 +02:00
|
|
|
var pos = _backgroundAudioPosition! + s * 1000;
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.seek(Duration(milliseconds: pos));
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> fastForward() async {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.fastForward();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> rewind() async {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.rewind();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> seekTo(int position) async {
|
2021-04-09 16:23:15 +02:00
|
|
|
if (_audioState != AudioProcessingState.loading) {
|
|
|
|
await _audioHandler.seek(Duration(milliseconds: position));
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> sliderSeek(double val) async {
|
2021-04-09 16:23:15 +02:00
|
|
|
if (_audioState != AudioProcessingState.loading) {
|
2020-10-28 13:10:43 +01:00
|
|
|
_noSlide = false;
|
|
|
|
_seekSliderValue = val;
|
|
|
|
notifyListeners();
|
|
|
|
_currentPosition = (val * _backgroundAudioDuration).toInt();
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.seek(Duration(milliseconds: _currentPosition));
|
2020-10-28 13:10:43 +01:00
|
|
|
_noSlide = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set player speed.
|
|
|
|
Future<void> setSpeed(double speed) async {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _audioHandler.customAction('setSpeed', {'speed': speed});
|
2020-10-28 13:10:43 +01:00
|
|
|
_currentSpeed = speed;
|
2022-04-30 17:16:19 +02:00
|
|
|
await _speedStorage.saveDouble(_currentSpeed!);
|
2020-10-28 13:10:43 +01:00
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
// Set skip silence.
|
2022-04-30 17:16:19 +02:00
|
|
|
Future<void> setSkipSilence({required bool skipSilence}) async {
|
2021-05-01 18:02:12 +02:00
|
|
|
await _audioHandler
|
|
|
|
.customAction('setSkipSilence', {'skipSilence': skipSilence});
|
|
|
|
_skipSilence = skipSilence;
|
|
|
|
await _skipSilenceStorage.saveBool(_skipSilence);
|
|
|
|
notifyListeners();
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
set setVolumeGain(int volumeGain) {
|
|
|
|
_volumeGain = volumeGain;
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_playerRunning && _boostVolume!) {
|
2021-05-01 18:02:12 +02:00
|
|
|
setBoostVolume(boostVolume: _boostVolume, gain: _volumeGain);
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
notifyListeners();
|
|
|
|
_volumeGainStorage.saveInt(volumeGain);
|
|
|
|
}
|
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
Future<void> setBoostVolume({required bool? boostVolume, int? gain}) async {
|
2021-05-01 18:02:12 +02:00
|
|
|
await _audioHandler.customAction(
|
|
|
|
'setBoostVolume', {'boostVolume': boostVolume, 'gain': _volumeGain});
|
|
|
|
_boostVolume = boostVolume;
|
|
|
|
notifyListeners();
|
|
|
|
await _boostVolumeStorage.saveBool(boostVolume);
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
|
|
|
|
//Set sleep timer
|
2022-04-30 17:16:19 +02:00
|
|
|
void sleepTimer(int? mins) {
|
2020-10-28 13:10:43 +01:00
|
|
|
if (_sleepTimerMode == SleepTimerMode.timer) {
|
|
|
|
_startSleepTimer = true;
|
|
|
|
_switchValue = 1;
|
|
|
|
notifyListeners();
|
2022-04-30 17:16:19 +02:00
|
|
|
_timeLeft = mins! * 60;
|
2020-10-28 13:10:43 +01:00
|
|
|
Timer.periodic(Duration(seconds: 1), (timer) {
|
|
|
|
if (_timeLeft == 0) {
|
|
|
|
timer.cancel();
|
|
|
|
notifyListeners();
|
|
|
|
} else {
|
|
|
|
_timeLeft = _timeLeft - 1;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
_stopTimer = Timer(Duration(minutes: mins), () {
|
|
|
|
_stopOnComplete = false;
|
|
|
|
_startSleepTimer = false;
|
|
|
|
_switchValue = 0;
|
|
|
|
if (_playerRunning) {
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioHandler.stop();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
notifyListeners();
|
|
|
|
// AudioService.disconnect();
|
|
|
|
});
|
|
|
|
} else if (_sleepTimerMode == SleepTimerMode.endOfEpisode) {
|
|
|
|
_stopOnComplete = true;
|
|
|
|
_switchValue = 1;
|
|
|
|
notifyListeners();
|
2020-12-20 10:35:39 +01:00
|
|
|
if (_queue.episodes.length > 1 && _autoPlay) {
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioHandler.customAction('stopAtEnd', {});
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-20 10:35:39 +01:00
|
|
|
set setSleepTimerMode(SleepTimerMode timer) {
|
|
|
|
_sleepTimerMode = timer;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
2020-10-28 13:10:43 +01:00
|
|
|
//Cancel sleep timer
|
|
|
|
void cancelTimer() {
|
|
|
|
if (_sleepTimerMode == SleepTimerMode.timer) {
|
|
|
|
_stopTimer.cancel();
|
|
|
|
_timeLeft = 0;
|
|
|
|
_startSleepTimer = false;
|
|
|
|
_switchValue = 0;
|
|
|
|
notifyListeners();
|
|
|
|
} else if (_sleepTimerMode == SleepTimerMode.endOfEpisode) {
|
2021-04-09 16:23:15 +02:00
|
|
|
_audioHandler.customAction('cancelStopAtEnd', {});
|
2020-10-28 13:10:43 +01:00
|
|
|
_switchValue = 0;
|
|
|
|
_stopOnComplete = false;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-09 16:23:15 +02:00
|
|
|
class CustomAudioHandler extends BaseAudioHandler
|
|
|
|
with QueueHandler, SeekHandler {
|
2020-10-28 13:10:43 +01:00
|
|
|
final cacheStorage = KeyValueStorage(cacheMaxKey);
|
|
|
|
final layoutStorage = KeyValueStorage(notificationLayoutKey);
|
2021-04-09 16:23:15 +02:00
|
|
|
final AudioPlayer _player = AudioPlayer();
|
2020-10-28 13:10:43 +01:00
|
|
|
bool _interrupted = false;
|
2022-04-30 17:16:19 +02:00
|
|
|
int? _layoutIndex;
|
2021-05-01 18:02:12 +02:00
|
|
|
bool _stopAtEnd = false;
|
2022-04-30 17:16:19 +02:00
|
|
|
bool? _isQueue = false;
|
2021-09-05 08:57:02 +02:00
|
|
|
bool _autoSkip = true;
|
|
|
|
|
|
|
|
ConcatenatingAudioSource _playlist = ConcatenatingAudioSource(
|
|
|
|
useLazyPreparation: true,
|
|
|
|
shuffleOrder: DefaultShuffleOrder(),
|
|
|
|
children: [],
|
|
|
|
);
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
bool get hasNext => queue.value!.length > 0;
|
|
|
|
MediaItem? get currentMediaItem => mediaItem.value;
|
|
|
|
bool get playing => playbackState.value!.playing;
|
2021-04-09 16:23:15 +02:00
|
|
|
|
2021-08-21 11:25:52 +02:00
|
|
|
PublishSubject<Map<String, dynamic>> customEvent = PublishSubject()..add({});
|
2021-04-09 16:23:15 +02:00
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
CustomAudioHandler(int cacheMax) {
|
|
|
|
_player.cacheMax = cacheMax;
|
2021-05-01 08:17:56 +02:00
|
|
|
_handleInterruption();
|
|
|
|
_player.currentIndexStream.listen(
|
|
|
|
(index) {
|
2022-04-30 17:16:19 +02:00
|
|
|
if (queue.value!.isNotEmpty && index! < queue.value!.length) {
|
|
|
|
mediaItem.add(queue.value![index]);
|
2021-05-01 08:17:56 +02:00
|
|
|
}
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_isQueue! && _autoSkip) {
|
|
|
|
customEvent.add({'removePlayed': queue.value!.first.title});
|
2021-05-01 18:02:12 +02:00
|
|
|
}
|
2021-09-05 08:57:02 +02:00
|
|
|
_autoSkip = true;
|
2021-05-01 08:17:56 +02:00
|
|
|
},
|
|
|
|
);
|
|
|
|
_player.playbackEventStream.listen((event) async {
|
|
|
|
if (_layoutIndex == null) {
|
|
|
|
_layoutIndex = await layoutStorage.getInt();
|
|
|
|
}
|
2022-04-30 17:16:19 +02:00
|
|
|
playbackState.add(playbackState.value!.copyWith(
|
2021-05-01 08:17:56 +02:00
|
|
|
controls: _getControls(_layoutIndex),
|
|
|
|
androidCompactActionIndices: [0, 1, 2],
|
2021-04-09 16:23:15 +02:00
|
|
|
systemActions: {
|
|
|
|
MediaAction.seek,
|
|
|
|
MediaAction.seekForward,
|
|
|
|
MediaAction.seekBackward,
|
|
|
|
},
|
|
|
|
processingState: {
|
|
|
|
ProcessingState.idle: AudioProcessingState.idle,
|
|
|
|
ProcessingState.loading: AudioProcessingState.loading,
|
|
|
|
ProcessingState.buffering: AudioProcessingState.buffering,
|
|
|
|
ProcessingState.ready: AudioProcessingState.ready,
|
|
|
|
ProcessingState.completed: AudioProcessingState.completed,
|
2022-04-30 17:16:19 +02:00
|
|
|
}[_player.processingState]!,
|
2021-04-09 16:23:15 +02:00
|
|
|
playing: _player.playing,
|
|
|
|
updatePosition: _player.position,
|
2022-04-30 17:16:19 +02:00
|
|
|
queueIndex: _player.currentIndex!,
|
2021-04-09 16:23:15 +02:00
|
|
|
bufferedPosition: _player.bufferedPosition,
|
|
|
|
speed: _player.speed,
|
|
|
|
));
|
|
|
|
});
|
2021-05-01 08:17:56 +02:00
|
|
|
|
|
|
|
_player.positionStream.listen((event) {
|
|
|
|
customEvent.add({'position': event});
|
|
|
|
});
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
_player.sequenceStream.listen((event) {
|
|
|
|
log(event.toString());
|
|
|
|
});
|
|
|
|
|
2021-05-01 08:17:56 +02:00
|
|
|
_player.durationStream.listen((event) {
|
2022-04-30 17:16:19 +02:00
|
|
|
mediaItem.add(mediaItem.value!.copyWith(duration: _player.duration!));
|
2021-05-01 08:17:56 +02:00
|
|
|
});
|
2021-04-09 16:23:15 +02:00
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
@override
|
2021-05-01 08:17:56 +02:00
|
|
|
Future<void> addQueueItems(List<MediaItem> items) async {
|
2021-09-05 08:57:02 +02:00
|
|
|
queue.add(items);
|
|
|
|
_setAudioSource(items);
|
|
|
|
_player.setAudioSource(_playlist);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _setAudioSource(List<MediaItem> items) {
|
|
|
|
_playlist.insertAll(
|
|
|
|
0,
|
|
|
|
[for (var item in items) _itemToSource(item)],
|
2021-05-01 08:17:56 +02:00
|
|
|
);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2021-05-01 08:17:56 +02:00
|
|
|
void _handleInterruption() async {
|
|
|
|
final session = await AudioSession.instance;
|
|
|
|
await session.configure(AudioSessionConfiguration.speech());
|
2020-10-28 13:10:43 +01:00
|
|
|
session.interruptionEventStream.listen((event) {
|
|
|
|
if (event.begin) {
|
|
|
|
switch (event.type) {
|
|
|
|
case AudioInterruptionType.pause:
|
2021-05-01 08:17:56 +02:00
|
|
|
if (playing) {
|
2021-04-09 16:23:15 +02:00
|
|
|
pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
_interrupted = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AudioInterruptionType.duck:
|
2021-05-01 08:17:56 +02:00
|
|
|
if (playing) {
|
2021-04-09 16:23:15 +02:00
|
|
|
pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
_interrupted = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AudioInterruptionType.unknown:
|
2021-05-01 08:17:56 +02:00
|
|
|
if (playing) {
|
2021-04-09 16:23:15 +02:00
|
|
|
pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
_interrupted = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (event.type) {
|
|
|
|
case AudioInterruptionType.pause:
|
2021-05-01 08:17:56 +02:00
|
|
|
if (!playing && _interrupted) {
|
2021-04-09 16:23:15 +02:00
|
|
|
play();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AudioInterruptionType.duck:
|
2021-05-01 08:17:56 +02:00
|
|
|
if (!playing && _interrupted) {
|
2021-04-09 16:23:15 +02:00
|
|
|
play();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AudioInterruptionType.unknown:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_interrupted = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
session.becomingNoisyEventStream.listen((_) {
|
2021-05-01 08:17:56 +02:00
|
|
|
if (playing) pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void playPause() {
|
2022-04-30 17:16:19 +02:00
|
|
|
if (playbackState.value!.playing) {
|
2021-04-09 16:23:15 +02:00
|
|
|
pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2021-04-09 16:23:15 +02:00
|
|
|
play();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
@override
|
2021-04-09 16:23:15 +02:00
|
|
|
Future<void> skipToNext() async {
|
2022-04-30 17:16:19 +02:00
|
|
|
if (queue.value!.length == 1 || _stopAtEnd) {
|
2020-10-28 13:10:43 +01:00
|
|
|
await Future.delayed(Duration(milliseconds: 200));
|
2021-04-09 16:23:15 +02:00
|
|
|
await stop();
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2021-09-05 08:57:02 +02:00
|
|
|
_autoSkip = false;
|
2021-05-01 08:17:56 +02:00
|
|
|
await super.skipToNext();
|
2021-05-01 18:02:12 +02:00
|
|
|
_player.seekToNext();
|
2022-04-30 17:16:19 +02:00
|
|
|
if (_isQueue!) {
|
2021-05-01 18:02:12 +02:00
|
|
|
removeQueueItemAt(0);
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
@override
|
2021-04-09 16:23:15 +02:00
|
|
|
Future<void> play() async {
|
2021-10-06 12:47:09 +02:00
|
|
|
if (playing == null || playing == false) {
|
2021-09-05 08:57:02 +02:00
|
|
|
log('playing');
|
2021-05-01 08:17:56 +02:00
|
|
|
await super.play();
|
2021-09-05 08:57:02 +02:00
|
|
|
await _player.play();
|
2021-04-09 16:23:15 +02:00
|
|
|
} else {
|
2021-05-01 08:17:56 +02:00
|
|
|
super.play();
|
2021-04-09 16:23:15 +02:00
|
|
|
await _player.play();
|
|
|
|
await _seekRelative(Duration(seconds: -3));
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
@override
|
|
|
|
Future<void> addQueueItem(MediaItem item) async {
|
2022-04-30 17:16:19 +02:00
|
|
|
_addQueueItemAt(item, queue.value!.length);
|
2021-09-05 08:57:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2021-05-01 18:02:12 +02:00
|
|
|
Future<void> removeQueueItemAt(int index) async {
|
2022-04-30 17:16:19 +02:00
|
|
|
queue.add(queue.value!..removeAt(index));
|
2021-09-05 08:57:02 +02:00
|
|
|
_playlist.removeAt(index);
|
2021-05-01 18:02:12 +02:00
|
|
|
super.removeQueueItemAt(index);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
@override
|
2021-04-09 16:23:15 +02:00
|
|
|
Future<void> pause() async {
|
2021-05-01 08:17:56 +02:00
|
|
|
await _player.pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
@override
|
2021-05-01 08:17:56 +02:00
|
|
|
Future<void> seek(Duration position) async {
|
2021-04-09 16:23:15 +02:00
|
|
|
await _player.seek(position);
|
2021-05-01 08:17:56 +02:00
|
|
|
super.seek(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> fastForward() async {
|
|
|
|
_seekRelative(AudioService.config.fastForwardInterval);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> rewind() async {
|
|
|
|
_seekRelative(-AudioService.config.rewindInterval);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> onClick(MediaButton button) async {
|
|
|
|
switch (button) {
|
|
|
|
case MediaButton.media:
|
2021-04-09 16:23:15 +02:00
|
|
|
if (playing) {
|
|
|
|
await pause();
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2021-04-09 16:23:15 +02:00
|
|
|
await play();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MediaButton.next:
|
2021-04-09 16:23:15 +02:00
|
|
|
await fastForward();
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
case MediaButton.previous:
|
2021-04-09 16:23:15 +02:00
|
|
|
await rewind();
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _seekRelative(Duration offset) async {
|
2022-04-30 17:16:19 +02:00
|
|
|
var newPosition = playbackState.value!.position + offset;
|
2020-10-28 13:10:43 +01:00
|
|
|
if (newPosition < Duration.zero) newPosition = Duration.zero;
|
2021-05-01 08:17:56 +02:00
|
|
|
seek(newPosition);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2021-04-09 16:23:15 +02:00
|
|
|
Future<void> stop() async {
|
|
|
|
await _player.stop();
|
2021-10-06 12:47:09 +02:00
|
|
|
// await _player.dispose();
|
|
|
|
_playlist.clear();
|
2021-04-09 16:23:15 +02:00
|
|
|
customEvent.add({'playerRunning': false});
|
|
|
|
await super.stop();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2021-04-09 16:23:15 +02:00
|
|
|
Future<void> taskRemoved() async {
|
|
|
|
await stop();
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2021-04-09 16:23:15 +02:00
|
|
|
Future<void> _addQueueItemAt(MediaItem item, int index) async {
|
2021-09-05 08:57:02 +02:00
|
|
|
log(index.toString() + ': ' + item.toString());
|
2022-04-30 17:16:19 +02:00
|
|
|
if (index == 0 && _isQueue!) {
|
|
|
|
queue.add(queue.value!..removeWhere((i) => i.id == item.id));
|
|
|
|
queue.add(queue.value!..insert(index, item));
|
2021-05-01 18:02:12 +02:00
|
|
|
skipToNext();
|
2020-10-28 13:10:43 +01:00
|
|
|
} else {
|
2022-04-30 17:16:19 +02:00
|
|
|
queue.add(queue.value!..insert(index, item));
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
2021-09-05 08:57:02 +02:00
|
|
|
_playlist.insert(index, _itemToSource(item));
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
|
2021-09-05 08:57:02 +02:00
|
|
|
@override
|
|
|
|
Future<dynamic> customAction(function, [argument]) async {
|
|
|
|
switch (function) {
|
2020-10-28 13:10:43 +01:00
|
|
|
case 'stopAtEnd':
|
|
|
|
_stopAtEnd = true;
|
|
|
|
break;
|
|
|
|
case 'cancelStopAtEnd':
|
|
|
|
_stopAtEnd = false;
|
|
|
|
break;
|
|
|
|
case 'setSpeed':
|
2022-04-30 17:16:19 +02:00
|
|
|
log('Argument' + argument!['speed'].toString());
|
2021-04-09 16:23:15 +02:00
|
|
|
await _player.setSpeed(argument['speed']);
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
case 'setSkipSilence':
|
2022-04-30 17:16:19 +02:00
|
|
|
await _setSkipSilence(argument!['skipSilence']);
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
case 'setBoostVolume':
|
2022-04-30 17:16:19 +02:00
|
|
|
await _setBoostVolume(argument!['boostVolume'], argument['gain']);
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
2020-12-20 10:35:39 +01:00
|
|
|
case 'setIsQueue':
|
2022-04-30 17:16:19 +02:00
|
|
|
log('Argument' + argument!['isQueue'].toString());
|
2021-04-09 16:23:15 +02:00
|
|
|
_isQueue = argument['isQueue'];
|
2020-12-20 10:35:39 +01:00
|
|
|
break;
|
|
|
|
case 'changeQueue':
|
2022-04-30 17:16:19 +02:00
|
|
|
await _changeQueue(argument!['queue']);
|
2020-12-23 15:03:07 +01:00
|
|
|
break;
|
|
|
|
case 'changeIndex':
|
2022-04-30 17:16:19 +02:00
|
|
|
await _changeIndex(argument!['index']);
|
2020-12-23 15:03:07 +01:00
|
|
|
break;
|
2021-04-09 16:23:15 +02:00
|
|
|
case 'addQueueItemAt':
|
2022-04-30 17:16:19 +02:00
|
|
|
await _addQueueItemAt(argument!['mediaItem'], argument['index']);
|
2021-09-05 08:57:02 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
super.customAction(function, argument);
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-09 16:23:15 +02:00
|
|
|
Future _changeQueue(List<MediaItem> newQueue) async {
|
|
|
|
await _player.stop();
|
|
|
|
queue.add(newQueue);
|
2021-05-01 18:02:12 +02:00
|
|
|
play();
|
2020-12-23 15:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Future _changeIndex(int index) async {
|
2021-05-01 08:17:56 +02:00
|
|
|
await super.skipToQueueItem(index);
|
2020-12-20 10:35:39 +01:00
|
|
|
}
|
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
Future _setSkipSilence(bool boo) async {
|
|
|
|
await _player.setSkipSilence(boo);
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2021-05-01 18:02:12 +02:00
|
|
|
Future _setBoostVolume(bool boo, int gain) async {
|
|
|
|
await _player.setBoostVolume(boo, gain);
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
|
2022-04-30 17:16:19 +02:00
|
|
|
List<MediaControl> _getControls(int? index) {
|
2020-10-28 13:10:43 +01:00
|
|
|
switch (index) {
|
|
|
|
case 0:
|
2020-11-06 15:04:08 +01:00
|
|
|
return [
|
2021-05-01 08:17:56 +02:00
|
|
|
playing ? pauseControl : playControl,
|
2020-11-06 15:04:08 +01:00
|
|
|
forwardControl,
|
|
|
|
skipToNextControl,
|
|
|
|
stopControl
|
|
|
|
];
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
case 1:
|
2020-11-06 15:04:08 +01:00
|
|
|
return [
|
2021-05-01 08:17:56 +02:00
|
|
|
playing ? pauseControl : playControl,
|
2020-11-06 15:04:08 +01:00
|
|
|
rewindControl,
|
|
|
|
skipToNextControl,
|
|
|
|
stopControl
|
|
|
|
];
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
case 2:
|
2020-11-06 15:04:08 +01:00
|
|
|
return [
|
|
|
|
rewindControl,
|
2021-05-01 08:17:56 +02:00
|
|
|
playing ? pauseControl : playControl,
|
2020-11-06 15:04:08 +01:00
|
|
|
forwardControl,
|
|
|
|
stopControl
|
|
|
|
];
|
|
|
|
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
default:
|
2020-11-06 15:04:08 +01:00
|
|
|
return [
|
2021-05-01 08:17:56 +02:00
|
|
|
playing ? pauseControl : playControl,
|
2020-11-06 15:04:08 +01:00
|
|
|
forwardControl,
|
|
|
|
skipToNextControl,
|
|
|
|
stopControl
|
|
|
|
];
|
2020-10-28 13:10:43 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-09-05 08:57:02 +02:00
|
|
|
|
|
|
|
static AudioSource _itemToSource(MediaItem item) {
|
|
|
|
return ClippingAudioSource(
|
2022-04-30 17:16:19 +02:00
|
|
|
start: Duration(seconds: item.extras!['skipSecondsStart']),
|
2021-09-05 08:57:02 +02:00
|
|
|
// end: Duration(seconds: item.extras['skipSecondsEnd']),
|
|
|
|
child: AudioSource.uri(Uri.parse(item.id)));
|
|
|
|
}
|
2020-10-28 13:10:43 +01:00
|
|
|
}
|