mirror of
https://github.com/stonega/tsacdop
synced 2025-02-03 08:57:33 +01:00
🚧 Auto download
This commit is contained in:
parent
9c13450a9c
commit
1a497a78ed
@ -22,7 +22,7 @@ class DBHelper {
|
||||
var documentsDirectory = await getDatabasesPath();
|
||||
String path = join(documentsDirectory, "podcasts.db");
|
||||
Database theDb = await openDatabase(path,
|
||||
version: 2, onCreate: _onCreate, onUpgrade: _onUpgrade);
|
||||
version: 3, onCreate: _onCreate, onUpgrade: _onUpgrade);
|
||||
return theDb;
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ class DBHelper {
|
||||
imageUrl TEXT,rssUrl TEXT UNIQUE, primaryColor TEXT, author TEXT,
|
||||
description TEXT, add_date INTEGER, imagePath TEXT, provider TEXT, link TEXT,
|
||||
background_image TEXT DEFAULT '',hosts TEXT DEFAULT '',update_count INTEGER DEFAULT 0,
|
||||
episode_count INTEGER DEFAULT 0, skip_seconds INTEGER DEFAULT 0)""");
|
||||
episode_count INTEGER DEFAULT 0, skip_seconds INTEGER DEFAULT 0, auto_download INTEGER DEFAULT 0)""");
|
||||
await db
|
||||
.execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT,
|
||||
enclosure_url TEXT UNIQUE, enclosure_length INTEGER, pubDate TEXT,
|
||||
@ -51,7 +51,12 @@ class DBHelper {
|
||||
void _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
||||
if (oldVersion == 1) {
|
||||
await db.execute(
|
||||
"ALTER TABLE PodcastLocal ADD skip_seconds INTEGER DEFAULT 0");
|
||||
"ALTER TABLE PodcastLocal ADD skip_seconds INTEGER DEFAULT 0 ");
|
||||
await db.execute(
|
||||
"ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0");
|
||||
} else if (oldVersion == 2) {
|
||||
await db.execute(
|
||||
"ALTER TABLE PodcastLocal ADD auto_download INTEGER DEFAULT 0");
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,6 +136,20 @@ class DBHelper {
|
||||
"UPDATE PodcastLocal SET skip_seconds = ? WHERE id = ?", [seconds, id]);
|
||||
}
|
||||
|
||||
Future<bool> getAutoDownload(String id) async {
|
||||
var dbClient = await database;
|
||||
List<Map> list = await dbClient
|
||||
.rawQuery('SELECT auto_download FROM PodcastLocal WHERE id = ?', [id]);
|
||||
return list.first['auto_download'] == 1;
|
||||
}
|
||||
|
||||
Future<int> saveAutoDownload(String id, bool boo) async {
|
||||
var dbClient = await database;
|
||||
return await dbClient.rawUpdate(
|
||||
"UPDATE PodcastLocal SET auto_download = ? WHERE id = ?",
|
||||
[boo ? 1 : 0, id]);
|
||||
}
|
||||
|
||||
Future<bool> checkPodcast(String url) async {
|
||||
var dbClient = await database;
|
||||
List<Map> list = await dbClient
|
||||
@ -813,6 +832,37 @@ class DBHelper {
|
||||
return episodes;
|
||||
}
|
||||
|
||||
Future<List<EpisodeBrief>> gettNewRssItem(String id) async {
|
||||
var dbClient = await database;
|
||||
List<EpisodeBrief> episodes = [];
|
||||
List<Map> list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length,
|
||||
E.milliseconds, P.title as feed_title, E.duration, E.explicit, E.liked,
|
||||
E.downloaded, P.imagePath, P.primaryColor, E.media_id, E.is_new, P.skip_seconds
|
||||
FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||
WHERE is_new = 1 AND downloaded != 'ND' AND P.id = ?ORDER BY E.milliseconds DESC """,
|
||||
[id],
|
||||
);
|
||||
for (int x = 0; x < list.length; x++) {
|
||||
episodes.add(EpisodeBrief(
|
||||
list[x]['title'],
|
||||
list[x]['enclosure_url'],
|
||||
list[x]['enclosure_length'],
|
||||
list[x]['milliseconds'],
|
||||
list[x]['feed_title'],
|
||||
list[x]['primaryColor'],
|
||||
list[x]['liked'],
|
||||
list[x]['downloaded'],
|
||||
list[x]['duration'],
|
||||
list[x]['explicit'],
|
||||
list[x]['imagePath'],
|
||||
list[x]['media_id'],
|
||||
list[x]['is_new'],
|
||||
list[x]['skip_seconds']));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
||||
Future<int> removeAllNewMark() async {
|
||||
var dbClient = await database;
|
||||
return await dbClient.rawUpdate("UPDATE Episodes SET is_new = 0 ");
|
||||
@ -971,34 +1021,6 @@ class DBHelper {
|
||||
return count;
|
||||
}
|
||||
|
||||
Future<List<EpisodeBrief>> getDownloadedRssItem() async {
|
||||
var dbClient = await database;
|
||||
List<EpisodeBrief> episodes = List();
|
||||
List<Map> list = await dbClient.rawQuery(
|
||||
"""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath,
|
||||
P.title as feed_title, E.duration, E.explicit, E.liked, E.downloaded,
|
||||
P.primaryColor, E.media_id, E.is_new, P.skip_seconds FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id
|
||||
WHERE E.downloaded != 'ND' ORDER BY E.download_date DESC""");
|
||||
for (int x = 0; x < list.length; x++) {
|
||||
episodes.add(EpisodeBrief(
|
||||
list[x]['title'],
|
||||
list[x]['enclosure_url'],
|
||||
list[x]['enclosure_length'],
|
||||
list[x]['milliseconds'],
|
||||
list[x]['feed_title'],
|
||||
list[x]['primaryColor'],
|
||||
list[x]['liked'],
|
||||
list[x]['downloaded'],
|
||||
list[x]['duration'],
|
||||
list[x]['explicit'],
|
||||
list[x]['imagePath'],
|
||||
list[x]['media_id'],
|
||||
list[x]['is_new'],
|
||||
list[x]['skip_seconds']));
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
||||
Future<String> getDescription(String url) async {
|
||||
var dbClient = await database;
|
||||
List<Map> list = await dbClient.rawQuery(
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../state/podcast_group.dart';
|
||||
import '../type/podcastlocal.dart';
|
||||
@ -16,7 +17,6 @@ import '../util/colorize.dart';
|
||||
import '../util/duraiton_picker.dart';
|
||||
import '../util/context_extension.dart';
|
||||
import '../util/general_dialog.dart';
|
||||
import 'podcastmanage.dart';
|
||||
|
||||
class PodcastGroupList extends StatefulWidget {
|
||||
final PodcastGroup group;
|
||||
@ -97,6 +97,16 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
await dbHelper.saveSkipSeconds(id, seconds);
|
||||
}
|
||||
|
||||
_setAutoDownload(String id, bool boo) async {
|
||||
DBHelper dbHelper = DBHelper();
|
||||
await dbHelper.saveAutoDownload(id, boo);
|
||||
}
|
||||
|
||||
Future<bool> _getAutoDownload(String id) async {
|
||||
DBHelper dbHelper = DBHelper();
|
||||
return await dbHelper.getAutoDownload(id);
|
||||
}
|
||||
|
||||
String _stringForSeconds(double seconds) {
|
||||
if (seconds == null) return null;
|
||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||
@ -120,14 +130,25 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buttonOnMenu(Widget widget, VoidCallback onTap) => Material(
|
||||
Widget _buttonOnMenu({Widget icon, VoidCallback onTap, String tooltip}) =>
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0),
|
||||
child: widget),
|
||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: icon,
|
||||
),
|
||||
Text(tooltip, style: context.textTheme.subtitle2),
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
||||
@ -209,20 +230,6 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
child: Text(group.name));
|
||||
}).toList(),
|
||||
),
|
||||
FutureBuilder<int>(
|
||||
future: getSkipSecond(widget.podcastLocal.id),
|
||||
initialData: 0,
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data == 0
|
||||
? Center()
|
||||
: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text('Skip ' +
|
||||
_stringForSeconds(
|
||||
snapshot.data.toDouble())),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
Spacer(),
|
||||
@ -230,9 +237,6 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
angle: math.pi * _value,
|
||||
child: Icon(Icons.keyboard_arrow_down),
|
||||
),
|
||||
// Icon(_loadMenu
|
||||
// ? Icons.keyboard_arrow_up
|
||||
// : Icons.keyboard_arrow_down),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||
),
|
||||
@ -326,97 +330,127 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
_buttonOnMenu(
|
||||
Icon(Icons.fullscreen, size: 20 * _value),
|
||||
() => Navigator.push(
|
||||
context,
|
||||
ScaleRoute(
|
||||
page: PodcastDetail(
|
||||
podcastLocal: widget.podcastLocal,
|
||||
)),
|
||||
)),
|
||||
_buttonOnMenu(Icon(Icons.add, size: 20 * _value),
|
||||
() {
|
||||
setState(() {
|
||||
_addGroup = true;
|
||||
});
|
||||
}),
|
||||
icon: Icon(Icons.add,
|
||||
size: _value == 0 ? 1 : 20 * _value),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_addGroup = true;
|
||||
});
|
||||
},
|
||||
tooltip: 'Group'),
|
||||
FutureBuilder<bool>(
|
||||
future:
|
||||
_getAutoDownload(widget.podcastLocal.id),
|
||||
initialData: false,
|
||||
builder: (context, snapshot) {
|
||||
return _buttonOnMenu(
|
||||
icon: Icon(
|
||||
LineIcons.cloud_download_alt_solid,
|
||||
size: _value == 0 ? 1 : 20 * _value,
|
||||
color: snapshot.data
|
||||
? context.accentColor
|
||||
: null),
|
||||
tooltip: 'AutoDownload',
|
||||
onTap: () {
|
||||
_setAutoDownload(widget.podcastLocal.id,
|
||||
!snapshot.data);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
FutureBuilder<int>(
|
||||
future: getSkipSecond(widget.podcastLocal.id),
|
||||
initialData: 0,
|
||||
builder: (context, snapshot) {
|
||||
return _buttonOnMenu(
|
||||
icon: Icon(
|
||||
Icons.fast_forward,
|
||||
size: _value == 0 ? 1 : 20 * (_value),
|
||||
),
|
||||
tooltip: 'Skip' +
|
||||
(snapshot.data == 0
|
||||
? ''
|
||||
: _stringForSeconds(
|
||||
snapshot.data.toDouble())),
|
||||
onTap: () {
|
||||
generalDialog(
|
||||
context,
|
||||
title: Text('Skip seconds at start',
|
||||
maxLines: 2),
|
||||
content: DurationPicker(
|
||||
duration: Duration(
|
||||
seconds: _skipSeconds ?? 0),
|
||||
onChange: (value) =>
|
||||
_seconds = value.inSeconds,
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_seconds = 0;
|
||||
},
|
||||
child: Text(
|
||||
'CANCEL',
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
saveSkipSeconds(
|
||||
widget.podcastLocal.id,
|
||||
_seconds);
|
||||
},
|
||||
child: Text(
|
||||
'CONFIRM',
|
||||
style: TextStyle(
|
||||
color:
|
||||
context.accentColor),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}),
|
||||
_buttonOnMenu(
|
||||
Icon(
|
||||
Icons.fast_forward,
|
||||
size: 20 * (_value),
|
||||
), () {
|
||||
generalDialog(
|
||||
context,
|
||||
title: Text('Skip seconds at start',
|
||||
maxLines: 2),
|
||||
content: DurationPicker(
|
||||
duration:
|
||||
Duration(seconds: _skipSeconds ?? 0),
|
||||
onChange: (value) =>
|
||||
_seconds = value.inSeconds,
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_seconds = 0;
|
||||
},
|
||||
child: Text(
|
||||
'CANCEL',
|
||||
style:
|
||||
TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
saveSkipSeconds(
|
||||
widget.podcastLocal.id, _seconds);
|
||||
},
|
||||
child: Text(
|
||||
'CONFIRM',
|
||||
style: TextStyle(
|
||||
color: context.accentColor),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
_buttonOnMenu(
|
||||
Icon(
|
||||
icon: Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
size: 20 * (_value),
|
||||
), () {
|
||||
generalDialog(
|
||||
context,
|
||||
title: Text('Remove confirm'),
|
||||
content: Text(
|
||||
'Are you sure you want to unsubscribe?'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
'CANCEL',
|
||||
style:
|
||||
TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
groupList.removePodcast(
|
||||
widget.podcastLocal.id);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'CONFIRM',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
size: _value == 0 ? 1 : 20 * _value,
|
||||
),
|
||||
tooltip: 'Remove',
|
||||
onTap: () {
|
||||
generalDialog(
|
||||
context,
|
||||
title: Text('Remove confirm'),
|
||||
content: Text(
|
||||
'Are you sure you want to unsubscribe?'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
'CANCEL',
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
groupList.removePodcast(
|
||||
widget.podcastLocal.id);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'CONFIRM',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -469,8 +503,7 @@ class _RenameGroupState extends State<RenameGroup> {
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20, left: 20, right: 20, bottom: 20),
|
||||
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
|
||||
actionsPadding: EdgeInsets.all(0),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
@ -497,7 +530,8 @@ class _RenameGroupState extends State<RenameGroup> {
|
||||
style: TextStyle(color: Theme.of(context).accentColor)),
|
||||
)
|
||||
],
|
||||
title: SizedBox(width: context.width - 160, child: Text('Edit group name')),
|
||||
title: SizedBox(
|
||||
width: context.width - 160, child: Text('Edit group name')),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
|
@ -152,6 +152,8 @@ class DownloadState extends ChangeNotifier {
|
||||
openFileFromNotification: false,
|
||||
);
|
||||
_episodeTasks.add(EpisodeTask(episode, taskId));
|
||||
var dbHelper = DBHelper();
|
||||
await dbHelper.saveDownloaded(taskId, episode.enclosureUrl);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -2,29 +2,41 @@ import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../local_storage/key_value_storage.dart';
|
||||
import '../type/podcastlocal.dart';
|
||||
import '../type/episodebrief.dart';
|
||||
import 'download_state.dart';
|
||||
|
||||
void callbackDispatcher() {
|
||||
if(Platform.isAndroid)
|
||||
Workmanager.executeTask((task, inputData) async {
|
||||
var dbHelper = DBHelper();
|
||||
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
||||
//lastWork is a indicator for if the app was opened since last backgroundwork
|
||||
//if the app wes opend,then the old marked new episode would be marked not new.
|
||||
KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey);
|
||||
int lastWork = await lastWorkStorage.getInt();
|
||||
await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async {
|
||||
await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork);
|
||||
print('Refresh ' + podcastLocal.title);
|
||||
if (Platform.isAndroid)
|
||||
Workmanager.executeTask((task, inputData) async {
|
||||
var dbHelper = DBHelper();
|
||||
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
||||
//lastWork is a indicator for if the app was opened since last backgroundwork
|
||||
//if the app wes opend,then the old marked new episode would be marked not new.
|
||||
KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey);
|
||||
int lastWork = await lastWorkStorage.getInt();
|
||||
await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async {
|
||||
int updateCount =
|
||||
await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork);
|
||||
bool autoDownload = await dbHelper.getAutoDownload(podcastLocal.id);
|
||||
if (autoDownload && updateCount > 0) {
|
||||
List<EpisodeBrief> episodes =
|
||||
await dbHelper.getNewEpisodes(podcastLocal.id);
|
||||
episodes.forEach((episode) {
|
||||
DownloadState().startTask(episode);
|
||||
});
|
||||
}
|
||||
print('Refresh ' + podcastLocal.title);
|
||||
});
|
||||
await lastWorkStorage.saveInt(1);
|
||||
KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey);
|
||||
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
|
||||
return Future.value(true);
|
||||
});
|
||||
await lastWorkStorage.saveInt(1);
|
||||
KeyValueStorage refreshstorage = KeyValueStorage(refreshdateKey);
|
||||
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
|
||||
return Future.value(true);
|
||||
});
|
||||
}
|
||||
|
||||
ThemeData lightTheme = ThemeData(
|
||||
|
Loading…
x
Reference in New Issue
Block a user