mirror of
https://github.com/stonega/tsacdop
synced 2025-02-13 01:50:39 +01:00
Custom playlist support.
This commit is contained in:
parent
1d8db22dde
commit
963415ded4
@ -359,14 +359,15 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
|
||||
topLeft: Radius.circular(10.0),
|
||||
topRight: Radius.circular(10.0)),
|
||||
),
|
||||
child: Selector<AudioPlayerNotifier, Tuple3<bool, Playlist, int>>(
|
||||
selector: (_, audio) =>
|
||||
Tuple3(audio.playerRunning, audio.queue, audio.lastPositin),
|
||||
child: Selector<AudioPlayerNotifier,
|
||||
Tuple3<bool, EpisodeBrief, int>>(
|
||||
selector: (_, audio) => Tuple3(
|
||||
audio.playerRunning, audio.episode, audio.lastPositin),
|
||||
builder: (_, data, __) => !_loadPlay
|
||||
? SizedBox(
|
||||
height: 8.0,
|
||||
)
|
||||
: data.item1 || data.item2.episodes.isEmpty
|
||||
: data.item1 || data.item2 == null
|
||||
? SizedBox(
|
||||
height: 8.0,
|
||||
)
|
||||
@ -390,8 +391,8 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
|
||||
children: <Widget>[
|
||||
CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundImage: data
|
||||
.item2.episodes.first.avatarImage),
|
||||
backgroundImage:
|
||||
data.item2.avatarImage),
|
||||
Container(
|
||||
height: 40.0,
|
||||
width: 40.0,
|
||||
@ -419,7 +420,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
|
||||
// TextStyle(color: Colors.white)
|
||||
),
|
||||
Text(
|
||||
data.item2.episodes.first.title,
|
||||
data.item2.title,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.fade,
|
||||
|
@ -56,6 +56,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
systemNavigationBarIconBrightness:
|
||||
@ -66,6 +67,12 @@ class _PlaylistHomeState extends State<PlaylistHome> {
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: CustomBackButton(),
|
||||
title: Selector<AudioPlayerNotifier, EpisodeBrief>(
|
||||
selector: (_, audio) => audio.episode,
|
||||
builder: (_, data, __) {
|
||||
return Text(data?.title ?? '', maxLines: 1);
|
||||
},
|
||||
),
|
||||
backgroundColor: context.scaffoldBackgroundColor,
|
||||
),
|
||||
body: Column(
|
||||
@ -126,23 +133,58 @@ class _PlaylistHomeState extends State<PlaylistHome> {
|
||||
})
|
||||
],
|
||||
),
|
||||
data.item4 != null
|
||||
? Padding(
|
||||
if (data.item2)
|
||||
Selector<AudioPlayerNotifier,
|
||||
Tuple3<bool, double, String>>(
|
||||
selector: (_, audio) => Tuple3(
|
||||
audio.buffering,
|
||||
(audio.backgroundAudioDuration -
|
||||
audio.backgroundAudioPosition) /
|
||||
1000,
|
||||
audio.remoteErrorMessage),
|
||||
builder: (_, data, __) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20.0),
|
||||
child: Text(data.item4.title, maxLines: 1),
|
||||
)
|
||||
: Center(),
|
||||
horizontal: 10),
|
||||
child: data.item3 != null
|
||||
? Text(data.item3,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFFFF0000)))
|
||||
: data.item1
|
||||
? Text(
|
||||
s.buffering,
|
||||
style: TextStyle(
|
||||
color: context.accentColor),
|
||||
)
|
||||
: Text(
|
||||
s.timeLeft((data.item2)
|
||||
.toInt()
|
||||
.toTime ??
|
||||
''),
|
||||
maxLines: 2,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
)),
|
||||
data.item3 != null
|
||||
? ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: SizedBox(
|
||||
width: 80,
|
||||
height: 80,
|
||||
child:
|
||||
Image(image: data.item4.avatarImage)),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (running) {
|
||||
context
|
||||
.read<AudioPlayerNotifier>()
|
||||
.playNext();
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 80,
|
||||
height: 80,
|
||||
child:
|
||||
Image(image: data.item4.avatarImage)),
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
@ -214,11 +256,13 @@ class __QueueState extends State<_Queue> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return Selector<AudioPlayerNotifier, Tuple2<Playlist, bool>>(
|
||||
selector: (_, audio) => Tuple2(audio.playlist, audio.playerRunning),
|
||||
return Selector<AudioPlayerNotifier, Tuple3<Playlist, bool, EpisodeBrief>>(
|
||||
selector: (_, audio) =>
|
||||
Tuple3(audio.playlist, audio.playerRunning, audio.episode),
|
||||
builder: (_, data, __) {
|
||||
var episodes = data.item1.episodes.toSet().toList();
|
||||
var queue = data.item1;
|
||||
var running = data.item2;
|
||||
return queue.name == 'Queue'
|
||||
? ReorderableListView(
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
@ -258,6 +302,8 @@ class __QueueState extends State<_Queue> {
|
||||
itemBuilder: (context, index) {
|
||||
final episode = queue.episodes[index];
|
||||
final c = episode.backgroudColor(context);
|
||||
final isPlaying =
|
||||
data.item3 != null && data.item3 == episode;
|
||||
return SizedBox(
|
||||
height: 90.0,
|
||||
child: Column(
|
||||
@ -265,11 +311,15 @@ class __QueueState extends State<_Queue> {
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ListTile(
|
||||
tileColor:
|
||||
isPlaying ? context.primaryColorDark : null,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 8),
|
||||
onTap: () async {
|
||||
await context
|
||||
.read<AudioPlayerNotifier>()
|
||||
.episodeLoad(episode);
|
||||
if (!isPlaying) {
|
||||
await context
|
||||
.read<AudioPlayerNotifier>()
|
||||
.loadEpisodeFromPlaylist(episode);
|
||||
}
|
||||
},
|
||||
title: Container(
|
||||
padding: EdgeInsets.fromLTRB(0, 5.0, 20.0, 5.0),
|
||||
@ -284,7 +334,8 @@ class __QueueState extends State<_Queue> {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.unfold_more, color: c),
|
||||
// Icon(Icons.unfold_more, color: c),
|
||||
SizedBox(width: 24),
|
||||
CircleAvatar(
|
||||
backgroundColor: c.withOpacity(0.5),
|
||||
backgroundImage: episode.avatarImage),
|
||||
@ -323,7 +374,18 @@ class __QueueState extends State<_Queue> {
|
||||
],
|
||||
),
|
||||
),
|
||||
//trailing: Icon(Icons.menu),
|
||||
trailing: isPlaying && running
|
||||
? Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
margin:
|
||||
EdgeInsets.symmetric(horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: WaveLoader(
|
||||
color: context.accentColor))
|
||||
: SizedBox(width: 1),
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
@ -688,7 +750,14 @@ class __PlaylistsState extends State<_Playlists> {
|
||||
style: context.textTheme.headline6,
|
||||
),
|
||||
Text('${queue.episodes.length} episodes'),
|
||||
OutlinedButton(
|
||||
TextButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(
|
||||
color: context.primaryColorDark),
|
||||
primary: context.accentColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(100)))),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<AudioPlayerNotifier>()
|
||||
@ -738,11 +807,11 @@ class __PlaylistsState extends State<_Playlists> {
|
||||
),
|
||||
title: Text(data[index].name),
|
||||
subtitle: Text(episodeList.isNotEmpty
|
||||
? s.episode(data[index].episodeList.length)
|
||||
? '${data[index].episodeList.length} episodes'
|
||||
: '0 episode'),
|
||||
trailing: IconButton(
|
||||
splashRadius: 20,
|
||||
icon: Icon(Icons.play_arrow),
|
||||
icon: Icon(LineIcons.play_circle_solid, size: 30),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<AudioPlayerNotifier>()
|
||||
|
@ -269,6 +269,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||
await _getAutoPlay();
|
||||
|
||||
var state = await _playerStateStorage.getPlayerState();
|
||||
print(state.toString());
|
||||
if (state[0] != '') {
|
||||
_playlist = _playlists.firstWhere((p) => p.id == state[0],
|
||||
orElse: () => _playlists.first);
|
||||
@ -306,7 +307,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||
|
||||
Future<void> playlistLoad(Playlist playlist) async {
|
||||
var p = playlist;
|
||||
if(playlist.name != 'Queue') {
|
||||
if (playlist.name != 'Queue') {
|
||||
await updatePlaylist(p, updateEpisodes: true);
|
||||
}
|
||||
_playlist = p;
|
||||
@ -337,6 +338,12 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||
final history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
||||
backgroundAudioPosition ~/ 1000, seekSliderValue);
|
||||
await _dbHelper.saveHistory(history);
|
||||
if (_playlist.name != 'Queue') {
|
||||
AudioService.customAction('setIsQueue', true);
|
||||
AudioService.customAction('changeQueue', [
|
||||
for (var e in _queue.episodes) jsonEncode(e.toMediaItem().toJson())
|
||||
]);
|
||||
}
|
||||
await AudioService.addQueueItemAt(episodeNew.toMediaItem(), 0);
|
||||
if (startPosition > 0) {
|
||||
await AudioService.seekTo(Duration(milliseconds: startPosition));
|
||||
@ -361,6 +368,13 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadEpisodeFromPlaylist(EpisodeBrief episode) async {
|
||||
if (_playlist.episodes.contains(episode)) {
|
||||
var index = _playlist.episodes.indexOf(episode);
|
||||
await AudioService.customAction('changeIndex', index);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _startAudioService(playlist,
|
||||
{int index = 0, int position = 0}) async {
|
||||
_stopOnComplete = false;
|
||||
@ -516,7 +530,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||
_lastPostion ~/ 1000, _seekSliderValue);
|
||||
await _dbHelper.saveHistory(history);
|
||||
}
|
||||
_episode = null;
|
||||
//_episode = null;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1019,7 +1033,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
_skipState = AudioProcessingState.skippingToNext;
|
||||
_playing = false;
|
||||
await _audioPlayer.stop();
|
||||
AudioServiceBackground.sendCustomEvent(_queue.first.title);
|
||||
AudioServiceBackground.sendCustomEvent(mediaItem.title);
|
||||
if (_isQueue) {
|
||||
if (_queue.length > 0) {
|
||||
_queue.removeAt(0);
|
||||
@ -1225,12 +1239,17 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
break;
|
||||
case 'changeQueue':
|
||||
await _changeQueue(argument);
|
||||
break;
|
||||
case 'changeIndex':
|
||||
await _changeIndex(argument);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future _changeQueue(List<dynamic> items) async {
|
||||
var queue = [for (var i in items) MediaItem.fromJson(json.decode(i))];
|
||||
await _audioPlayer.stop();
|
||||
AudioServiceBackground.sendCustomEvent(mediaItem.title);
|
||||
_queue.clear();
|
||||
_queue.addAll(queue);
|
||||
_index = 0;
|
||||
@ -1242,6 +1261,17 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||
_playFromStart();
|
||||
}
|
||||
|
||||
Future _changeIndex(int index) async {
|
||||
await _audioPlayer.stop();
|
||||
AudioServiceBackground.sendCustomEvent(mediaItem.title);
|
||||
_index = index;
|
||||
await AudioServiceBackground.setMediaItem(mediaItem);
|
||||
await _audioPlayer.setUrl(mediaItem.id, cacheMax: _cacheMax);
|
||||
var duration = await _audioPlayer.durationFuture ?? Duration.zero;
|
||||
AudioServiceBackground.setMediaItem(mediaItem.copyWith(duration: duration));
|
||||
_playFromStart();
|
||||
}
|
||||
|
||||
Future _setSkipSilence(bool boo) async {
|
||||
await _audioPlayer.setSkipSilence(boo);
|
||||
var duration = await _audioPlayer.durationFuture ?? Duration.zero;
|
||||
|
@ -61,7 +61,6 @@ class Playlist extends Equatable {
|
||||
episodes.clear();
|
||||
if (episodeList.isNotEmpty) {
|
||||
for (var url in episodeList) {
|
||||
print(url);
|
||||
var episode = await _dbHelper.getRssItemWithUrl(url);
|
||||
if (episode != null) episodes.add(episode);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user