import 'package:sqflite/sqflite.dart'; import 'dart:async'; import 'package:path/path.dart'; import 'package:intl/intl.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:dio/dio.dart'; import '../type/podcastlocal.dart'; import '../state/audio_state.dart'; import '../type/episodebrief.dart'; import '../webfeed/webfeed.dart'; import '../type/sub_history.dart'; class DBHelper { static Database _db; Future get database async { if (_db != null) return _db; _db = await initDb(); return _db; } initDb() async { var documentsDirectory = await getDatabasesPath(); String path = join(documentsDirectory, "podcasts.db"); Database theDb = await openDatabase(path, version: 3, onCreate: _onCreate, onUpgrade: _onUpgrade); return theDb; } void _onCreate(Database db, int version) async { await db .execute("""CREATE TABLE PodcastLocal(id TEXT PRIMARY KEY,title TEXT, 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, 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, description TEXT, feed_id TEXT, feed_link TEXT, milliseconds INTEGER, duration INTEGER DEFAULT 0, explicit INTEGER DEFAULT 0, liked INTEGER DEFAULT 0, liked_date INTEGER DEFAULT 0, downloaded TEXT DEFAULT 'ND', download_date INTEGER DEFAULT 0, media_id TEXT, is_new INTEGER DEFAULT 0)"""); await db.execute( """CREATE TABLE PlayHistory(id INTEGER PRIMARY KEY, title TEXT, enclosure_url TEXT, seconds REAL, seek_value REAL, add_date INTEGER, listen_time INTEGER DEFAULT 0)"""); await db.execute( """CREATE TABLE SubscribeHistory(id TEXT PRIMARY KEY, title TEXT, rss_url TEXT UNIQUE, add_date INTEGER, remove_date INTEGER DEFAULT 0, status INTEGER DEFAULT 0)"""); } void _onUpgrade(Database db, int oldVersion, int newVersion) async { if (oldVersion == 1) { await db.execute( "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"); } } Future> getPodcastLocal(List podcasts) async { var dbClient = await database; List podcastLocal = []; for (var s in podcasts) { List list; list = await dbClient.rawQuery( """SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider, link ,update_count, episode_count FROM PodcastLocal WHERE id = ?""", [s]); if (list.length > 0) podcastLocal.add(PodcastLocal( list.first['title'], list.first['imageUrl'], list.first['rssUrl'], list.first['primaryColor'], list.first['author'], list.first['id'], list.first['imagePath'], list.first['provider'], list.first['link'], upateCount: list.first['update_count'], episodeCount: list.first['episode_count'])); } return podcastLocal; } Future> getPodcastLocalAll() async { var dbClient = await database; List list = await dbClient.rawQuery( 'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, provider, link FROM PodcastLocal ORDER BY add_date DESC'); List podcastLocal = []; for (var i in list) { podcastLocal.add(PodcastLocal( i['title'], i['imageUrl'], i['rssUrl'], i['primaryColor'], i['author'], i['id'], i['imagePath'], list.first['provider'], list.first['link'])); } return podcastLocal; } Future getPodcastCounts(String id) async { var dbClient = await database; List list = await dbClient .rawQuery('SELECT episode_count FROM PodcastLocal WHERE id = ?', [id]); return list.first['episode_count']; } Future getPodcastUpdateCounts(String id) async { var dbClient = await database; List list = await dbClient.rawQuery( 'SELECt count(*) as count FROM Episodes WHERE feed_id = ? AND is_new = 1', [id]); return list.first['count']; } Future getSkipSeconds(String id) async { var dbClient = await database; List list = await dbClient .rawQuery('SELECT skip_seconds FROM PodcastLocal WHERE id = ?', [id]); return list.first['skip_seconds']; } Future saveSkipSeconds(String id, int seconds) async { var dbClient = await database; return await dbClient.rawUpdate( "UPDATE PodcastLocal SET skip_seconds = ? WHERE id = ?", [seconds, id]); } Future getAutoDownload(String id) async { var dbClient = await database; List list = await dbClient .rawQuery('SELECT auto_download FROM PodcastLocal WHERE id = ?', [id]); return list.first['auto_download'] == 1; } Future 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 checkPodcast(String url) async { var dbClient = await database; List list = await dbClient .rawQuery('SELECT id FROM PodcastLocal WHERE rssUrl = ?', [url]); if (list.isEmpty) return ''; return list.first['id']; } Future savePodcastLocal(PodcastLocal podcastLocal) async { int _milliseconds = DateTime.now().millisecondsSinceEpoch; var dbClient = await database; await dbClient.transaction((txn) async { await txn.rawInsert( """INSERT OR IGNORE INTO PodcastLocal (id, title, imageUrl, rssUrl, primaryColor, author, description, add_date, imagePath, provider, link) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", [ podcastLocal.id, podcastLocal.title, podcastLocal.imageUrl, podcastLocal.rssUrl, podcastLocal.primaryColor, podcastLocal.author, podcastLocal.description, _milliseconds, podcastLocal.imagePath, podcastLocal.provider, podcastLocal.link ]); await txn.rawInsert( """REPLACE INTO SubscribeHistory(id, title, rss_url, add_date) VALUES (?, ?, ?, ?)""", [ podcastLocal.id, podcastLocal.title, podcastLocal.rssUrl, _milliseconds ]); }); } Future saveFiresideData(List list) async { var dbClient = await database; int result = await dbClient.rawUpdate( 'UPDATE PodcastLocal SET background_image = ? , hosts = ? WHERE id = ?', [list[1], list[2], list[0]]); print('Fireside data save in sqllite'); return result; } Future> getFiresideData(String id) async { var dbClient = await database; List list = await dbClient.rawQuery( 'SELECT background_image, hosts FROM PodcastLocal WHERE id = ?', [id]); if (list.length > 0) { List data = [list.first['background_image'], list.first['hosts']]; return data; } return ['', '']; } Future delPodcastLocal(String id) async { var dbClient = await database; await dbClient.rawDelete('DELETE FROM PodcastLocal WHERE id =?', [id]); List list = await dbClient.rawQuery( """SELECT downloaded FROM Episodes WHERE downloaded != 'ND' AND feed_id = ?""", [id]); for (var i in list) { if (i != null) await FlutterDownloader.remove( taskId: i['downloaded'], shouldDeleteContent: true); print('Removed all download tasks'); } await dbClient.rawDelete('DELETE FROM Episodes WHERE feed_id=?', [id]); int _milliseconds = DateTime.now().millisecondsSinceEpoch; await dbClient.rawUpdate( """UPDATE SubscribeHistory SET remove_date = ? , status = ? WHERE id = ?""", [_milliseconds, 1, id]); } Future saveHistory(PlayHistory history) async { var dbClient = await database; int _milliseconds = DateTime.now().millisecondsSinceEpoch; List recent = await getPlayHistory(1); if (recent.length == 1) { if (recent.first.url == history.url) { await dbClient.rawDelete("DELETE FROM PlayHistory WHERE add_date = ?", [recent.first.playdate.millisecondsSinceEpoch]); } } int result = await dbClient.transaction((txn) async { return await txn.rawInsert( """REPLACE INTO PlayHistory (title, enclosure_url, seconds, seek_value, add_date, listen_time) VALUES (?, ?, ?, ?, ?, ?) """, [ history.title, history.url, history.seconds, history.seekValue, _milliseconds, history.seekValue > 0.95 ? 1 : 0 ]); }); return result; } Future> getPlayHistory(int top) async { var dbClient = await database; List list = await dbClient.rawQuery( """SELECT title, enclosure_url, seconds, seek_value, add_date FROM PlayHistory ORDER BY add_date DESC LIMIT ? """, [top]); List playHistory = []; for (var record in list) { playHistory.add(PlayHistory(record['title'], record['enclosure_url'], record['seconds'], record['seek_value'], playdate: DateTime.fromMillisecondsSinceEpoch(record['add_date']))); } return playHistory; } Future isListened(String url) async { var dbClient = await database; int i = 0; List list = await dbClient.rawQuery( "SELECT listen_time FROM PlayHistory WHERE enclosure_url = ?", [url]); if (list.length == 0) return 0; else { for (var element in list) { i += element['listen_time']; } return i; } } Future> getSubHistory() async { var dbClient = await database; List list = await dbClient.rawQuery( """SELECT title, rss_url, add_date, remove_date, status FROM SubscribeHistory ORDER BY add_date DESC"""); return list .map((record) => SubHistory( record['status'] == 0 ? true : false, DateTime.fromMillisecondsSinceEpoch(record['remove_date']), DateTime.fromMillisecondsSinceEpoch(record['add_date']), record['rss_url'], record['title'])) .toList(); } Future listenMins(int day) async { var dbClient = await database; var now = DateTime.now(); var start = DateTime(now.year, now.month, now.day) .subtract(Duration(days: day)) .millisecondsSinceEpoch; var end = DateTime(now.year, now.month, now.day) .subtract(Duration(days: (day - 1))) .millisecondsSinceEpoch; List list = await dbClient.rawQuery( "SELECT seconds FROM PlayHistory WHERE add_date > ? AND add_date < ?", [start, end]); double sum = 0; if (list.isEmpty) { sum = 0; } else { for (var record in list) sum += record['seconds']; } return (sum ~/ 60).toDouble(); } Future getPosition(EpisodeBrief episodeBrief) async { var dbClient = await database; List list = await dbClient.rawQuery( """SELECT title, enclosure_url, seconds, seek_value, add_date FROM PlayHistory WHERE enclosure_url = ? ORDER BY add_date DESC LIMIT 1""", [episodeBrief.enclosureUrl]); return list.isNotEmpty ? PlayHistory(list.first['title'], list.first['enclosure_url'], list.first['seconds'], list.first['seek_value'], playdate: DateTime.fromMillisecondsSinceEpoch(list.first['add_date'])) : PlayHistory(episodeBrief.title, episodeBrief.enclosureUrl, 0, 0); } /// Check if episode was marked listend. Future checkMarked(EpisodeBrief episodeBrief) async { var dbClient = await database; List list = await dbClient.rawQuery( """SELECT title, enclosure_url, seconds, seek_value, add_date FROM PlayHistory WHERE enclosure_url = ? AND seek_value = 1 ORDER BY add_date DESC LIMIT 1""", [episodeBrief.enclosureUrl]); return list.isEmpty; } DateTime _parsePubDate(String pubDate) { if (pubDate == null) return DateTime.now(); DateTime date; RegExp yyyy = RegExp(r'[1-2][0-9]{3}'); RegExp hhmm = RegExp(r'[0-2][0-9]\:[0-5][0-9]'); RegExp ddmmm = RegExp(r'[0-3][0-9]\s[A-Z][a-z]{2}'); RegExp mmDd = RegExp(r'([1-2][0-9]{3}\-[0-1]|\s)[0-9]\-[0-3][0-9]'); // RegExp timezone RegExp z = RegExp(r'(\+|\-)[0-1][0-9]00'); String timezone = z.stringMatch(pubDate); int timezoneInt = 0; if (timezone != null) { if (timezone.substring(0, 1) == '-') { timezoneInt = int.parse(timezone.substring(1, 2)); } else { timezoneInt = -int.parse(timezone.substring(1, 2)); } } try { date = DateFormat('EEE, dd MMM yyyy HH:mm:ss Z', 'en_US').parse(pubDate); } catch (e) { try { date = DateFormat('dd MMM yyyy HH:mm:ss Z', 'en_US').parse(pubDate); } catch (e) { try { date = DateFormat('EEE, dd MMM yyyy HH:mm Z', 'en_US').parse(pubDate); } catch (e) { //parse date using regex, still have issue in parse maonth/day String year = yyyy.stringMatch(pubDate); String time = hhmm.stringMatch(pubDate); String month = ddmmm.stringMatch(pubDate); if (year != null && time != null && month != null) { date = DateFormat('dd MMM yyyy HH:mm', 'en_US') .parse(month + year + time); } else if (year != null && time != null && month == null) { String month = mmDd.stringMatch(pubDate); date = DateFormat('yyyy-MM-dd HH:mm', 'en_US') .parse(month + ' ' + time); print(date.toString()); } else { date = DateTime.now(); } } } } DateTime result = date .add(Duration(hours: timezoneInt)) .add(DateTime.now().timeZoneOffset); return result; } int _getExplicit(bool b) { int result; if (b == true) { result = 1; return result; } else { result = 0; return result; } } bool _isXimalaya(String input) { RegExp ximalaya = RegExp(r"ximalaya.com"); return ximalaya.hasMatch(input); } String _getDescription(String content, String description, String summary) { if (content.length >= description.length) { if (content.length >= summary.length) { return content; } else { return summary; } } else if (description.length >= summary.length) { return description; } else { return summary; } } Future savePodcastRss(RssFeed feed, String id) async { feed.items.removeWhere((item) => item == null); int result = feed.items.length; var dbClient = await database; String description, url; for (int i = 0; i < result; i++) { print(feed.items[i].title); description = _getDescription(feed.items[i].content.value ?? '', feed.items[i].description ?? '', feed.items[i].itunes.summary ?? ''); if (feed.items[i].enclosure != null) { _isXimalaya(feed.items[i].enclosure.url) ? url = feed.items[i].enclosure.url.split('=').last : url = feed.items[i].enclosure.url; } final title = feed.items[i].itunes.title ?? feed.items[i].title; final length = feed.items[i]?.enclosure?.length; final pubDate = feed.items[i].pubDate; final date = _parsePubDate(pubDate); print(date); final milliseconds = date.millisecondsSinceEpoch; final duration = feed.items[i].itunes.duration?.inSeconds ?? 0; final explicit = _getExplicit(feed.items[i].itunes.explicit); if (url != null) { await dbClient.transaction((txn) { return txn.rawInsert( """INSERT OR IGNORE INTO Episodes(title, enclosure_url, enclosure_length, pubDate, description, feed_id, milliseconds, duration, explicit, media_id) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", [ title, url, length, pubDate, description, id, milliseconds, duration, explicit, url ]); }); } } int countUpdate = Sqflite.firstIntValue(await dbClient .rawQuery('SELECT COUNT(*) FROM Episodes WHERE feed_id = ?', [id])); await dbClient.rawUpdate( """UPDATE PodcastLocal SET episode_count = ? WHERE id = ?""", [countUpdate, id]); return result; } Future updatePodcastRss(PodcastLocal podcastLocal, {int removeMark = 0}) async { BaseOptions options = BaseOptions( connectTimeout: 20000, receiveTimeout: 20000, ); try { Response response = await Dio(options).get(podcastLocal.rssUrl); if (response.statusCode == 200) { var feed = RssFeed.parse(response.data); String url, description; feed.items.removeWhere((item) => item == null); int result = feed.items.length; var dbClient = await database; int count = Sqflite.firstIntValue(await dbClient.rawQuery( 'SELECT COUNT(*) FROM Episodes WHERE feed_id = ?', [podcastLocal.id])); if (removeMark == 0) await dbClient.rawUpdate( "UPDATE Episodes SET is_new = 0 WHERE feed_id = ?", [podcastLocal.id]); for (var item in feed.items) { print(item.title); description = _getDescription(item.content.value ?? '', item.description ?? '', item.itunes.summary ?? ''); if (item.enclosure?.url != null) { _isXimalaya(item.enclosure.url) ? url = item.enclosure.url.split('=').last : url = item.enclosure.url; } final title = item.itunes.title ?? item.title; final length = item?.enclosure?.length ?? 0; final pubDate = item.pubDate; final date = _parsePubDate(pubDate); final milliseconds = date.millisecondsSinceEpoch; final duration = item.itunes.duration?.inSeconds ?? 0; final explicit = _getExplicit(item.itunes.explicit); if (url != null) { await dbClient.transaction((txn) async { await txn.rawInsert( """INSERT OR IGNORE INTO Episodes(title, enclosure_url, enclosure_length, pubDate, description, feed_id, milliseconds, duration, explicit, media_id, is_new) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)""", [ title, url, length, pubDate, description, podcastLocal.id, milliseconds, duration, explicit, url, ]); }); } } int countUpdate = Sqflite.firstIntValue(await dbClient.rawQuery( 'SELECT COUNT(*) FROM Episodes WHERE feed_id = ?', [podcastLocal.id])); await dbClient.rawUpdate( """UPDATE PodcastLocal SET update_count = ?, episode_count = ? WHERE id = ?""", [countUpdate - count, countUpdate, podcastLocal.id]); return countUpdate - count; } return 0; } catch (e) { print(e); return -1; } } Future> getRssItem(String id, int i, bool reverse) async { var dbClient = await database; List episodes = []; if (i == -1) { List list = await dbClient .rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feedTitle, 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 P.id = ? ORDER BY E.milliseconds ASC""", [id]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feedTitle'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } else if (reverse) { List list = await dbClient .rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feedTitle, 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 P.id = ? ORDER BY E.milliseconds ASC LIMIT ?""", [id, i]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feedTitle'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } else { List list = await dbClient .rawQuery("""SELECT E.title, E.enclosure_url, E.enclosure_length, E.milliseconds, P.imagePath, P.title as feedTitle, 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 P.id = ? ORDER BY E.milliseconds DESC LIMIT ?""", [id, i]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feedTitle'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } } Future> getNewEpisodes(String id) async { var dbClient = await database; List episodes = []; List list; if (id == 'all') 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.is_new = 1 AND E.downloaded = 'ND' AND P.auto_download = 1 ORDER BY E.milliseconds ASC""", ); else 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.is_new = 1 AND E.downloaded = 'ND' AND E.feed_id = ? ORDER BY E.milliseconds ASC""", [id]); if (list.isNotEmpty) for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } Future> getRssItemTop(String id) async { var dbClient = await database; List episodes = []; List 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.feed_id = ? ORDER BY E.milliseconds DESC LIMIT 2""", [id]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } //Future getRssItemDownload(String url) async { // var dbClient = await database; // EpisodeBrief episode; // List 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.enclosure_url = ? ORDER BY E.milliseconds DESC LIMIT 3""", // [url]); // if (list != null) // episode = EpisodeBrief( // list.first['title'], // list.first['enclosure_url'], // list.first['enclosure_length'], // list.first['milliseconds'], // list.first['feed_title'], // list.first['primaryColor'], // list.first['liked'], // list.first['downloaded'], // list.first['duration'], // list.first['explicit'], // list.first['imagePath'], // list.first['media_id'], // list.first['is_new'], // list.first['skip_seconds']); // return episode; //} Future> getRecentRssItem(int top) async { var dbClient = await database; List episodes = []; List 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 ORDER BY E.milliseconds DESC LIMIT ? """, [top]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } Future> getGroupRssItem( int top, List group) async { var dbClient = await database; List episodes = []; if (group.length > 0) { List s = group.map((e) => "'$e'").toList(); List 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 P.id in (${s.join(',')}) ORDER BY E.milliseconds DESC LIMIT ? """, [top]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } } return episodes; } Future> getRecentNewRssItem() async { var dbClient = await database; List episodes = []; List 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 ORDER BY E.milliseconds DESC """, ); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } //Future> getNewRssItem(String id) async { // var dbClient = await database; // List episodes = []; // List 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> getOutdatedEpisode(int deadline) async { var dbClient = await database; List episodes = []; List 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 E.download_date < ? AND E.enclosure_url != E.media_id ORDER BY E.milliseconds DESC""", [deadline]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } return episodes; } Future> getDownloadedEpisode(int mode) async { var dbClient = await database; List episodes = []; List list; //Ordered by date if (mode == 0) list = await dbClient.rawQuery( """SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, 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 E.enclosure_url != E.media_id ORDER BY E.download_date DESC""", ); //Ordered by date else if (mode == 1) list = await dbClient.rawQuery( """SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, 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 E.enclosure_url != E.media_id ORDER BY E.download_date ASC""", ); //Ordered by size else if (mode == 2) list = await dbClient.rawQuery( """SELECT E.title, E.enclosure_url, E.enclosure_length, E.download_date, 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 E.enclosure_url != E.media_id ORDER BY E.enclosure_length DESC""", ); for (var i in list) { episodes.add( EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'], downloadDate: i['download_date']), ); } return episodes; } removeAllNewMark() async { var dbClient = await database; await dbClient.transaction((txn) async { await txn.rawUpdate("UPDATE Episodes SET is_new = 0 "); }); } Future> getGroupNewRssItem(List group) async { var dbClient = await database; List episodes = []; if (group.length > 0) { List s = group.map((e) => "'$e'").toList(); List 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 P.id in (${s.join(',')}) AND is_new = 1 ORDER BY E.milliseconds DESC""", ); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } } return episodes; } removeGroupNewMark(List group) async { var dbClient = await database; if (group.length > 0) { List s = group.map((e) => "'$e'").toList(); await dbClient.transaction((txn) async { await txn.rawUpdate( "UPDATE Episodes SET is_new = 0 WHERE feed_id in (${s.join(',')})"); }); } } removeEpisodeNewMark(String url) async { var dbClient = await database; await dbClient.transaction((txn) async { await txn.rawUpdate( "UPDATE Episodes SET is_new = 0 WHERE enclosure_url = ?", [url]); }); print('remove new episode'); } Future> getLikedRssItem(int i, int sortBy) async { var dbClient = await database; List episodes = []; if (sortBy == 0) { List 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.liked = 1 ORDER BY E.milliseconds DESC LIMIT ?""", [i]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } } else { List 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.liked = 1 ORDER BY E.liked_date DESC LIMIT ?""", [i]); for (var i in list) { episodes.add(EpisodeBrief( i['title'], i['enclosure_url'], i['enclosure_length'], i['milliseconds'], i['feed_title'], i['primaryColor'], i['liked'], i['downloaded'], i['duration'], i['explicit'], i['imagePath'], i['media_id'], i['is_new'], i['skip_seconds'])); } } return episodes; } setLiked(String url) async { var dbClient = await database; int milliseconds = DateTime.now().millisecondsSinceEpoch; await dbClient.transaction((txn) async { await txn.rawUpdate( "UPDATE Episodes SET liked = 1, liked_date = ? WHERE enclosure_url= ?", [milliseconds, url]); }); } setUniked(String url) async { var dbClient = await database; await dbClient.transaction((txn) async { await txn.rawUpdate( "UPDATE Episodes SET liked = 0 WHERE enclosure_url = ?", [url]); }); } Future isLiked(String url) async { var dbClient = await database; List list = await dbClient .rawQuery("SELECT liked FROM Episodes WHERE enclosure_url = ?", [url]); return list.first['liked'] == 0 ? false : true; } Future isDownloaded(String url) async { var dbClient = await database; List list = await dbClient.rawQuery( "SELECT downloaded FROM Episodes WHERE enclosure_url = ?", [url]); return list.first['downloaded'] == 'ND' ? false : true; } Future saveDownloaded(String url, String id) async { var dbClient = await database; int milliseconds = DateTime.now().millisecondsSinceEpoch; int count; await dbClient.transaction((txn) async { count = await txn.rawUpdate( "UPDATE Episodes SET downloaded = ?, download_date = ? WHERE enclosure_url = ?", [id, milliseconds, url]); }); return count; } Future saveMediaId(String url, String path, String id, int size) async { var dbClient = await database; int milliseconds = DateTime.now().millisecondsSinceEpoch; int count; await dbClient.transaction((txn) async { count = await txn.rawUpdate( "UPDATE Episodes SET enclosure_length = ?, media_id = ?, download_date = ?, downloaded = ? WHERE enclosure_url = ?", [size, path, milliseconds, id, url]); }); return count; } Future delDownloaded(String url) async { var dbClient = await database; int count; await dbClient.transaction((txn) async { count = await txn.rawUpdate( "UPDATE Episodes SET downloaded = 'ND', media_id = ? WHERE enclosure_url = ?", [url, url]); }); print('Deleted ' + url); return count; } Future getDescription(String url) async { var dbClient = await database; List list = await dbClient.rawQuery( 'SELECT description FROM Episodes WHERE enclosure_url = ?', [url]); String description = list[0]['description']; return description; } Future getFeedDescription(String id) async { var dbClient = await database; List list = await dbClient .rawQuery('SELECT description FROM PodcastLocal WHERE id = ?', [id]); String description = list[0]['description']; return description; } Future getRssItemWithUrl(String url) async { var dbClient = await database; EpisodeBrief episode; List 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.enclosure_url = ?""", [url]); if (list.isEmpty) { return null; } else { episode = EpisodeBrief( list.first['title'], list.first['enclosure_url'], list.first['enclosure_length'], list.first['milliseconds'], list.first['feed_title'], list.first['primaryColor'], list.first['liked'], list.first['downloaded'], list.first['duration'], list.first['explicit'], list.first['imagePath'], list.first['media_id'], list.first['is_new'], list.first['skip_seconds']); return episode; } } Future getRssItemWithMediaId(String id) async { var dbClient = await database; EpisodeBrief episode; List 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.media_id = ?""", [id]); if (list.isEmpty) return null; else { episode = EpisodeBrief( list.first['title'], list.first['enclosure_url'], list.first['enclosure_length'], list.first['milliseconds'], list.first['feed_title'], list.first['primaryColor'], list.first['liked'], list.first['downloaded'], list.first['duration'], list.first['explicit'], list.first['imagePath'], list.first['media_id'], list.first['is_new'], list.first['skip_seconds']); return episode; } } Future getImageUrl(String url) async { var dbClient = await database; List list = await dbClient.rawQuery( """SELECT P.imageUrl FROM Episodes E INNER JOIN PodcastLocal P ON E.feed_id = P.id WHERE E.enclosure_url = ?""", [url]); if (list.isEmpty) return null; return list.first["imageUrl"]; } }