Move subscribe_worker to group state namagement.
Improve OMPL file export/import, support groups now.
This commit is contained in:
parent
21fc7e027b
commit
98fd594eb5
|
@ -202,6 +202,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"removeConfirm" : MessageLookupByLibrary.simpleMessage("Remove confirm"),
|
||||
"removePodcastDes" : MessageLookupByLibrary.simpleMessage("Are you sure you want to unsubscribe?"),
|
||||
"removedAt" : m21,
|
||||
"save" : MessageLookupByLibrary.simpleMessage("Save"),
|
||||
"schedule" : MessageLookupByLibrary.simpleMessage("Schedule"),
|
||||
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("Invalid RSS link"),
|
||||
"searchPodcast" : MessageLookupByLibrary.simpleMessage("Search podcast"),
|
||||
|
@ -217,6 +218,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"settingsAutoDelete" : MessageLookupByLibrary.simpleMessage("Auto delete downloads after"),
|
||||
"settingsAutoDeleteDes" : MessageLookupByLibrary.simpleMessage("Default 30 days"),
|
||||
"settingsAutoPlayDes" : MessageLookupByLibrary.simpleMessage("Auto play next episode in playlist"),
|
||||
"settingsBackup" : MessageLookupByLibrary.simpleMessage("Backup"),
|
||||
"settingsBackupDes" : MessageLookupByLibrary.simpleMessage("Backup app data"),
|
||||
"settingsDefaultGrid" : MessageLookupByLibrary.simpleMessage("Default grid view"),
|
||||
"settingsDefaultGridDownload" : MessageLookupByLibrary.simpleMessage("Download tab"),
|
||||
"settingsDefaultGridFavorite" : MessageLookupByLibrary.simpleMessage("Favorite tab"),
|
||||
|
@ -265,6 +268,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"settingsTheme" : MessageLookupByLibrary.simpleMessage("Theme"),
|
||||
"settingsUpdateInterval" : MessageLookupByLibrary.simpleMessage("Update interval"),
|
||||
"settingsUpdateIntervalDes" : MessageLookupByLibrary.simpleMessage("Default 24 hours"),
|
||||
"share" : MessageLookupByLibrary.simpleMessage("Share"),
|
||||
"size" : MessageLookupByLibrary.simpleMessage("Size"),
|
||||
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Skip seconds at start"),
|
||||
"sleepTimer" : MessageLookupByLibrary.simpleMessage("Sleep timer"),
|
||||
|
|
|
@ -202,6 +202,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"removeConfirm" : MessageLookupByLibrary.simpleMessage("取消订阅"),
|
||||
"removePodcastDes" : MessageLookupByLibrary.simpleMessage("您确认要取消订阅吗?"),
|
||||
"removedAt" : m21,
|
||||
"save" : MessageLookupByLibrary.simpleMessage("保存"),
|
||||
"schedule" : MessageLookupByLibrary.simpleMessage("定时"),
|
||||
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("RSS 链接错误"),
|
||||
"searchPodcast" : MessageLookupByLibrary.simpleMessage("搜索播客"),
|
||||
|
@ -217,6 +218,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"settingsAutoDelete" : MessageLookupByLibrary.simpleMessage("自动删除下载节目"),
|
||||
"settingsAutoDeleteDes" : MessageLookupByLibrary.simpleMessage("默认 30 天"),
|
||||
"settingsAutoPlayDes" : MessageLookupByLibrary.simpleMessage("自动播放下一节目"),
|
||||
"settingsBackup" : MessageLookupByLibrary.simpleMessage("备份"),
|
||||
"settingsBackupDes" : MessageLookupByLibrary.simpleMessage("备份应用数据"),
|
||||
"settingsDefaultGrid" : MessageLookupByLibrary.simpleMessage("默认布局"),
|
||||
"settingsDefaultGridDownload" : MessageLookupByLibrary.simpleMessage("下载页"),
|
||||
"settingsDefaultGridFavorite" : MessageLookupByLibrary.simpleMessage("收藏页"),
|
||||
|
@ -265,6 +268,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||
"settingsTheme" : MessageLookupByLibrary.simpleMessage("主题"),
|
||||
"settingsUpdateInterval" : MessageLookupByLibrary.simpleMessage("更新频率"),
|
||||
"settingsUpdateIntervalDes" : MessageLookupByLibrary.simpleMessage("默认 24 小时"),
|
||||
"share" : MessageLookupByLibrary.simpleMessage("分享"),
|
||||
"size" : MessageLookupByLibrary.simpleMessage("大小"),
|
||||
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("开头跳过秒数"),
|
||||
"sleepTimer" : MessageLookupByLibrary.simpleMessage("睡眠模式"),
|
||||
|
|
|
@ -1316,6 +1316,16 @@ class S {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Save`
|
||||
String get save {
|
||||
return Intl.message(
|
||||
'Save',
|
||||
name: 'save',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Schedule`
|
||||
String get schedule {
|
||||
return Intl.message(
|
||||
|
@ -1459,6 +1469,26 @@ class S {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Backup`
|
||||
String get settingsBackup {
|
||||
return Intl.message(
|
||||
'Backup',
|
||||
name: 'settingsBackup',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Backup app data`
|
||||
String get settingsBackupDes {
|
||||
return Intl.message(
|
||||
'Backup app data',
|
||||
name: 'settingsBackupDes',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Default grid view`
|
||||
String get settingsDefaultGrid {
|
||||
return Intl.message(
|
||||
|
@ -1949,6 +1979,16 @@ class S {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Share`
|
||||
String get share {
|
||||
return Intl.message(
|
||||
'Share',
|
||||
name: 'share',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Size`
|
||||
String get size {
|
||||
return Intl.message(
|
||||
|
|
|
@ -10,9 +10,10 @@ import 'package:flutter/services.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:tsacdop/state/podcast_group.dart';
|
||||
|
||||
import '../type/searchpodcast.dart';
|
||||
import '../state/subscribe_podcast.dart';
|
||||
import '../state/podcast_group.dart';
|
||||
import '../util/context_extension.dart';
|
||||
import '../webfeed/webfeed.dart';
|
||||
import '../.env.dart';
|
||||
|
@ -346,7 +347,7 @@ class _SearchResultState extends State<SearchResult>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var subscribeWorker = Provider.of<SubscribeWorker>(context, listen: false);
|
||||
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
|
||||
final s = context.s;
|
||||
savePodcast(OnlinePodcast podcast) {
|
||||
SubscribeItem item =
|
||||
|
|
|
@ -20,7 +20,6 @@ import '../util/context_extension.dart';
|
|||
import '../util/custompaint.dart';
|
||||
import '../state/download_state.dart';
|
||||
import '../state/podcast_group.dart';
|
||||
import '../state/subscribe_podcast.dart';
|
||||
import 'playlist.dart';
|
||||
import 'importompl.dart';
|
||||
import 'audioplayer.dart';
|
||||
|
@ -726,7 +725,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
|
|||
super.build(context);
|
||||
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
|
||||
final s = context.s;
|
||||
return Selector<SubscribeWorker, bool>(
|
||||
return Selector<GroupList, bool>(
|
||||
selector: (_, worker) => worker.created,
|
||||
builder: (context, created, child) {
|
||||
return FutureBuilder<List<EpisodeBrief>>(
|
||||
|
|
|
@ -13,7 +13,6 @@ import 'package:line_icons/line_icons.dart';
|
|||
|
||||
import '../type/episodebrief.dart';
|
||||
import '../state/podcast_group.dart';
|
||||
import '../state/subscribe_podcast.dart';
|
||||
import '../state/download_state.dart';
|
||||
import '../type/podcastlocal.dart';
|
||||
import '../state/audio_state.dart';
|
||||
|
@ -397,7 +396,7 @@ class PodcastPreview extends StatelessWidget {
|
|||
return Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Selector<SubscribeWorker, bool>(
|
||||
child: Selector<GroupList, bool>(
|
||||
selector: (_, worker) => worker.created,
|
||||
builder: (context, created, child) {
|
||||
return FutureBuilder<List<EpisodeBrief>>(
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'package:provider/provider.dart';
|
|||
import '../local_storage/key_value_storage.dart';
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../state/podcast_group.dart';
|
||||
import '../state/subscribe_podcast.dart';
|
||||
import '../state/download_state.dart';
|
||||
import '../state/refresh_podcast.dart';
|
||||
import '../type/episodebrief.dart';
|
||||
|
@ -60,7 +59,7 @@ class Import extends StatelessWidget {
|
|||
GroupList groupList = Provider.of<GroupList>(context, listen: false);
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Consumer<SubscribeWorker>(
|
||||
Consumer<GroupList>(
|
||||
builder: (_, subscribeWorker, __) {
|
||||
SubscribeItem item = subscribeWorker.currentSubscribeItem;
|
||||
switch (item.subscribeState) {
|
||||
|
@ -70,11 +69,8 @@ class Import extends StatelessWidget {
|
|||
case SubscribeState.subscribe:
|
||||
return importColumn(s.notificaitonFatch(item.title), context);
|
||||
case SubscribeState.fetch:
|
||||
groupList.subscribeNewPodcast(item.id);
|
||||
// groupList.updatePodcast(item.id);
|
||||
return importColumn(s.notificationSuccess(item.title), context);
|
||||
case SubscribeState.exist:
|
||||
//groupList.subscribeNewPodcast(item.id);
|
||||
return importColumn(
|
||||
s.notificationSubscribeExisted(item.title), context);
|
||||
case SubscribeState.error:
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:provider/provider.dart';
|
|||
|
||||
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
||||
import 'package:tsacdop/service/ompl_build.dart';
|
||||
import 'package:tsacdop/webfeed/webfeed.dart';
|
||||
import 'package:tsacdop/state/podcast_group.dart';
|
||||
import 'package:xml/xml.dart' as xml;
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -16,24 +16,9 @@ import 'package:intl/intl.dart';
|
|||
|
||||
import '../settings/settting.dart';
|
||||
import '../state/refresh_podcast.dart';
|
||||
import '../state/subscribe_podcast.dart';
|
||||
import '../util/context_extension.dart';
|
||||
import 'about.dart';
|
||||
|
||||
class OmplOutline {
|
||||
final String text;
|
||||
final String xmlUrl;
|
||||
OmplOutline({this.text, this.xmlUrl});
|
||||
|
||||
factory OmplOutline.parse(xml.XmlElement element) {
|
||||
if (element == null) return null;
|
||||
return OmplOutline(
|
||||
text: element.getAttribute("text")?.trim(),
|
||||
xmlUrl: element.getAttribute("xmlUrl")?.trim(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PopupMenu extends StatefulWidget {
|
||||
@override
|
||||
_PopupMenuState createState() => _PopupMenuState();
|
||||
|
@ -69,70 +54,30 @@ class _PopupMenuState extends State<PopupMenu> {
|
|||
}
|
||||
|
||||
void _saveOmpl(String path) async {
|
||||
var subscribeWorker = Provider.of<SubscribeWorker>(context, listen: false);
|
||||
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
|
||||
final s = context.s;
|
||||
File file = File(path);
|
||||
try {
|
||||
Map data = PodcastsBackup.parseOMPL(file);
|
||||
data.forEach((title, list) async {
|
||||
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOMPL(file);
|
||||
for (var entry in data.entries) {
|
||||
String title = entry.key;
|
||||
var list = entry.value;
|
||||
for (var rss in list) {
|
||||
if (rss.xmlUrl != null) {
|
||||
SubscribeItem item = SubscribeItem(rss.xmlUrl, rss.text);
|
||||
SubscribeItem item =
|
||||
SubscribeItem(rss.xmlUrl, rss.text, group: title);
|
||||
await subscribeWorker.setSubscribeItem(item);
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
print(rss.text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
Fluttertoast.showToast(
|
||||
msg: s.toastFileError,
|
||||
gravity: ToastGravity.TOP,
|
||||
);
|
||||
// try {
|
||||
// String opml = file.readAsStringSync();
|
||||
// var content = xml.XmlDocument.parse(opml);
|
||||
// String title = content
|
||||
// .findAllElements('head')
|
||||
// .first
|
||||
// .findElements('title')
|
||||
// .first
|
||||
// .text;
|
||||
// print(title);
|
||||
// if (title != 'Tsacdop Subscriptions') {
|
||||
// var total = content
|
||||
// .findAllElements('outline')
|
||||
// .map((ele) => OmplOutline.parse(ele))
|
||||
// .toList();
|
||||
// if (total.length == 0) {
|
||||
// Fluttertoast.showToast(
|
||||
// msg: s.toastFileNotValid,
|
||||
// gravity: ToastGravity.BOTTOM,
|
||||
// );
|
||||
// } else {
|
||||
// for (int i = 0; i < total.length; i++) {
|
||||
// if (total[i].xmlUrl != null) {
|
||||
// // importOmpl.rssTitle = total[i].text;
|
||||
// //await saveOmpl(total[i].xmlUrl);
|
||||
// SubscribeItem item =
|
||||
// SubscribeItem(total[i].xmlUrl, total[i].text);
|
||||
// await subscribeWorker.setSubscribeItem(item);
|
||||
// await Future.delayed(Duration(milliseconds: 500));
|
||||
// print(total[i].text);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// print('Import fisnished');
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print(e);
|
||||
// Fluttertoast.showToast(
|
||||
// msg: s.toastFileError,
|
||||
// gravity: ToastGravity.TOP,
|
||||
// );
|
||||
//await Future.delayed(Duration(seconds: 5));
|
||||
// importOmpl.importState = ImportState.stop;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -307,6 +307,8 @@
|
|||
},
|
||||
"removePodcastDes": "Are you sure you want to unsubscribe?",
|
||||
"@removePodcastDes": {},
|
||||
"save": "Save",
|
||||
"@save": {},
|
||||
"schedule": "Schedule",
|
||||
"@schedule": {},
|
||||
"searchInvalidRss": "Invalid RSS link",
|
||||
|
@ -335,6 +337,10 @@
|
|||
"@settingsAutoDeleteDes": {},
|
||||
"settingsAutoPlayDes": "Auto play next episode in playlist",
|
||||
"@settingsAutoPlayDes": {},
|
||||
"settingsBackup": "Backup",
|
||||
"@settingsBackup": {},
|
||||
"settingsBackupDes": "Backup app data",
|
||||
"@settingsBackupDes": {},
|
||||
"settingsDefaultGrid": "Default grid view",
|
||||
"@settingsDefaultGrid": {},
|
||||
"settingsDefaultGridDownload": "Download tab",
|
||||
|
@ -433,6 +439,8 @@
|
|||
"@settingsUpdateInterval": {},
|
||||
"settingsUpdateIntervalDes": "Default 24 hours",
|
||||
"@settingsUpdateIntervalDes": {},
|
||||
"share": "Share",
|
||||
"@share": {},
|
||||
"size": "Size",
|
||||
"@size": {},
|
||||
"skipSecondsAtStart": "Skip seconds at start",
|
||||
|
|
|
@ -307,6 +307,8 @@
|
|||
},
|
||||
"removePodcastDes": "您确认要取消订阅吗?",
|
||||
"@removePodcastDes": {},
|
||||
"save": "保存",
|
||||
"@save": {},
|
||||
"schedule": "定时",
|
||||
"@schedule": {},
|
||||
"searchInvalidRss": "RSS 链接错误",
|
||||
|
@ -335,6 +337,10 @@
|
|||
"@settingsAutoDeleteDes": {},
|
||||
"settingsAutoPlayDes": "自动播放下一节目",
|
||||
"@settingsAutoPlayDes": {},
|
||||
"settingsBackup": "备份",
|
||||
"@settingsBackup": {},
|
||||
"settingsBackupDes": "备份应用数据",
|
||||
"@settingsBackupDes": {},
|
||||
"settingsDefaultGrid": "默认布局",
|
||||
"@settingsDefaultGrid": {},
|
||||
"settingsDefaultGridDownload": "下载页",
|
||||
|
@ -433,6 +439,8 @@
|
|||
"@settingsUpdateInterval": {},
|
||||
"settingsUpdateIntervalDes": "默认 24 小时",
|
||||
"@settingsUpdateIntervalDes": {},
|
||||
"share": "分享",
|
||||
"@share": {},
|
||||
"size": "大小",
|
||||
"@size": {},
|
||||
"skipSecondsAtStart": "开头跳过秒数",
|
||||
|
|
|
@ -12,7 +12,6 @@ import 'state/audio_state.dart';
|
|||
import 'state/setting_state.dart';
|
||||
import 'state/download_state.dart';
|
||||
import 'state/refresh_podcast.dart';
|
||||
import 'state/subscribe_podcast.dart';
|
||||
import 'home/home.dart';
|
||||
import 'intro_slider/app_intro.dart';
|
||||
|
||||
|
@ -30,7 +29,6 @@ Future main() async {
|
|||
),
|
||||
ChangeNotifierProvider(create: (_) => AudioPlayerNotifier()),
|
||||
ChangeNotifierProvider(create: (_) => GroupList()),
|
||||
ChangeNotifierProvider(create: (_) => SubscribeWorker()),
|
||||
ChangeNotifierProvider(create: (_) => RefreshWorker()),
|
||||
ChangeNotifierProvider(
|
||||
lazy: false,
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:fluttertoast/fluttertoast.dart';
|
|||
import 'package:feature_discovery/feature_discovery.dart';
|
||||
|
||||
import '../state/podcast_group.dart';
|
||||
import 'podcastgroup.dart';
|
||||
import 'podcast_group.dart';
|
||||
import 'podcastlist.dart';
|
||||
import '../util/pageroute.dart';
|
||||
import '../util/context_extension.dart';
|
||||
|
@ -586,7 +586,7 @@ class _AddGroupState extends State<AddGroup> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
var groupList = Provider.of<GroupList>(context);
|
||||
var groupList = Provider.of<GroupList>(context, listen: false);
|
||||
List list = groupList.groups.map((e) => e.name).toList();
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
|
@ -600,8 +600,9 @@ class _AddGroupState extends State<AddGroup> {
|
|||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding:
|
||||
const EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
|
||||
actionsPadding: EdgeInsets.all(0),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:xml/xml.dart' as xml;
|
||||
import '../state/podcast_group.dart';
|
||||
|
||||
|
@ -29,7 +28,7 @@ class PodcastsBackup {
|
|||
builder.element('ompl', nest: () {
|
||||
builder.attribute('version', '1.0');
|
||||
builder.element('head', nest: () {
|
||||
builder.element('title', nest: 'Tsacdop Subscriptions');
|
||||
builder.element('title', nest: 'Tsacdop Feed Groups');
|
||||
});
|
||||
builder.element('body', nest: () {
|
||||
for (var group in groups) {
|
||||
|
@ -55,20 +54,20 @@ class PodcastsBackup {
|
|||
}
|
||||
|
||||
static parseOMPL(File file) {
|
||||
var data = Map();
|
||||
Map<String, List<OmplOutline>> data = Map();
|
||||
String opml = file.readAsStringSync();
|
||||
var content = xml.XmlDocument.parse(opml);
|
||||
String title =
|
||||
content.findAllElements('head').first.findElements('title').first.text;
|
||||
print(title);
|
||||
var groups = content.findAllElements('body').first.findElements('outline');
|
||||
if (title != 'Tsacdop Subscriptions' &&
|
||||
groups.first.getAttribute('title') != 'Home') {
|
||||
var total = content
|
||||
if (title != 'Tsacdop Feed Groups') {
|
||||
List<OmplOutline> total = content
|
||||
.findAllElements('outline')
|
||||
.map((ele) => OmplOutline.parse(ele))
|
||||
.toList()
|
||||
..removeWhere((element) => element == null);
|
||||
.toList();
|
||||
data['Home'] = total;
|
||||
print(data.toString());
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -77,11 +76,10 @@ class PodcastsBackup {
|
|||
var total = element
|
||||
.findElements('outline')
|
||||
.map((ele) => OmplOutline.parse(ele))
|
||||
.toList()
|
||||
..removeWhere((element) => element == null);
|
||||
|
||||
.toList();
|
||||
data[title] = total;
|
||||
}
|
||||
print(data.toString());
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
|
@ -0,0 +1,132 @@
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wc_flutter_share/wc_flutter_share.dart';
|
||||
|
||||
import '../state/podcast_group.dart';
|
||||
import '../util/context_extension.dart';
|
||||
import '../service/ompl_build.dart';
|
||||
|
||||
class DataBackup extends StatelessWidget {
|
||||
Future<File> _exportOmpl(BuildContext context) async {
|
||||
var groups = context.read<GroupList>().groups;
|
||||
var ompl = PodcastsBackup(groups).omplBuilder();
|
||||
var tempdir = await getTemporaryDirectory();
|
||||
DateTime now = DateTime.now();
|
||||
String datePlus = now.year.toString() +
|
||||
now.month.toString() +
|
||||
now.day.toString() +
|
||||
now.second.toString();
|
||||
var file = File(join(tempdir.path, 'tsacdop_ompl_$datePlus.xml'));
|
||||
await file.writeAsString(ompl.toString());
|
||||
return file;
|
||||
}
|
||||
|
||||
Future<void> _saveOmpl(File file) async {
|
||||
final params = SaveFileDialogParams(sourceFilePath: file.path);
|
||||
await FlutterFileDialog.saveFile(params: params);
|
||||
}
|
||||
|
||||
Future<void> _shareOmpl(File file) async {
|
||||
final Uint8List bytes = await file.readAsBytes();
|
||||
await WcFlutterShare.share(
|
||||
sharePopupTitle: 'share Clip',
|
||||
fileName: file.path.split('/').last,
|
||||
mimeType: 'text/plain',
|
||||
bytesOfFile: bytes.buffer.asUint8List());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = context.s;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).accentColorBrightness,
|
||||
),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
title: Text(s.settingsBackup),
|
||||
backgroundColor: context.primaryColor,
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(s.subscribe,
|
||||
style: context.textTheme.bodyText1
|
||||
.copyWith(color: Theme.of(context).accentColor)),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 70.0, right: 20, top: 10, bottom: 10),
|
||||
child: Text(s.settingsExportDes),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 70.0, right: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
OutlineButton(
|
||||
highlightedBorderColor: context.accentColor,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.save,
|
||||
color: Colors.green[700],
|
||||
size: context.textTheme.headline6.fontSize,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.save,
|
||||
style: TextStyle(color: Colors.green[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
File file = await _exportOmpl(context);
|
||||
await _saveOmpl(file);
|
||||
}),
|
||||
SizedBox(width: 50),
|
||||
OutlineButton(
|
||||
highlightedBorderColor: Colors.blue[700],
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.share,
|
||||
size: context.textTheme.headline6.fontSize,
|
||||
color: Colors.blue[700],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(s.share,
|
||||
style: TextStyle(color: Colors.blue[700])),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
File file = await _exportOmpl(context);
|
||||
await _shareOmpl(file);
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,13 +6,14 @@ import 'package:intl/intl.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:tsacdop/state/podcast_group.dart';
|
||||
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../webfeed/webfeed.dart';
|
||||
import '../type/searchpodcast.dart';
|
||||
import '../util/context_extension.dart';
|
||||
import '../state/audio_state.dart';
|
||||
import '../state/subscribe_podcast.dart';
|
||||
import '../state/podcast_group.dart';
|
||||
import '../type/sub_history.dart';
|
||||
|
||||
class PlayedHistory extends StatefulWidget {
|
||||
|
@ -69,7 +70,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
|||
msg: context.s.toastPodcastRecovering,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
var subscribeWorker = context.read<SubscribeWorker>();
|
||||
var subscribeWorker = context.watch<GroupList>();
|
||||
try {
|
||||
BaseOptions options = new BaseOptions(
|
||||
connectTimeout: 10000,
|
||||
|
|
|
@ -1,23 +1,14 @@
|
|||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:tsacdop/state/podcast_group.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||
import 'package:feature_discovery/feature_discovery.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../service/ompl_build.dart';
|
||||
import '../util/context_extension.dart';
|
||||
import '../intro_slider/app_intro.dart';
|
||||
import '../type/podcastlocal.dart';
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../home/home.dart';
|
||||
import '../podcasts/podcast_manage.dart';
|
||||
import 'theme.dart';
|
||||
|
@ -28,6 +19,7 @@ import 'syncing.dart';
|
|||
import 'libries.dart';
|
||||
import 'languages.dart';
|
||||
import 'play_setting.dart';
|
||||
import 'data_backup.dart';
|
||||
|
||||
class Settings extends StatefulWidget {
|
||||
@override
|
||||
|
@ -44,18 +36,6 @@ class _SettingsState extends State<Settings>
|
|||
}
|
||||
}
|
||||
|
||||
_exportOmpl(BuildContext context) async {
|
||||
var groups = context.read<GroupList>().groups;
|
||||
var ompl = PodcastsBackup(groups).omplBuilder();
|
||||
var tempdir = await getTemporaryDirectory();
|
||||
var file = File(join(tempdir.path, 'tsacdop_ompl.xml'));
|
||||
await file.writeAsString(ompl.toString());
|
||||
final params = SaveFileDialogParams(sourceFilePath: file.path);
|
||||
final filePath = await FlutterFileDialog.saveFile(params: params);
|
||||
print(filePath);
|
||||
print(ompl.toString());
|
||||
}
|
||||
|
||||
bool _showFeedback;
|
||||
Animation _animation;
|
||||
AnimationController _controller;
|
||||
|
@ -239,14 +219,18 @@ class _SettingsState extends State<Settings>
|
|||
Divider(height: 2),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
_exportOmpl(context);
|
||||
//_exportOmpl(context);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DataBackup()));
|
||||
},
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 25.0),
|
||||
leading: Icon(LineIcons.file_code_solid,
|
||||
color: Colors.lightGreen[700]),
|
||||
title: Text(s.settingsExport),
|
||||
subtitle: Text(s.settingsExportDes),
|
||||
title: Text(s.settingsBackup),
|
||||
subtitle: Text(s.settingsBackupDes),
|
||||
),
|
||||
Divider(height: 2),
|
||||
],
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
import 'dart:core';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_isolate/flutter_isolate.dart';
|
||||
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:color_thief_flutter/color_thief_flutter.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../webfeed/webfeed.dart';
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../local_storage/key_value_storage.dart';
|
||||
import '../type/fireside_data.dart';
|
||||
import '../type/podcastlocal.dart';
|
||||
|
||||
class GroupEntity {
|
||||
|
@ -28,9 +40,15 @@ class GroupEntity {
|
|||
}
|
||||
|
||||
class PodcastGroup {
|
||||
/// Group name.
|
||||
final String name;
|
||||
|
||||
final String id;
|
||||
|
||||
/// Group theme color, not used.
|
||||
final String color;
|
||||
|
||||
/// Id lists of podcasts in group.
|
||||
List<String> podcastList;
|
||||
|
||||
PodcastGroup(this.name,
|
||||
|
@ -56,11 +74,11 @@ class PodcastGroup {
|
|||
|
||||
///Podcast in group.
|
||||
List<PodcastLocal> _podcasts;
|
||||
List<PodcastLocal> get podcasts => _podcasts;
|
||||
|
||||
///Ordered podcast list.
|
||||
List<PodcastLocal> _orderedPodcasts;
|
||||
List<PodcastLocal> get ordereddPodcasts => _orderedPodcasts;
|
||||
List<PodcastLocal> get podcasts => _podcasts;
|
||||
|
||||
set setOrderedPodcasts(List<PodcastLocal> list) {
|
||||
_orderedPodcasts = list;
|
||||
|
@ -80,20 +98,127 @@ class PodcastGroup {
|
|||
}
|
||||
}
|
||||
|
||||
enum SubscribeState { none, start, subscribe, fetch, stop, exist, error }
|
||||
|
||||
class SubscribeItem {
|
||||
///Rss url.
|
||||
String url;
|
||||
|
||||
///Rss title.
|
||||
String title;
|
||||
|
||||
/// Subscribe status.
|
||||
SubscribeState subscribeState;
|
||||
|
||||
/// Podcast id.
|
||||
String id;
|
||||
|
||||
///Avatar image link.
|
||||
String imgUrl;
|
||||
|
||||
///Podcast group, default Home.
|
||||
String group;
|
||||
SubscribeItem(this.url, this.title,
|
||||
{this.subscribeState = SubscribeState.none,
|
||||
this.id = '',
|
||||
this.imgUrl = '',
|
||||
this.group = ''});
|
||||
}
|
||||
|
||||
class GroupList extends ChangeNotifier {
|
||||
List<PodcastGroup> _groups;
|
||||
DBHelper dbHelper = DBHelper();
|
||||
/// List of all gourps.
|
||||
List<PodcastGroup> _groups = [];
|
||||
List<PodcastGroup> get groups => _groups;
|
||||
|
||||
KeyValueStorage storage = KeyValueStorage('groups');
|
||||
GroupList({List<PodcastGroup> groups}) : _groups = groups ?? [];
|
||||
DBHelper dbHelper = DBHelper();
|
||||
|
||||
/// Groups save in shared_prefrences.
|
||||
KeyValueStorage storage = KeyValueStorage('groups');
|
||||
|
||||
//GroupList({List<PodcastGroup> groups}) : _groups = groups ?? [];
|
||||
|
||||
/// Default false, true during loading groups from storage.
|
||||
bool _isLoading = false;
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
/// Svae ordered gourps info before saved.
|
||||
List<PodcastGroup> _orderChanged = [];
|
||||
List<PodcastGroup> get orderChanged => _orderChanged;
|
||||
|
||||
/// Subscribe worker isolate
|
||||
FlutterIsolate subIsolate;
|
||||
ReceivePort receivePort;
|
||||
SendPort subSendPort;
|
||||
|
||||
/// Current subsribe item from isolate.
|
||||
SubscribeItem _currentSubscribeItem = SubscribeItem('', '');
|
||||
SubscribeItem get currentSubscribeItem => _currentSubscribeItem;
|
||||
|
||||
/// Default false, true if subscribe isolate is created.
|
||||
bool _created = false;
|
||||
bool get created => _created;
|
||||
|
||||
/// Add subsribe item
|
||||
SubscribeItem _subscribeItem;
|
||||
setSubscribeItem(SubscribeItem item) async {
|
||||
_subscribeItem = item;
|
||||
await _start();
|
||||
}
|
||||
|
||||
_setCurrentSubscribeItem(SubscribeItem item) {
|
||||
_currentSubscribeItem = item;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future _start() async {
|
||||
if (_created == false) {
|
||||
await _createIsolate();
|
||||
_created = true;
|
||||
listen();
|
||||
} else
|
||||
subSendPort.send([
|
||||
_subscribeItem.url,
|
||||
_subscribeItem.title,
|
||||
_subscribeItem.imgUrl,
|
||||
_subscribeItem.group
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> _createIsolate() async {
|
||||
receivePort = ReceivePort();
|
||||
subIsolate =
|
||||
await FlutterIsolate.spawn(subIsolateEntryPoint, receivePort.sendPort);
|
||||
}
|
||||
|
||||
/// Isolate listener to get subscrribe status.
|
||||
void listen() {
|
||||
receivePort.distinct().listen((message) {
|
||||
if (message is SendPort) {
|
||||
subSendPort = message;
|
||||
subSendPort.send([
|
||||
_subscribeItem.url,
|
||||
_subscribeItem.title,
|
||||
_subscribeItem.imgUrl,
|
||||
_subscribeItem.group
|
||||
]);
|
||||
} else if (message is List) {
|
||||
_setCurrentSubscribeItem(SubscribeItem(
|
||||
message[1],
|
||||
message[0],
|
||||
subscribeState: SubscribeState.values[message[2]],
|
||||
));
|
||||
if (message.length == 5)
|
||||
_subscribeNewPodcast(id: message[3], groupName: message[4]);
|
||||
} else if (message is String && message == "done") {
|
||||
subIsolate.kill();
|
||||
subIsolate = null;
|
||||
_currentSubscribeItem = SubscribeItem('', '');
|
||||
_created = false;
|
||||
notifyListeners();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addToOrderChanged(PodcastGroup group) {
|
||||
_orderChanged.add(group);
|
||||
notifyListeners();
|
||||
|
@ -112,20 +237,19 @@ class GroupList extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
// _initGroup() async {
|
||||
// storage.getGroups().then((loadgroups) async {
|
||||
// _groups.addAll(loadgroups.map((e) => PodcastGroup.fromEntity(e)));
|
||||
// await Future.forEach(_groups, (group) async {
|
||||
// await group.getPodcasts();
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
@override
|
||||
void addListener(VoidCallback listener) {
|
||||
loadGroups().then((value) => super.addListener(listener));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
subIsolate?.kill();
|
||||
subIsolate = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Load groups from storage at start.
|
||||
Future loadGroups() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
@ -137,12 +261,13 @@ class GroupList extends ChangeNotifier {
|
|||
});
|
||||
}
|
||||
|
||||
//update podcasts of each group
|
||||
/// Update podcasts of each group
|
||||
Future updateGroups() async {
|
||||
for (var group in _groups) await group.getPodcasts();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Add new group.
|
||||
Future addGroup(PodcastGroup podcastGroup) async {
|
||||
_isLoading = true;
|
||||
_groups.add(podcastGroup);
|
||||
|
@ -151,6 +276,7 @@ class GroupList extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Remove group.
|
||||
Future delGroup(PodcastGroup podcastGroup) async {
|
||||
_isLoading = true;
|
||||
for (var podcast in podcastGroup.podcastList)
|
||||
|
@ -177,6 +303,7 @@ class GroupList extends ChangeNotifier {
|
|||
await storage.saveGroup(_groups.map((it) => it.toEntity()).toList());
|
||||
}
|
||||
|
||||
/// Subscribe podcast from search result.
|
||||
Future subscribe(PodcastLocal podcastLocal) async {
|
||||
_groups[0].podcastList.insert(0, podcastLocal.id);
|
||||
await _saveGroup();
|
||||
|
@ -195,13 +322,26 @@ class GroupList extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
Future subscribeNewPodcast(String id) async {
|
||||
if (!_groups[0].podcastList.contains(id)) {
|
||||
_groups[0].podcastList.insert(0, id);
|
||||
await _saveGroup();
|
||||
await _groups[0].getPodcasts();
|
||||
notifyListeners();
|
||||
/// Subscribe podcast from OMPL.
|
||||
Future _subscribeNewPodcast({String id, String groupName = 'Home'}) async {
|
||||
for (PodcastGroup group in _groups) {
|
||||
if (group.name == groupName) {
|
||||
if (!group.podcastList.contains(id)) {
|
||||
group.podcastList.insert(0, id);
|
||||
await _saveGroup();
|
||||
await group.getPodcasts();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
_isLoading = true;
|
||||
_groups.add(PodcastGroup(groupName));
|
||||
_groups.last.podcastList.insert(0, id);
|
||||
await _saveGroup();
|
||||
await _groups.last.getPodcasts();
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<PodcastGroup> getPodcastGroup(String id) {
|
||||
|
@ -225,18 +365,14 @@ class GroupList extends ChangeNotifier {
|
|||
group.podcastList.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
for (var s in list) s.podcastList.insert(0, id);
|
||||
|
||||
await _saveGroup();
|
||||
|
||||
for (var group in _groups) await group.getPodcasts();
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
//Unsubscribe podcast
|
||||
/// Unsubscribe podcast
|
||||
removePodcast(String id) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
@ -255,3 +391,169 @@ class GroupList extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||
List<SubscribeItem> items = [];
|
||||
bool _running = false;
|
||||
final List<String> listColor = [
|
||||
'388E3C',
|
||||
'1976D2',
|
||||
'D32F2F',
|
||||
'00796B',
|
||||
];
|
||||
ReceivePort subReceivePort = ReceivePort();
|
||||
sendPort.send(subReceivePort.sendPort);
|
||||
|
||||
Future<String> _getColor(File file) async {
|
||||
final imageProvider = FileImage(file);
|
||||
var colorImage = await getImageFromProvider(imageProvider);
|
||||
var color = await getColorFromImage(colorImage);
|
||||
String primaryColor = color.toString();
|
||||
return primaryColor;
|
||||
}
|
||||
|
||||
Future<void> _subscribe(SubscribeItem item) async {
|
||||
var dbHelper = DBHelper();
|
||||
String rss = item.url;
|
||||
sendPort.send([item.title, item.url, 1]);
|
||||
BaseOptions options = new BaseOptions(
|
||||
connectTimeout: 20000,
|
||||
receiveTimeout: 20000,
|
||||
);
|
||||
print(rss);
|
||||
|
||||
try {
|
||||
Response response = await Dio(options).get(rss);
|
||||
RssFeed p;
|
||||
try {
|
||||
p = RssFeed.parse(response.data);
|
||||
} on ArgumentError catch (e) {
|
||||
print(e);
|
||||
sendPort.send([item.title, item.url, 6]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.isNotEmpty) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
|
||||
String realUrl =
|
||||
response.redirects.isEmpty ? rss : response.realUri.toString();
|
||||
|
||||
bool checkUrl = await dbHelper.checkPodcast(realUrl);
|
||||
|
||||
if (checkUrl) {
|
||||
img.Image thumbnail;
|
||||
String imageUrl;
|
||||
try {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
p.itunes.image.href,
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = p.itunes.image.href;
|
||||
img.Image image = img.decodeImage(imageResponse.data);
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
} catch (e) {
|
||||
try {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
item.imgUrl,
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = item.imgUrl;
|
||||
img.Image image = img.decodeImage(imageResponse.data);
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
try {
|
||||
int index = math.Random().nextInt(3);
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
"https://ui-avatars.com/api/?size=300&background="
|
||||
"${listColor[index]}&color=fff&name=${item.title}&length=2&bold=true",
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = "https://ui-avatars.com/api/?size=300&background="
|
||||
"${listColor[index]}&color=fff&name=${item.title}&length=2&bold=true";
|
||||
thumbnail = img.decodeImage(imageResponse.data);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
sendPort.send([item.title, item.url, 6]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
}
|
||||
}
|
||||
String uuid = Uuid().v4();
|
||||
File("${dir.path}/$uuid.png")
|
||||
..writeAsBytesSync(img.encodePng(thumbnail));
|
||||
|
||||
String imagePath = "${dir.path}/$uuid.png";
|
||||
String primaryColor = await _getColor(File("${dir.path}/$uuid.png"));
|
||||
String author = p.itunes.author ?? p.author ?? '';
|
||||
String provider = p.generator ?? '';
|
||||
String link = p.link ?? '';
|
||||
PodcastLocal podcastLocal = PodcastLocal(p.title, imageUrl, realUrl,
|
||||
primaryColor, author, uuid, imagePath, provider, link,
|
||||
description: p.description);
|
||||
|
||||
await dbHelper.savePodcastLocal(podcastLocal);
|
||||
sendPort.send([item.title, item.url, 2, uuid, item.group]);
|
||||
if (provider.contains('fireside')) {
|
||||
FiresideData data = FiresideData(uuid, link);
|
||||
try {
|
||||
await data.fatchData();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
await dbHelper.savePodcastRss(p, uuid);
|
||||
|
||||
sendPort.send([item.title, item.url, 3, uuid]);
|
||||
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
} else {
|
||||
sendPort.send([item.title, item.url, 5]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
print(e);
|
||||
sendPort.send([item.title, item.url, 6]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
}
|
||||
|
||||
subReceivePort.distinct().listen((message) {
|
||||
if (message is List<String>) {
|
||||
items.add(SubscribeItem(message[0], message[1],
|
||||
imgUrl: message[2], group: message[3]));
|
||||
if (!_running) {
|
||||
_subscribe(items.first);
|
||||
_running = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,279 +0,0 @@
|
|||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:color_thief_flutter/color_thief_flutter.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_isolate/flutter_isolate.dart';
|
||||
|
||||
import '../webfeed/webfeed.dart';
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../local_storage/key_value_storage.dart';
|
||||
import '../type/fireside_data.dart';
|
||||
import '../type/podcastlocal.dart';
|
||||
import 'podcast_group.dart';
|
||||
|
||||
enum SubscribeState { none, start, subscribe, fetch, stop, exist, error }
|
||||
|
||||
class SubscribeItem {
|
||||
///Rss url.
|
||||
String url;
|
||||
|
||||
///Rss title.
|
||||
String title;
|
||||
|
||||
SubscribeState subscribeState;
|
||||
|
||||
///Uuid for podcast.
|
||||
String id;
|
||||
|
||||
///Avatat image link.
|
||||
String imgUrl;
|
||||
|
||||
///Podcast group, default Home.
|
||||
String group;
|
||||
SubscribeItem(this.url, this.title,
|
||||
{this.subscribeState = SubscribeState.none,
|
||||
this.id = '',
|
||||
this.imgUrl = '',
|
||||
this.group = 'Home'});
|
||||
}
|
||||
|
||||
class SubscribeWorker extends ChangeNotifier {
|
||||
FlutterIsolate subIsolate;
|
||||
ReceivePort receivePort;
|
||||
SendPort subSendPort;
|
||||
|
||||
SubscribeItem _subscribeItem;
|
||||
SubscribeItem _currentSubscribeItem = SubscribeItem('', '');
|
||||
bool _created = false;
|
||||
bool get created => _created;
|
||||
|
||||
setSubscribeItem(SubscribeItem item) async {
|
||||
_subscribeItem = item;
|
||||
await _start();
|
||||
}
|
||||
|
||||
_setCurrentSubscribeItem(SubscribeItem item) {
|
||||
_currentSubscribeItem = item;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SubscribeItem get currentSubscribeItem => _currentSubscribeItem;
|
||||
|
||||
Future<void> _createIsolate() async {
|
||||
receivePort = ReceivePort();
|
||||
subIsolate =
|
||||
await FlutterIsolate.spawn(subIsolateEntryPoint, receivePort.sendPort);
|
||||
}
|
||||
|
||||
void listen() {
|
||||
receivePort.distinct().listen((message) {
|
||||
if (message is SendPort) {
|
||||
subSendPort = message;
|
||||
subSendPort.send(
|
||||
[_subscribeItem.url, _subscribeItem.title, _subscribeItem.imgUrl]);
|
||||
} else if (message is List) {
|
||||
_setCurrentSubscribeItem(SubscribeItem(message[1], message[0],
|
||||
subscribeState: SubscribeState.values[message[2]],
|
||||
id: message.length == 4 ? message[3] : ''));
|
||||
} else if (message is String && message == "done") {
|
||||
subIsolate.kill();
|
||||
subIsolate = null;
|
||||
_currentSubscribeItem = SubscribeItem('', '');
|
||||
_created = false;
|
||||
notifyListeners();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future _start() async {
|
||||
if (_created == false) {
|
||||
await _createIsolate();
|
||||
_created = true;
|
||||
listen();
|
||||
} else
|
||||
subSendPort.send([
|
||||
_subscribeItem.url,
|
||||
_subscribeItem.title,
|
||||
_subscribeItem.imgUrl,
|
||||
_subscribeItem.group
|
||||
]);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
subIsolate?.kill();
|
||||
subIsolate = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||
List<SubscribeItem> items = [];
|
||||
bool _running = false;
|
||||
final List<String> listColor = [
|
||||
'388E3C',
|
||||
'1976D2'
|
||||
'D32F2F',
|
||||
'00796B',
|
||||
];
|
||||
ReceivePort subReceivePort = ReceivePort();
|
||||
sendPort.send(subReceivePort.sendPort);
|
||||
|
||||
Future<String> _getColor(File file) async {
|
||||
final imageProvider = FileImage(file);
|
||||
var colorImage = await getImageFromProvider(imageProvider);
|
||||
var color = await getColorFromImage(colorImage);
|
||||
String primaryColor = color.toString();
|
||||
return primaryColor;
|
||||
}
|
||||
|
||||
Future<void> _subscribe(SubscribeItem item) async {
|
||||
var dbHelper = DBHelper();
|
||||
String rss = item.url;
|
||||
sendPort.send([item.title, item.url, 1]);
|
||||
BaseOptions options = new BaseOptions(
|
||||
connectTimeout: 20000,
|
||||
receiveTimeout: 20000,
|
||||
);
|
||||
print(rss);
|
||||
|
||||
try {
|
||||
Response response = await Dio(options).get(rss);
|
||||
RssFeed p;
|
||||
try {
|
||||
p = RssFeed.parse(response.data);
|
||||
} on ArgumentError catch (e) {
|
||||
print(e);
|
||||
sendPort.send([item.title, item.url, 6]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.isNotEmpty) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
|
||||
String realUrl =
|
||||
response.redirects.isEmpty ? rss : response.realUri.toString();
|
||||
|
||||
bool checkUrl = await dbHelper.checkPodcast(realUrl);
|
||||
|
||||
if (checkUrl) {
|
||||
img.Image thumbnail;
|
||||
String imageUrl;
|
||||
try {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
p.itunes.image.href,
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = p.itunes.image.href;
|
||||
img.Image image = img.decodeImage(imageResponse.data);
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
} catch (e) {
|
||||
try {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
item.imgUrl,
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = item.imgUrl;
|
||||
img.Image image = img.decodeImage(imageResponse.data);
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
try {
|
||||
int index = math.Random().nextInt(3);
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
"https://ui-avatars.com/api/?size=300&background="
|
||||
"${listColor[index]}&color=fff&name=${item.title}&length=2&bold=true",
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = "https://ui-avatars.com/api/?size=300&background="
|
||||
"${listColor[index]}&color=fff&name=${item.title}&length=2&bold=true";
|
||||
thumbnail = img.decodeImage(imageResponse.data);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
sendPort.send([item.title, item.url, 6]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
}
|
||||
}
|
||||
String uuid = Uuid().v4();
|
||||
File("${dir.path}/$uuid.png")
|
||||
..writeAsBytesSync(img.encodePng(thumbnail));
|
||||
|
||||
String imagePath = "${dir.path}/$uuid.png";
|
||||
String primaryColor = await _getColor(File("${dir.path}/$uuid.png"));
|
||||
String author = p.itunes.author ?? p.author ?? '';
|
||||
String provider = p.generator ?? '';
|
||||
String link = p.link ?? '';
|
||||
PodcastLocal podcastLocal = PodcastLocal(p.title, imageUrl, realUrl,
|
||||
primaryColor, author, uuid, imagePath, provider, link,
|
||||
description: p.description);
|
||||
|
||||
// await groupList.subscribe(podcastLocal);
|
||||
await dbHelper.savePodcastLocal(podcastLocal);
|
||||
sendPort.send([item.title, item.url, 2, uuid]);
|
||||
if (provider.contains('fireside')) {
|
||||
FiresideData data = FiresideData(uuid, link);
|
||||
try {
|
||||
await data.fatchData();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
await dbHelper.savePodcastRss(p, uuid);
|
||||
|
||||
sendPort.send([item.title, item.url, 3, uuid]);
|
||||
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
} else {
|
||||
sendPort.send([item.title, item.url, 5]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
} on DioError catch (e) {
|
||||
print(e);
|
||||
sendPort.send([item.title, item.url, 6]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
sendPort.send([item.title, item.url, 4]);
|
||||
items.removeWhere((element) => element.url == item.url);
|
||||
if (items.length > 0) {
|
||||
await _subscribe(items.first);
|
||||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
}
|
||||
|
||||
subReceivePort.distinct().listen((message) {
|
||||
if (message is List<String>) {
|
||||
items.add(SubscribeItem(message[0], message[1], imgUrl: message[2]));
|
||||
if (!_running) {
|
||||
_subscribe(items.first);
|
||||
_running = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue