mirror of
https://github.com/stonega/tsacdop
synced 2025-02-17 20:10:37 +01:00
Play episode from search result.
This commit is contained in:
parent
5b2a95cd2d
commit
190f4b992d
@ -466,8 +466,11 @@ class _PlaylistWidgetState extends State<PlaylistWidget> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 30.0,
|
height: 30.0,
|
||||||
width: 30.0,
|
width: 30.0,
|
||||||
child: Image.file(File(
|
child: Image(
|
||||||
"${episodes[index].imagePath}"))),
|
image: episodes[index].avatarImage)
|
||||||
|
// Image.file(File(
|
||||||
|
// "${episodes[index].imagePath}"))
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -590,7 +593,7 @@ class SleepModeState extends State<SleepMode>
|
|||||||
Future _getDefaultTime() async {
|
Future _getDefaultTime() async {
|
||||||
var defaultSleepTimerStorage = KeyValueStorage(defaultSleepTimerKey);
|
var defaultSleepTimerStorage = KeyValueStorage(defaultSleepTimerKey);
|
||||||
var defaultTime = await defaultSleepTimerStorage.getInt(defaultValue: 30);
|
var defaultTime = await defaultSleepTimerStorage.getInt(defaultValue: 30);
|
||||||
if(mounted) setState(() => _minSelected = defaultTime);
|
if (mounted) setState(() => _minSelected = defaultTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -992,7 +995,7 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
|
|||||||
foregroundColor: MaterialStateProperty.all<Color>(
|
foregroundColor: MaterialStateProperty.all<Color>(
|
||||||
context.accentColor),
|
context.accentColor),
|
||||||
overlayColor: MaterialStateProperty.all<Color>(
|
overlayColor: MaterialStateProperty.all<Color>(
|
||||||
context.primaryColor),
|
context.primaryColor.withOpacity(0.3)),
|
||||||
),
|
),
|
||||||
onPressed: () => chapters.url.launchUrl,
|
onPressed: () => chapters.url.launchUrl,
|
||||||
child: Text('Visit')),
|
child: Text('Visit')),
|
||||||
@ -1093,7 +1096,7 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
context.s.settingsInfo,
|
context.s.homeToprightMenuAbout,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.accentColor,
|
color: context.accentColor,
|
||||||
|
@ -10,7 +10,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tsacdop/type/episodebrief.dart';
|
import 'package:tsacdop/state/audio_state.dart';
|
||||||
import 'package:webfeed/webfeed.dart';
|
import 'package:webfeed/webfeed.dart';
|
||||||
|
|
||||||
import '../.env.dart';
|
import '../.env.dart';
|
||||||
@ -898,7 +898,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
|||||||
var searchResult = await searchEngine.fetchEpisode(rssUrl: id);
|
var searchResult = await searchEngine.fetchEpisode(rssUrl: id);
|
||||||
var episodes = searchResult.items.cast();
|
var episodes = searchResult.items.cast();
|
||||||
for (var episode in episodes) {
|
for (var episode in episodes) {
|
||||||
_episodeList.add(episode.toOnlineWEpisode);
|
_episodeList.add(episode.toOnlineEpisode);
|
||||||
}
|
}
|
||||||
_loading = false;
|
_loading = false;
|
||||||
return _episodeList;
|
return _episodeList;
|
||||||
@ -1027,6 +1027,25 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
|||||||
: '${content[index].length.toTime} | '
|
: '${content[index].length.toTime} | '
|
||||||
'${content[index].pubDate.toDate(context)}',
|
'${content[index].pubDate.toDate(context)}',
|
||||||
style: TextStyle(color: context.accentColor)),
|
style: TextStyle(color: context.accentColor)),
|
||||||
|
trailing: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor: MaterialStateProperty.all<Color>(
|
||||||
|
context.accentColor),
|
||||||
|
overlayColor: MaterialStateProperty.all<Color>(
|
||||||
|
context.primaryColor.withOpacity(0.3)),
|
||||||
|
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
|
||||||
|
EdgeInsets.symmetric(horizontal: 2))),
|
||||||
|
child: Text(context.s.play),
|
||||||
|
onPressed: () {
|
||||||
|
context.read<AudioPlayerNotifier>().episodeLoad(
|
||||||
|
content[index].toEpisode,
|
||||||
|
fromSearch: true);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Wait a moment',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -1106,6 +1125,8 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
|||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
imageUrl: widget.onlinePodcast.image,
|
imageUrl: widget.onlinePodcast.image,
|
||||||
|
fadeInDuration: Duration.zero,
|
||||||
|
placeholderFadeInDuration: Duration.zero,
|
||||||
progressIndicatorBuilder:
|
progressIndicatorBuilder:
|
||||||
(context, url, downloadProgress) => Container(
|
(context, url, downloadProgress) => Container(
|
||||||
height: 120,
|
height: 120,
|
||||||
|
@ -175,6 +175,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
|
|
||||||
bool _markListened;
|
bool _markListened;
|
||||||
|
|
||||||
|
// Tmep episode list, playing from search result
|
||||||
|
List<EpisodeBrief> _playFromSearchList = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addListener(VoidCallback listener) {
|
void addListener(VoidCallback listener) {
|
||||||
super.addListener(listener);
|
super.addListener(listener);
|
||||||
@ -279,7 +282,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
await _playlist.getPlaylist();
|
await _playlist.getPlaylist();
|
||||||
if (state[1] != '') {
|
if (state[1] != '') {
|
||||||
var episode = await _dbHelper.getRssItemWithUrl(state[1]);
|
var episode = await _dbHelper.getRssItemWithUrl(state[1]);
|
||||||
if ((!_playlist.isQueue && _playlist.contains(episode)) ||
|
if ((!_playlist.isQueue && episode != null && _playlist.contains(episode)) ||
|
||||||
(_playlist.isQueue &&
|
(_playlist.isQueue &&
|
||||||
_queue.isNotEmpty &&
|
_queue.isNotEmpty &&
|
||||||
_queue.episodes.first.title == episode.title)) {
|
_queue.episodes.first.title == episode.title)) {
|
||||||
@ -358,15 +361,21 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> episodeLoad(EpisodeBrief episode,
|
Future<void> episodeLoad(EpisodeBrief episode,
|
||||||
{int startPosition = 0}) async {
|
{int startPosition = 0, bool fromSearch = false}) async {
|
||||||
final episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl);
|
var episodeNew;
|
||||||
|
if (fromSearch) {
|
||||||
|
episodeNew = episode;
|
||||||
|
_playFromSearchList.add(episode);
|
||||||
|
} else {
|
||||||
|
episodeNew = await _dbHelper.getRssItemWithUrl(episode.enclosureUrl);
|
||||||
|
}
|
||||||
//TODO load episode from last position when player running
|
//TODO load episode from last position when player running
|
||||||
if (playerRunning) {
|
if (playerRunning) {
|
||||||
final history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
final history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
||||||
backgroundAudioPosition ~/ 1000, seekSliderValue);
|
backgroundAudioPosition ~/ 1000, seekSliderValue);
|
||||||
await _dbHelper.saveHistory(history);
|
await _dbHelper.saveHistory(history);
|
||||||
_queue.addToPlayListAt(episodeNew, 0);
|
_queue.addToPlayListAt(episodeNew, 0);
|
||||||
await updatePlaylist(_queue);
|
await updatePlaylist(_queue, updateEpisodes: !fromSearch);
|
||||||
if (!_playlist.isQueue) {
|
if (!_playlist.isQueue) {
|
||||||
AudioService.customAction('setIsQueue', true);
|
AudioService.customAction('setIsQueue', true);
|
||||||
AudioService.customAction('changeQueue', [
|
AudioService.customAction('changeQueue', [
|
||||||
@ -486,7 +495,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||||||
.where((event) => event != null)
|
.where((event) => event != null)
|
||||||
.listen((item) async {
|
.listen((item) async {
|
||||||
var episode = await _dbHelper.getRssItemWithMediaId(item.id);
|
var episode = await _dbHelper.getRssItemWithMediaId(item.id);
|
||||||
|
if(episode == null){
|
||||||
|
episode = _playFromSearchList.firstWhere((e) => e.mediaId == item.id, orElse: () => null);
|
||||||
|
}
|
||||||
_backgroundAudioDuration = item.duration?.inMilliseconds ?? 0;
|
_backgroundAudioDuration = item.duration?.inMilliseconds ?? 0;
|
||||||
if (episode != null) {
|
if (episode != null) {
|
||||||
_episode = episode;
|
_episode = episode;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
@ -55,7 +56,7 @@ class EpisodeBrief extends Equatable {
|
|||||||
artist: feedTitle,
|
artist: feedTitle,
|
||||||
album: feedTitle,
|
album: feedTitle,
|
||||||
duration: Duration.zero,
|
duration: Duration.zero,
|
||||||
artUri: 'file://$imagePath',
|
artUri: imagePath == '' ? episodeImage : 'file://$imagePath',
|
||||||
extras: {
|
extras: {
|
||||||
'skipSecondsStart': skipSecondsStart,
|
'skipSecondsStart': skipSecondsStart,
|
||||||
'skipSecondsEnd': skipSecondsEnd
|
'skipSecondsEnd': skipSecondsEnd
|
||||||
@ -65,7 +66,9 @@ class EpisodeBrief extends Equatable {
|
|||||||
ImageProvider get avatarImage {
|
ImageProvider get avatarImage {
|
||||||
return File(imagePath).existsSync()
|
return File(imagePath).existsSync()
|
||||||
? FileImage(File(imagePath))
|
? FileImage(File(imagePath))
|
||||||
: const AssetImage('assets/avatar_backup.png');
|
: episodeImage != ''
|
||||||
|
? CachedNetworkImageProvider(episodeImage)
|
||||||
|
: AssetImage('assets/avatar_backup.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
Color backgroudColor(BuildContext context) {
|
Color backgroudColor(BuildContext context) {
|
||||||
|
@ -35,17 +35,25 @@ class IndexEpisode {
|
|||||||
final int datePublished;
|
final int datePublished;
|
||||||
final String enclosureUrl;
|
final String enclosureUrl;
|
||||||
final int enclosureLength;
|
final int enclosureLength;
|
||||||
|
final int duration;
|
||||||
|
final String feedImage;
|
||||||
IndexEpisode(
|
IndexEpisode(
|
||||||
{this.title,
|
{this.title,
|
||||||
this.description,
|
this.description,
|
||||||
this.datePublished,
|
this.datePublished,
|
||||||
this.enclosureLength,
|
this.enclosureLength,
|
||||||
this.enclosureUrl});
|
this.enclosureUrl,
|
||||||
|
this.duration,
|
||||||
|
this.feedImage});
|
||||||
|
|
||||||
factory IndexEpisode.fromJson(Map<String, dynamic> json) =>
|
factory IndexEpisode.fromJson(Map<String, dynamic> json) =>
|
||||||
_$IndexEpisodeFromJson(json);
|
_$IndexEpisodeFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$IndexEpisodeToJson(this);
|
Map<String, dynamic> toJson() => _$IndexEpisodeToJson(this);
|
||||||
|
|
||||||
OnlineEpisode get toOnlineWEpisode =>
|
OnlineEpisode get toOnlineEpisode => OnlineEpisode(
|
||||||
OnlineEpisode(title: title, pubDate: datePublished * 1000, length: 0);
|
title: title,
|
||||||
|
pubDate: datePublished * 1000,
|
||||||
|
length: duration ?? 0,
|
||||||
|
audio: enclosureUrl,
|
||||||
|
thumbnail: feedImage);
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ IndexEpisode _$IndexEpisodeFromJson(Map<String, dynamic> json) {
|
|||||||
datePublished: json['datePublished'] as int,
|
datePublished: json['datePublished'] as int,
|
||||||
enclosureLength: json['enclosureLength'] as int,
|
enclosureLength: json['enclosureLength'] as int,
|
||||||
enclosureUrl: json['enclosureUrl'] as String,
|
enclosureUrl: json['enclosureUrl'] as String,
|
||||||
|
duration: json['duration'] as int,
|
||||||
|
feedImage: json['feedImage'] as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,4 +42,6 @@ Map<String, dynamic> _$IndexEpisodeToJson(IndexEpisode instance) =>
|
|||||||
'datePublished': instance.datePublished,
|
'datePublished': instance.datePublished,
|
||||||
'enclosureUrl': instance.enclosureUrl,
|
'enclosureUrl': instance.enclosureUrl,
|
||||||
'enclosureLength': instance.enclosureLength,
|
'enclosureLength': instance.enclosureLength,
|
||||||
|
'duration': instance.duration,
|
||||||
|
'feedImage': instance.feedImage,
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:tsacdop/type/episodebrief.dart';
|
||||||
part 'searchepisodes.g.dart';
|
part 'searchepisodes.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@ -33,8 +36,31 @@ class OnlineEpisode {
|
|||||||
final int pubDate;
|
final int pubDate;
|
||||||
@JsonKey(name: 'audio_length_sec')
|
@JsonKey(name: 'audio_length_sec')
|
||||||
final int length;
|
final int length;
|
||||||
OnlineEpisode({this.title, this.pubDate, this.length});
|
final String audio;
|
||||||
|
final String thumbnail;
|
||||||
|
|
||||||
|
OnlineEpisode({this.title, this.pubDate, this.length, this.audio, this.thumbnail});
|
||||||
factory OnlineEpisode.fromJson(Map<String, dynamic> json) =>
|
factory OnlineEpisode.fromJson(Map<String, dynamic> json) =>
|
||||||
_$OnlineEpisodeFromJson(json);
|
_$OnlineEpisodeFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$OnlineEpisodeToJson(this);
|
Map<String, dynamic> toJson() => _$OnlineEpisodeToJson(this);
|
||||||
|
|
||||||
|
EpisodeBrief get toEpisode {
|
||||||
|
return EpisodeBrief(
|
||||||
|
title,
|
||||||
|
audio,
|
||||||
|
0,
|
||||||
|
pubDate,
|
||||||
|
title,
|
||||||
|
jsonEncode([120,220,128]),
|
||||||
|
length ?? 0,
|
||||||
|
0,
|
||||||
|
'',
|
||||||
|
0,
|
||||||
|
mediaId: audio,
|
||||||
|
skipSecondsEnd: 0,
|
||||||
|
skipSecondsStart: 0,
|
||||||
|
chapterLink: '',
|
||||||
|
episodeImage: thumbnail
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ OnlineEpisode _$OnlineEpisodeFromJson(Map<String, dynamic> json) {
|
|||||||
title: json['title'] as String,
|
title: json['title'] as String,
|
||||||
pubDate: json['pub_date_ms'] as int,
|
pubDate: json['pub_date_ms'] as int,
|
||||||
length: json['audio_length_sec'] as int,
|
length: json['audio_length_sec'] as int,
|
||||||
|
audio: json['audio'] as String,
|
||||||
|
thumbnail: json['thumbnail'] as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,4 +35,6 @@ Map<String, dynamic> _$OnlineEpisodeToJson(OnlineEpisode instance) =>
|
|||||||
'title': instance.title,
|
'title': instance.title,
|
||||||
'pub_date_ms': instance.pubDate,
|
'pub_date_ms': instance.pubDate,
|
||||||
'audio_length_sec': instance.length,
|
'audio_length_sec': instance.length,
|
||||||
|
'audio': instance.audio,
|
||||||
|
'thumbnail': instance.thumbnail,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user