2021-04-02 17:06:27 +02:00
/**
2021-04-03 19:29:40 +02:00
* SPDX - FileCopyrightText : 2021 Bart De Vries < bart @ mogwai . be >
2021-04-02 17:06:27 +02:00
*
* SPDX - License - Identifier : GPL - 2.0 - only OR GPL - 3.0 - only OR LicenseRef - KDE - Accepted - GPL
*/
2021-05-01 21:35:37 +02:00
# include "datamanager.h"
2021-09-08 11:46:22 +02:00
# include "datamanagerlogging.h"
2021-05-14 16:46:54 +02:00
2021-04-02 22:31:34 +02:00
# include <QDateTime>
# include <QDir>
# include <QSqlDatabase>
# include <QSqlError>
# include <QStandardPaths>
# include <QUrl>
# include <QXmlStreamReader>
# include <QXmlStreamWriter>
2021-04-02 17:06:27 +02:00
2021-07-04 18:35:09 +02:00
# include "audiomanager.h"
# include "database.h"
# include "entry.h"
# include "feed.h"
# include "fetcher.h"
# include "settingsmanager.h"
# include "storagemanager.h"
2021-10-29 17:00:52 +02:00
# include "sync/sync.h"
2021-07-04 18:35:09 +02:00
2021-04-02 17:06:27 +02:00
DataManager : : DataManager ( )
{
2021-05-01 21:35:37 +02:00
connect (
& Fetcher : : instance ( ) ,
& Fetcher : : feedDetailsUpdated ,
this ,
[ this ] ( const QString & url , const QString & name , const QString & image , const QString & link , const QString & description , const QDateTime & lastUpdated ) {
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Start updating feed details " < < m_feeds ;
2021-05-01 21:35:37 +02:00
Feed * feed = getFeed ( url ) ;
if ( feed ! = nullptr ) {
feed - > setName ( name ) ;
feed - > setImage ( image ) ;
feed - > setLink ( link ) ;
feed - > setDescription ( description ) ;
feed - > setLastUpdated ( lastUpdated ) ;
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Retrieving authors " ;
2021-05-01 21:35:37 +02:00
feed - > updateAuthors ( ) ;
2021-05-08 16:13:28 +02:00
// For feeds that have just been added, this is probably the point
// where the Feed object gets created; let's set refreshing to
// true in order to show user feedback that the feed is still
// being fetched
feed - > setRefreshing ( true ) ;
2021-05-01 21:35:37 +02:00
}
} ) ;
2021-04-03 11:44:08 +02:00
connect ( & Fetcher : : instance ( ) , & Fetcher : : entryAdded , this , [ this ] ( const QString & feedurl , const QString & id ) {
2021-09-23 19:23:39 +02:00
Q_UNUSED ( feedurl )
2021-04-03 11:44:08 +02:00
// Only add the new entry to m_entries
// we will repopulate m_entrymap once all new entries have been added,
// such that m_entrymap will show all new entries in the correct order
m_entries [ id ] = nullptr ;
} ) ;
connect ( & Fetcher : : instance ( ) , & Fetcher : : feedUpdated , this , [ this ] ( const QString & feedurl ) {
// Update m_entrymap for feedurl, such that the new and old entries show
// up in the correct order
// TODO: put this code into a separate method and re-use this in the constructor
QSqlQuery query ;
m_entrymap [ feedurl ] . clear ( ) ;
query . prepare ( QStringLiteral ( " SELECT id FROM Entries WHERE feed=:feed ORDER BY updated DESC; " ) ) ;
query . bindValue ( QStringLiteral ( " :feed " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
while ( query . next ( ) ) {
m_entrymap [ feedurl ] + = query . value ( QStringLiteral ( " id " ) ) . toString ( ) ;
}
2021-04-09 21:43:29 +02:00
// Check for "new" entries
2021-04-10 08:46:14 +02:00
if ( SettingsManager : : self ( ) - > autoQueue ( ) ) {
2022-05-24 22:27:20 +02:00
query . prepare ( QStringLiteral ( " SELECT id FROM Entries WHERE feed=:feed AND new=:new ORDER BY updated ASC; " ) ) ;
2021-04-09 21:43:29 +02:00
query . bindValue ( QStringLiteral ( " :feed " ) , feedurl ) ;
query . bindValue ( QStringLiteral ( " :new " ) , true ) ;
Database : : instance ( ) . execute ( query ) ;
while ( query . next ( ) ) {
2021-05-01 21:00:12 +02:00
QString id = query . value ( QStringLiteral ( " id " ) ) . toString ( ) ;
2021-09-06 16:54:59 +02:00
getEntry ( id ) - > setQueueStatusInternal ( true ) ;
2021-04-10 08:46:14 +02:00
if ( SettingsManager : : self ( ) - > autoDownload ( ) ) {
2021-05-07 22:09:15 +02:00
if ( getEntry ( id ) & & getEntry ( id ) - > hasEnclosure ( ) & & getEntry ( id ) - > enclosure ( ) ) {
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Start downloading " < < getEntry ( id ) - > title ( ) ;
2021-04-09 23:39:41 +02:00
getEntry ( id ) - > enclosure ( ) - > download ( ) ;
2021-04-10 08:46:14 +02:00
}
2021-04-09 23:39:41 +02:00
}
2021-04-09 21:43:29 +02:00
}
}
2021-04-03 11:44:08 +02:00
Q_EMIT feedEntriesUpdated ( feedurl ) ;
2021-04-02 22:31:34 +02:00
} ) ;
2021-04-02 17:06:27 +02:00
// Only read unique feedurls and entry ids from the database.
2021-04-02 22:31:34 +02:00
// The feed and entry datastructures will be loaded lazily.
2021-04-02 17:06:27 +02:00
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT url FROM Feeds; " ) ) ;
Database : : instance ( ) . execute ( query ) ;
while ( query . next ( ) ) {
m_feedmap + = query . value ( QStringLiteral ( " url " ) ) . toString ( ) ;
2021-04-22 10:53:02 +02:00
m_feeds [ query . value ( QStringLiteral ( " url " ) ) . toString ( ) ] = nullptr ;
2021-04-02 17:06:27 +02:00
}
for ( auto & feedurl : m_feedmap ) {
2021-04-03 11:44:08 +02:00
query . prepare ( QStringLiteral ( " SELECT id FROM Entries WHERE feed=:feed ORDER BY updated DESC; " ) ) ;
2021-04-02 17:06:27 +02:00
query . bindValue ( QStringLiteral ( " :feed " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
while ( query . next ( ) ) {
m_entrymap [ feedurl ] + = query . value ( QStringLiteral ( " id " ) ) . toString ( ) ;
2021-04-03 11:44:08 +02:00
m_entries [ query . value ( QStringLiteral ( " id " ) ) . toString ( ) ] = nullptr ;
2021-04-02 17:06:27 +02:00
}
}
2021-06-05 20:12:42 +02:00
// qCDebug(kastsDataManager) << "entrymap contains:" << m_entrymap;
2021-04-03 19:29:40 +02:00
query . prepare ( QStringLiteral ( " SELECT id FROM Queue ORDER BY listnr; " ) ) ;
Database : : instance ( ) . execute ( query ) ;
while ( query . next ( ) ) {
m_queuemap + = query . value ( QStringLiteral ( " id " ) ) . toString ( ) ;
}
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Queuemap contains: " < < m_queuemap ;
2021-04-02 17:06:27 +02:00
}
2021-05-01 21:35:37 +02:00
Feed * DataManager : : getFeed ( const int index ) const
2021-04-02 17:06:27 +02:00
{
return getFeed ( m_feedmap [ index ] ) ;
}
2021-05-01 21:35:37 +02:00
Feed * DataManager : : getFeed ( const QString & feedurl ) const
2021-04-02 17:06:27 +02:00
{
if ( m_feeds [ feedurl ] = = nullptr )
loadFeed ( feedurl ) ;
return m_feeds [ feedurl ] ;
}
2021-05-01 21:35:37 +02:00
Entry * DataManager : : getEntry ( const int feed_index , const int entry_index ) const
2021-04-02 22:31:34 +02:00
{
return getEntry ( m_entrymap [ m_feedmap [ feed_index ] ] [ entry_index ] ) ;
}
2021-05-01 21:35:37 +02:00
Entry * DataManager : : getEntry ( const Feed * feed , const int entry_index ) const
2021-04-02 22:31:34 +02:00
{
return getEntry ( m_entrymap [ feed - > url ( ) ] [ entry_index ] ) ;
}
2021-05-01 21:35:37 +02:00
Entry * DataManager : : getEntry ( const QString & id ) const
2021-04-02 22:31:34 +02:00
{
if ( m_entries [ id ] = = nullptr )
loadEntry ( id ) ;
return m_entries [ id ] ;
}
int DataManager : : feedCount ( ) const
{
return m_feedmap . count ( ) ;
}
2021-09-06 16:54:59 +02:00
QStringList DataManager : : getIdList ( const Feed * feed ) const
{
return m_entrymap [ feed - > url ( ) ] ;
}
2021-04-02 22:31:34 +02:00
int DataManager : : entryCount ( const int feed_index ) const
{
return m_entrymap [ m_feedmap [ feed_index ] ] . count ( ) ;
}
2021-05-01 21:35:37 +02:00
int DataManager : : entryCount ( const Feed * feed ) const
2021-04-02 22:31:34 +02:00
{
return m_entrymap [ feed - > url ( ) ] . count ( ) ;
}
2021-05-01 21:35:37 +02:00
int DataManager : : newEntryCount ( const Feed * feed ) const
2021-04-07 10:39:12 +02:00
{
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT COUNT (id) FROM Entries where feed=:feed AND new=1; " ) ) ;
query . bindValue ( QStringLiteral ( " :feed " ) , feed - > url ( ) ) ;
Database : : instance ( ) . execute ( query ) ;
if ( ! query . next ( ) )
return - 1 ;
return query . value ( 0 ) . toInt ( ) ;
}
2021-05-01 21:35:37 +02:00
void DataManager : : removeFeed ( Feed * feed )
2021-04-02 22:31:34 +02:00
{
2021-10-29 17:00:52 +02:00
QList < Feed * > feeds ;
feeds < < feed ;
removeFeeds ( feeds ) ;
2021-04-02 22:31:34 +02:00
}
2021-05-01 21:00:12 +02:00
void DataManager : : removeFeed ( const int index )
2021-04-02 22:31:34 +02:00
{
// Get feed pointer
2021-05-01 21:35:37 +02:00
Feed * feed = getFeed ( m_feedmap [ index ] ) ;
2021-10-29 17:00:52 +02:00
removeFeed ( feed ) ;
}
void DataManager : : removeFeeds ( const QStringList & feedurls )
{
QList < Feed * > feeds ;
for ( QString feedurl : feedurls ) {
feeds < < getFeed ( feedurl ) ;
2021-04-03 21:39:20 +02:00
}
2021-10-29 17:00:52 +02:00
removeFeeds ( feeds ) ;
}
void DataManager : : removeFeeds ( const QVariantList feedVariantList )
{
QList < Feed * > feeds ;
for ( QVariant feedVariant : feedVariantList ) {
if ( feedVariant . canConvert < Feed * > ( ) ) {
feeds < < feedVariant . value < Feed * > ( ) ;
}
2021-04-03 21:39:20 +02:00
}
2021-10-29 17:00:52 +02:00
removeFeeds ( feeds ) ;
}
2021-04-03 21:39:20 +02:00
2021-10-29 17:00:52 +02:00
void DataManager : : removeFeeds ( const QList < Feed * > & feeds )
{
for ( Feed * feed : feeds ) {
const QString feedurl = feed - > url ( ) ;
int index = m_feedmap . indexOf ( feedurl ) ;
2021-04-03 21:39:20 +02:00
2021-10-29 17:00:52 +02:00
qCDebug ( kastsDataManager ) < < " deleting feed " < < feedurl < < " with index " < < index ;
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
// Delete the object instances and mappings
// First delete entries in Queue
qCDebug ( kastsDataManager ) < < " delete queueentries of " < < feedurl ;
QStringList removeFromQueueList ;
for ( auto & id : m_queuemap ) {
if ( getEntry ( id ) - > feed ( ) - > url ( ) = = feedurl ) {
if ( AudioManager : : instance ( ) . entry ( ) = = getEntry ( id ) ) {
AudioManager : : instance ( ) . next ( ) ;
}
removeFromQueueList + = id ;
}
}
bulkQueueStatus ( false , removeFromQueueList ) ;
// Delete entries themselves
qCDebug ( kastsDataManager ) < < " delete entries of " < < feedurl ;
for ( auto & id : m_entrymap [ feedurl ] ) {
if ( getEntry ( id ) - > hasEnclosure ( ) )
getEntry ( id ) - > enclosure ( ) - > deleteFile ( ) ; // delete enclosure (if it exists)
if ( ! getEntry ( id ) - > image ( ) . isEmpty ( ) )
StorageManager : : instance ( ) . removeImage ( getEntry ( id ) - > image ( ) ) ; // delete entry images
delete m_entries [ id ] ; // delete pointer
m_entries . remove ( id ) ; // delete the hash key
}
m_entrymap . remove ( feedurl ) ; // remove all the entry mappings belonging to the feed
2021-10-01 15:46:17 +02:00
2021-10-29 17:00:52 +02:00
qCDebug ( kastsDataManager ) < < " Remove feed image " < < feed - > image ( ) < < " for feed " < < feedurl ;
if ( ! feed - > image ( ) . isEmpty ( ) )
StorageManager : : instance ( ) . removeImage ( feed - > image ( ) ) ;
m_feeds . remove ( m_feedmap [ index ] ) ; // remove from m_feeds
m_feedmap . removeAt ( index ) ; // remove from m_feedmap
delete feed ; // remove the pointer
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
// Then delete everything from the database
qCDebug ( kastsDataManager ) < < " delete database part of " < < feedurl ;
2021-09-21 22:36:54 +02:00
2021-10-29 17:00:52 +02:00
// Delete related Errors
QSqlQuery query ;
query . prepare ( QStringLiteral ( " DELETE FROM Errors WHERE url=:url; " ) ) ;
query . bindValue ( QStringLiteral ( " :url " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
// Delete Authors
query . prepare ( QStringLiteral ( " DELETE FROM Authors WHERE feed=:feed; " ) ) ;
query . bindValue ( QStringLiteral ( " :feed " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
// Delete Chapters
query . prepare ( QStringLiteral ( " DELETE FROM Chapters WHERE feed=:feed; " ) ) ;
query . bindValue ( QStringLiteral ( " :feed " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
// Delete Entries
query . prepare ( QStringLiteral ( " DELETE FROM Entries WHERE feed=:feed; " ) ) ;
query . bindValue ( QStringLiteral ( " :feed " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
// Delete Enclosures
query . prepare ( QStringLiteral ( " DELETE FROM Enclosures WHERE feed=:feed; " ) ) ;
query . bindValue ( QStringLiteral ( " :feed " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
// Delete Feed
query . prepare ( QStringLiteral ( " DELETE FROM Feeds WHERE url=:url; " ) ) ;
query . bindValue ( QStringLiteral ( " :url " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
// Save this action to the database (including timestamp) in order to be
// able to sync with remote services
Sync : : instance ( ) . storeRemoveFeedAction ( feedurl ) ;
Q_EMIT feedRemoved ( index ) ;
}
// if settings allow, then upload these changes immediately to sync server
Sync : : instance ( ) . doQuickSync ( ) ;
2021-04-02 22:31:34 +02:00
}
2021-04-21 10:43:21 +02:00
void DataManager : : addFeed ( const QString & url )
{
addFeed ( url , true ) ;
}
void DataManager : : addFeed ( const QString & url , const bool fetch )
2021-04-02 22:31:34 +02:00
{
2021-10-29 17:00:52 +02:00
addFeeds ( QStringList ( url ) , fetch ) ;
}
void DataManager : : addFeeds ( const QStringList & urls )
{
addFeeds ( urls , true ) ;
}
void DataManager : : addFeeds ( const QStringList & urls , const bool fetch )
{
if ( urls . count ( ) = = 0 )
return ;
2021-04-08 11:12:16 +02:00
// This method will add the relevant internal data structures, and then add
// a preliminary entry into the database. Those details (as well as entries,
// authors and enclosures) will be updated by calling Fetcher::fetch() which
// will trigger a full update of the feed and all related items.
2021-10-29 17:00:52 +02:00
for ( QString url : urls ) {
qCDebug ( kastsDataManager ) < < " Adding feed " ;
if ( feedExists ( url ) ) {
qCDebug ( kastsDataManager ) < < " Feed already exists " ;
2021-11-19 10:32:00 +01:00
continue ;
2021-10-29 17:00:52 +02:00
}
qCDebug ( kastsDataManager ) < < " Feed does not yet exist " ;
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
QUrl urlFromInput = QUrl : : fromUserInput ( url ) ;
QSqlQuery query ;
query . prepare (
QStringLiteral ( " INSERT INTO Feeds VALUES (:name, :url, :image, :link, :description, :deleteAfterCount, :deleteAfterType, :subscribed, "
" :lastUpdated, :new, :notify); " ) ) ;
query . bindValue ( QStringLiteral ( " :name " ) , urlFromInput . toString ( ) ) ;
query . bindValue ( QStringLiteral ( " :url " ) , urlFromInput . toString ( ) ) ;
query . bindValue ( QStringLiteral ( " :image " ) , QLatin1String ( " " ) ) ;
query . bindValue ( QStringLiteral ( " :link " ) , QLatin1String ( " " ) ) ;
query . bindValue ( QStringLiteral ( " :description " ) , QLatin1String ( " " ) ) ;
query . bindValue ( QStringLiteral ( " :deleteAfterCount " ) , 0 ) ;
query . bindValue ( QStringLiteral ( " :deleteAfterType " ) , 0 ) ;
query . bindValue ( QStringLiteral ( " :subscribed " ) , QDateTime : : currentDateTime ( ) . toSecsSinceEpoch ( ) ) ;
query . bindValue ( QStringLiteral ( " :lastUpdated " ) , 0 ) ;
query . bindValue ( QStringLiteral ( " :new " ) , true ) ;
query . bindValue ( QStringLiteral ( " :notify " ) , false ) ;
Database : : instance ( ) . execute ( query ) ;
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
m_feeds [ urlFromInput . toString ( ) ] = nullptr ;
m_feedmap . append ( urlFromInput . toString ( ) ) ;
2021-04-02 22:31:34 +02:00
2021-10-29 17:00:52 +02:00
// Save this action to the database (including timestamp) in order to be
// able to sync with remote services
Sync : : instance ( ) . storeAddFeedAction ( urlFromInput . toString ( ) ) ;
2021-04-20 21:14:19 +02:00
2021-10-29 17:00:52 +02:00
Q_EMIT feedAdded ( urlFromInput . toString ( ) ) ;
}
2021-04-20 21:14:19 +02:00
2021-10-29 17:00:52 +02:00
if ( fetch ) {
Fetcher : : instance ( ) . fetch ( urls ) ;
2021-04-20 21:14:19 +02:00
}
2021-10-29 17:00:52 +02:00
// if settings allow, upload these changes immediately to sync servers
Sync : : instance ( ) . doQuickSync ( ) ;
2021-04-02 22:31:34 +02:00
}
2021-05-01 21:35:37 +02:00
Entry * DataManager : : getQueueEntry ( int index ) const
2021-04-03 13:49:33 +02:00
{
return getEntry ( m_queuemap [ index ] ) ;
}
int DataManager : : queueCount ( ) const
{
return m_queuemap . count ( ) ;
}
2021-05-01 21:00:12 +02:00
QStringList DataManager : : queue ( ) const
2021-04-11 23:07:21 +02:00
{
return m_queuemap ;
}
2021-05-01 21:35:37 +02:00
bool DataManager : : entryInQueue ( const Entry * entry )
2021-04-16 20:38:13 +02:00
{
2021-09-06 16:54:59 +02:00
return entryInQueue ( entry - > id ( ) ) ;
2021-04-16 20:38:13 +02:00
}
2021-09-06 16:54:59 +02:00
bool DataManager : : entryInQueue ( const QString & id ) const
2021-04-07 22:10:39 +02:00
{
return m_queuemap . contains ( id ) ;
}
2021-09-06 16:54:59 +02:00
void DataManager : : moveQueueItem ( const int from , const int to )
2021-04-15 23:10:40 +02:00
{
2021-09-06 16:54:59 +02:00
// First move the items in the internal data structure
m_queuemap . move ( from , to ) ;
// Then make sure that the database Queue table reflects these changes
updateQueueListnrs ( ) ;
// Make sure that the QueueModel is aware of the changes so it can update
Q_EMIT queueEntryMoved ( from , to ) ;
2021-04-15 23:10:40 +02:00
}
2021-09-06 16:54:59 +02:00
void DataManager : : addToQueue ( const QString & id )
2021-04-03 13:49:33 +02:00
{
2021-04-03 19:29:40 +02:00
// If item is already in queue, then stop here
2021-05-01 21:35:37 +02:00
if ( m_queuemap . contains ( id ) )
return ;
2021-04-03 19:29:40 +02:00
// Add to internal queuemap data structure
2021-04-03 13:49:33 +02:00
m_queuemap + = id ;
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Queue mapping is now: " < < m_queuemap ;
2021-04-03 19:29:40 +02:00
// Get index of this entry
const int index = m_queuemap . indexOf ( id ) ; // add new entry to end of queue
// Add to Queue database
QSqlQuery query ;
2021-04-17 20:55:01 +02:00
query . prepare ( QStringLiteral ( " INSERT INTO Queue VALUES (:index, :feedurl, :id, :playing); " ) ) ;
2021-04-03 19:29:40 +02:00
query . bindValue ( QStringLiteral ( " :index " ) , index ) ;
2021-09-06 16:54:59 +02:00
query . bindValue ( QStringLiteral ( " :feedurl " ) , getEntry ( id ) - > feed ( ) - > url ( ) ) ;
2021-04-03 19:29:40 +02:00
query . bindValue ( QStringLiteral ( " :id " ) , id ) ;
2021-04-17 20:55:01 +02:00
query . bindValue ( QStringLiteral ( " :playing " ) , false ) ;
2021-04-03 19:29:40 +02:00
Database : : instance ( ) . execute ( query ) ;
// Make sure that the QueueModel is aware of the changes
Q_EMIT queueEntryAdded ( index , id ) ;
2021-04-03 13:49:33 +02:00
}
2021-09-06 16:54:59 +02:00
void DataManager : : removeFromQueue ( const QString & id )
2021-04-03 13:49:33 +02:00
{
2021-09-06 16:54:59 +02:00
if ( ! entryInQueue ( id ) ) {
return ;
}
2021-04-03 13:49:33 +02:00
2021-09-06 16:54:59 +02:00
const int index = m_queuemap . indexOf ( id ) ;
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Queuemap is now: " < < m_queuemap ;
2021-09-06 16:54:59 +02:00
qCDebug ( kastsDataManager ) < < " Queue index of item to be removed " < < index ;
2021-07-03 22:44:48 +02:00
2021-09-06 16:54:59 +02:00
// Move to next track if it's currently playing
2021-07-03 22:44:48 +02:00
if ( AudioManager : : instance ( ) . entry ( ) = = getEntry ( id ) ) {
2021-09-06 16:54:59 +02:00
AudioManager : : instance ( ) . next ( ) ;
2021-07-03 22:44:48 +02:00
}
// Remove the item from the internal data structure
2021-04-03 19:29:40 +02:00
m_queuemap . removeAt ( index ) ;
// Then make sure that the database Queue table reflects these changes
QSqlQuery query ;
2021-09-06 16:54:59 +02:00
query . prepare ( QStringLiteral ( " DELETE FROM Queue WHERE id=:id; " ) ) ;
query . bindValue ( QStringLiteral ( " :id " ) , id ) ;
2021-04-03 19:29:40 +02:00
Database : : instance ( ) . execute ( query ) ;
// Make sure that the QueueModel is aware of the change so it can update
Q_EMIT queueEntryRemoved ( index , id ) ;
}
2021-04-17 20:55:01 +02:00
QString DataManager : : lastPlayingEntry ( )
{
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT id FROM Queue WHERE playing=:playing; " ) ) ;
query . bindValue ( QStringLiteral ( " :playing " ) , true ) ;
Database : : instance ( ) . execute ( query ) ;
2021-05-01 21:35:37 +02:00
if ( ! query . next ( ) )
return QStringLiteral ( " none " ) ;
2021-04-17 20:55:01 +02:00
return query . value ( QStringLiteral ( " id " ) ) . toString ( ) ;
}
2021-05-01 21:35:37 +02:00
void DataManager : : setLastPlayingEntry ( const QString & id )
2021-04-17 20:55:01 +02:00
{
QSqlQuery query ;
// First set playing to false for all Queue items
query . prepare ( QStringLiteral ( " UPDATE Queue SET playing=:playing; " ) ) ;
query . bindValue ( QStringLiteral ( " :playing " ) , false ) ;
Database : : instance ( ) . execute ( query ) ;
// Now set the correct track to playing=true
query . prepare ( QStringLiteral ( " UPDATE Queue SET playing=:playing WHERE id=:id; " ) ) ;
query . bindValue ( QStringLiteral ( " :playing " ) , true ) ;
query . bindValue ( QStringLiteral ( " :id " ) , id ) ;
Database : : instance ( ) . execute ( query ) ;
}
2021-07-03 22:17:56 +02:00
void DataManager : : deletePlayedEnclosures ( )
{
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT * FROM Enclosures INNER JOIN Entries ON Enclosures.id = Entries.id WHERE downloaded=:downloaded AND read=:read; " ) ) ;
query . bindValue ( QStringLiteral ( " :downloaded " ) , Enclosure : : statusToDb ( Enclosure : : Downloaded ) ) ;
query . bindValue ( QStringLiteral ( " :read " ) , true ) ;
Database : : instance ( ) . execute ( query ) ;
while ( query . next ( ) ) {
QString feed = query . value ( QStringLiteral ( " feed " ) ) . toString ( ) ;
QString id = query . value ( QStringLiteral ( " id " ) ) . toString ( ) ;
qCDebug ( kastsDataManager ) < < " Found entry which has been downloaded and is marked as played; deleting now: " < < id ;
Entry * entry = getEntry ( id ) ;
if ( entry - > hasEnclosure ( ) ) {
entry - > enclosure ( ) - > deleteFile ( ) ;
}
}
}
2021-04-02 22:31:34 +02:00
void DataManager : : importFeeds ( const QString & path )
{
QUrl url ( path ) ;
QFile file ( url . isLocalFile ( ) ? url . toLocalFile ( ) : url . toString ( ) ) ;
file . open ( QIODevice : : ReadOnly ) ;
2021-04-20 21:14:19 +02:00
QStringList urls ;
2021-04-02 22:31:34 +02:00
QXmlStreamReader xmlReader ( & file ) ;
2021-05-01 21:35:37 +02:00
while ( ! xmlReader . atEnd ( ) ) {
2021-04-02 22:31:34 +02:00
xmlReader . readNext ( ) ;
2021-05-01 21:35:37 +02:00
if ( xmlReader . tokenType ( ) = = 4 & & xmlReader . attributes ( ) . hasAttribute ( QStringLiteral ( " xmlUrl " ) ) ) {
2021-04-20 21:14:19 +02:00
urls + = xmlReader . attributes ( ) . value ( QStringLiteral ( " xmlUrl " ) ) . toString ( ) ;
2021-04-02 22:31:34 +02:00
}
}
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Start importing urls: " < < urls ;
2021-04-20 21:14:19 +02:00
addFeeds ( urls ) ;
2021-04-02 22:31:34 +02:00
}
void DataManager : : exportFeeds ( const QString & path )
{
QUrl url ( path ) ;
QFile file ( url . isLocalFile ( ) ? url . toLocalFile ( ) : url . toString ( ) ) ;
file . open ( QIODevice : : WriteOnly ) ;
QXmlStreamWriter xmlWriter ( & file ) ;
xmlWriter . setAutoFormatting ( true ) ;
xmlWriter . writeStartDocument ( QStringLiteral ( " 1.0 " ) ) ;
xmlWriter . writeStartElement ( QStringLiteral ( " opml " ) ) ;
xmlWriter . writeEmptyElement ( QStringLiteral ( " head " ) ) ;
xmlWriter . writeStartElement ( QStringLiteral ( " body " ) ) ;
xmlWriter . writeAttribute ( QStringLiteral ( " version " ) , QStringLiteral ( " 1.0 " ) ) ;
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT url, name FROM Feeds; " ) ) ;
Database : : instance ( ) . execute ( query ) ;
2021-05-01 21:35:37 +02:00
while ( query . next ( ) ) {
2021-04-02 22:31:34 +02:00
xmlWriter . writeEmptyElement ( QStringLiteral ( " outline " ) ) ;
xmlWriter . writeAttribute ( QStringLiteral ( " xmlUrl " ) , query . value ( 0 ) . toString ( ) ) ;
xmlWriter . writeAttribute ( QStringLiteral ( " title " ) , query . value ( 1 ) . toString ( ) ) ;
}
xmlWriter . writeEndElement ( ) ;
xmlWriter . writeEndElement ( ) ;
xmlWriter . writeEndDocument ( ) ;
}
2021-10-29 17:00:52 +02:00
void DataManager : : loadFeed ( const QString & feedurl ) const
2021-04-02 17:06:27 +02:00
{
2021-10-01 15:46:17 +02:00
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT url FROM Feeds WHERE url=:feedurl; " ) ) ;
query . bindValue ( QStringLiteral ( " :feedurl " ) , feedurl ) ;
Database : : instance ( ) . execute ( query ) ;
if ( ! query . next ( ) ) {
qWarning ( ) < < " Failed to load feed " < < feedurl ;
} else {
m_feeds [ feedurl ] = new Feed ( feedurl ) ;
}
2021-04-02 17:06:27 +02:00
}
2021-04-02 22:31:34 +02:00
void DataManager : : loadEntry ( const QString id ) const
{
// First find the feed that this entry belongs to
2021-05-01 21:35:37 +02:00
Feed * feed = nullptr ;
2021-04-02 22:31:34 +02:00
QHashIterator < QString , QStringList > i ( m_entrymap ) ;
while ( i . hasNext ( ) ) {
i . next ( ) ;
if ( i . value ( ) . contains ( id ) )
feed = getFeed ( i . key ( ) ) ;
}
2021-05-01 20:59:08 +02:00
if ( ! feed ) {
2021-06-05 20:12:42 +02:00
qCDebug ( kastsDataManager ) < < " Failed to find feed belonging to entry " < < id ;
2021-04-02 22:31:34 +02:00
return ;
}
m_entries [ id ] = new Entry ( feed , id ) ;
}
bool DataManager : : feedExists ( const QString & url )
{
2021-10-29 17:00:52 +02:00
// Try to account for some common cases where the URL is different but is
// actually pointing to the same data. Currently covering:
// - http vs https
// - encoded vs non-encoded URLs
QString cleanUrl = QUrl ( url ) . authority ( ) + QUrl ( url ) . path ( QUrl : : FullyDecoded ) ;
for ( QString listUrl : m_feedmap ) {
if ( cleanUrl = = ( QUrl ( listUrl ) . authority ( ) + QUrl ( listUrl ) . path ( QUrl : : FullyDecoded ) ) ) {
return true ;
}
}
return false ;
2021-04-02 22:31:34 +02:00
}
2021-04-03 19:29:40 +02:00
void DataManager : : updateQueueListnrs ( ) const
{
QSqlQuery query ;
2021-09-06 16:54:59 +02:00
query . prepare ( QStringLiteral ( " UPDATE Queue SET listnr=:i WHERE id=:id; " ) ) ;
2021-05-01 21:35:37 +02:00
for ( int i = 0 ; i < m_queuemap . count ( ) ; i + + ) {
2021-04-03 19:29:40 +02:00
query . bindValue ( QStringLiteral ( " :i " ) , i ) ;
query . bindValue ( QStringLiteral ( " :id " ) , m_queuemap [ i ] ) ;
Database : : instance ( ) . execute ( query ) ;
}
2021-06-23 21:11:04 +02:00
}
2021-07-02 00:19:03 +02:00
2021-09-06 16:54:59 +02:00
void DataManager : : bulkMarkReadByIndex ( bool state , QModelIndexList list )
{
bulkMarkRead ( state , getIdsFromModelIndexList ( list ) ) ;
}
void DataManager : : bulkMarkRead ( bool state , QStringList list )
{
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . transaction ( ) ;
2021-09-06 16:54:59 +02:00
if ( state ) { // Mark as read
// This needs special attention as the DB operations are very intensive.
// Reversing the loop is much faster
for ( int i = list . count ( ) - 1 ; i > = 0 ; i - - ) {
getEntry ( list [ i ] ) - > setReadInternal ( state ) ;
}
updateQueueListnrs ( ) ; // update queue after modification
} else { // Mark as unread
for ( QString id : list ) {
getEntry ( id ) - > setReadInternal ( state ) ;
}
}
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . commit ( ) ;
2021-09-06 16:54:59 +02:00
Q_EMIT bulkReadStatusActionFinished ( ) ;
2021-10-29 17:00:52 +02:00
// if settings allow, upload these changes immediately to sync servers
if ( state ) {
Sync : : instance ( ) . doQuickSync ( ) ;
}
2021-09-06 16:54:59 +02:00
}
void DataManager : : bulkMarkNewByIndex ( bool state , QModelIndexList list )
{
bulkMarkNew ( state , getIdsFromModelIndexList ( list ) ) ;
}
void DataManager : : bulkMarkNew ( bool state , QStringList list )
{
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . transaction ( ) ;
2021-09-06 16:54:59 +02:00
for ( QString id : list ) {
getEntry ( id ) - > setNewInternal ( state ) ;
}
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . commit ( ) ;
2021-09-06 16:54:59 +02:00
Q_EMIT bulkNewStatusActionFinished ( ) ;
}
void DataManager : : bulkQueueStatusByIndex ( bool state , QModelIndexList list )
{
bulkQueueStatus ( state , getIdsFromModelIndexList ( list ) ) ;
}
void DataManager : : bulkQueueStatus ( bool state , QStringList list )
{
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . transaction ( ) ;
2021-09-06 16:54:59 +02:00
if ( state ) { // i.e. add to queue
for ( QString id : list ) {
getEntry ( id ) - > setQueueStatusInternal ( state ) ;
}
} else { // i.e. remove from queue
// This needs special attention as the DB operations are very intensive.
// Reversing the loop is much faster.
for ( int i = list . count ( ) - 1 ; i > = 0 ; i - - ) {
qCDebug ( kastsDataManager ) < < " getting entry " < < getEntry ( list [ i ] ) - > id ( ) ;
getEntry ( list [ i ] ) - > setQueueStatusInternal ( state ) ;
}
updateQueueListnrs ( ) ;
}
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . commit ( ) ;
2021-09-06 16:54:59 +02:00
Q_EMIT bulkReadStatusActionFinished ( ) ;
Q_EMIT bulkNewStatusActionFinished ( ) ;
}
void DataManager : : bulkDownloadEnclosuresByIndex ( QModelIndexList list )
{
bulkDownloadEnclosures ( getIdsFromModelIndexList ( list ) ) ;
}
void DataManager : : bulkDownloadEnclosures ( QStringList list )
{
bulkQueueStatus ( true , list ) ;
for ( QString id : list ) {
2021-09-23 13:17:06 +02:00
if ( getEntry ( id ) - > hasEnclosure ( ) ) {
getEntry ( id ) - > enclosure ( ) - > download ( ) ;
}
2021-09-06 16:54:59 +02:00
}
}
void DataManager : : bulkDeleteEnclosuresByIndex ( QModelIndexList list )
{
bulkDeleteEnclosures ( getIdsFromModelIndexList ( list ) ) ;
}
void DataManager : : bulkDeleteEnclosures ( QStringList list )
{
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . transaction ( ) ;
2021-09-06 16:54:59 +02:00
for ( QString id : list ) {
2021-09-23 13:17:06 +02:00
if ( getEntry ( id ) - > hasEnclosure ( ) ) {
getEntry ( id ) - > enclosure ( ) - > deleteFile ( ) ;
}
2021-09-06 16:54:59 +02:00
}
2021-09-23 19:23:39 +02:00
Database : : instance ( ) . commit ( ) ;
2021-09-06 16:54:59 +02:00
}
QStringList DataManager : : getIdsFromModelIndexList ( const QModelIndexList & list ) const
{
QStringList ids ;
for ( QModelIndex index : list ) {
ids + = index . data ( EpisodeModel : : Roles : : IdRole ) . value < QString > ( ) ;
}
qCDebug ( kastsDataManager ) < < " Ids of selection: " < < ids ;
return ids ;
}