2020-02-28 23:25:08 +01:00
/**
* Copyright 2020 Tobias Fella < fella @ posteo . de >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License or ( at your option ) version 3 or any later version
* accepted by the membership of KDE e . V . ( or its successor approved
* by the membership of KDE e . V . ) , which shall act as a proxy
* defined in Section 14 of version 3 of the license .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
*/
2020-06-06 00:05:32 +02:00
# include <QDateTime>
2020-04-26 23:40:09 +02:00
# include <QFile>
# include <QFileInfo>
2020-02-28 23:25:08 +01:00
# include <QNetworkAccessManager>
# include <QNetworkReply>
2020-04-25 22:16:19 +02:00
# include <QStandardPaths>
2020-05-02 12:26:00 +02:00
# include <QTextDocumentFragment>
2020-02-28 23:25:08 +01:00
2020-02-29 15:34:12 +01:00
# include <Syndication/Syndication>
2020-02-28 23:25:08 +01:00
2020-03-16 22:37:04 +01:00
# include "database.h"
2020-04-22 02:17:57 +02:00
# include "fetcher.h"
2020-03-16 22:37:04 +01:00
2020-04-22 02:17:57 +02:00
Fetcher : : Fetcher ( )
{
2020-04-25 22:16:19 +02:00
manager = new QNetworkAccessManager ( this ) ;
manager - > setRedirectPolicy ( QNetworkRequest : : NoLessSafeRedirectPolicy ) ;
manager - > setStrictTransportSecurityEnabled ( true ) ;
manager - > enableStrictTransportSecurityStore ( true ) ;
2020-02-28 23:25:08 +01:00
}
2020-05-26 16:32:07 +02:00
void Fetcher : : fetch ( QString url )
2020-02-28 23:25:08 +01:00
{
2020-05-26 16:32:07 +02:00
qDebug ( ) < < " Starting to fetch " < < url ;
2020-03-31 23:52:03 +02:00
2020-05-31 18:17:25 +02:00
Q_EMIT startedFetchingFeed ( url ) ;
2020-05-26 16:32:07 +02:00
QNetworkRequest request ( ( QUrl ( url ) ) ) ;
2020-07-02 19:14:07 +02:00
QNetworkReply * reply = get ( request ) ;
2020-02-28 23:25:08 +01:00
connect ( reply , & QNetworkReply : : finished , this , [ this , url , reply ] ( ) {
QByteArray data = reply - > readAll ( ) ;
2020-05-26 16:32:07 +02:00
Syndication : : DocumentSource * document = new Syndication : : DocumentSource ( data , url ) ;
2020-02-28 23:25:08 +01:00
Syndication : : FeedPtr feed = Syndication : : parserCollection ( ) - > parse ( * document , QStringLiteral ( " Atom " ) ) ;
2020-05-18 16:47:12 +02:00
processFeed ( feed , url ) ;
2020-04-18 21:07:49 +02:00
2020-02-29 00:51:26 +01:00
delete reply ;
2020-02-28 23:25:08 +01:00
} ) ;
}
2020-04-25 22:16:19 +02:00
2020-05-31 18:17:25 +02:00
void Fetcher : : fetchAll ( )
{
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT url FROM Feeds; " ) ) ;
Database : : instance ( ) . execute ( query ) ;
2020-06-06 00:05:32 +02:00
while ( query . next ( ) ) {
2020-05-31 18:17:25 +02:00
fetch ( query . value ( 0 ) . toString ( ) ) ;
}
}
2020-05-26 16:32:07 +02:00
void Fetcher : : processFeed ( Syndication : : FeedPtr feed , QString url )
2020-05-18 16:47:12 +02:00
{
if ( feed . isNull ( ) )
return ;
QSqlQuery query ;
2020-06-06 00:05:32 +02:00
query . prepare ( QStringLiteral ( " UPDATE Feeds SET name=:name, image=:image, link=:link, description=:description, lastUpdated=:lastUpdated WHERE url=:url; " ) ) ;
2020-05-18 16:47:12 +02:00
query . bindValue ( QStringLiteral ( " :name " ) , feed - > title ( ) ) ;
query . bindValue ( QStringLiteral ( " :url " ) , url ) ;
2020-05-30 17:33:08 +02:00
query . bindValue ( QStringLiteral ( " :link " ) , feed - > link ( ) ) ;
query . bindValue ( QStringLiteral ( " :description " ) , feed - > description ( ) ) ;
2020-06-06 00:05:32 +02:00
QDateTime current = QDateTime : : currentDateTime ( ) ;
query . bindValue ( QStringLiteral ( " :lastUpdated " ) , current . toSecsSinceEpoch ( ) ) ;
for ( auto & author : feed - > authors ( ) ) {
2020-05-30 17:33:08 +02:00
processAuthor ( author , QLatin1String ( " " ) , url ) ;
}
2020-05-26 16:32:07 +02:00
QString image ;
if ( feed - > image ( ) - > url ( ) . startsWith ( QStringLiteral ( " / " ) ) )
image = QUrl ( url ) . adjusted ( QUrl : : RemovePath ) . toString ( ) + feed - > image ( ) - > url ( ) ;
else
image = feed - > image ( ) - > url ( ) ;
query . bindValue ( QStringLiteral ( " :image " ) , image ) ;
2020-05-18 16:47:12 +02:00
Database : : instance ( ) . execute ( query ) ;
2020-05-30 17:33:08 +02:00
2020-05-18 16:47:12 +02:00
qDebug ( ) < < " Updated feed title: " < < feed - > title ( ) ;
2020-06-06 00:05:32 +02:00
Q_EMIT feedDetailsUpdated ( url , feed - > title ( ) , image , feed - > link ( ) , feed - > description ( ) , current ) ;
2020-05-26 16:32:07 +02:00
2020-05-18 16:47:12 +02:00
for ( const auto & entry : feed - > items ( ) ) {
processEntry ( entry , url ) ;
}
2020-05-26 16:32:07 +02:00
Q_EMIT feedUpdated ( url ) ;
2020-05-18 16:47:12 +02:00
}
2020-05-26 16:32:07 +02:00
void Fetcher : : processEntry ( Syndication : : ItemPtr entry , QString url )
2020-05-18 16:47:12 +02:00
{
qDebug ( ) < < " Processing " < < entry - > title ( ) ;
QSqlQuery query ;
query . prepare ( QStringLiteral ( " SELECT COUNT (id) FROM Entries WHERE id=:id; " ) ) ;
query . bindValue ( QStringLiteral ( " :id " ) , entry - > id ( ) ) ;
Database : : instance ( ) . execute ( query ) ;
query . next ( ) ;
if ( query . value ( 0 ) . toInt ( ) ! = 0 )
return ;
2020-06-06 00:05:32 +02:00
query . prepare ( QStringLiteral ( " INSERT INTO Entries VALUES (:feed, :id, :title, :content, :created, :updated, :link, false); " ) ) ;
2020-05-18 16:47:12 +02:00
query . bindValue ( QStringLiteral ( " :feed " ) , url ) ;
query . bindValue ( QStringLiteral ( " :id " ) , entry - > id ( ) ) ;
query . bindValue ( QStringLiteral ( " :title " ) , QTextDocumentFragment : : fromHtml ( entry - > title ( ) ) . toPlainText ( ) ) ;
query . bindValue ( QStringLiteral ( " :created " ) , static_cast < int > ( entry - > datePublished ( ) ) ) ;
query . bindValue ( QStringLiteral ( " :updated " ) , static_cast < int > ( entry - > dateUpdated ( ) ) ) ;
query . bindValue ( QStringLiteral ( " :link " ) , entry - > link ( ) ) ;
if ( ! entry - > content ( ) . isEmpty ( ) )
query . bindValue ( QStringLiteral ( " :content " ) , entry - > content ( ) ) ;
else
query . bindValue ( QStringLiteral ( " :content " ) , entry - > description ( ) ) ;
Database : : instance ( ) . execute ( query ) ;
for ( const auto & author : entry - > authors ( ) ) {
2020-05-30 17:33:08 +02:00
processAuthor ( author , entry - > id ( ) , url ) ;
2020-05-18 16:47:12 +02:00
}
2020-05-18 17:02:46 +02:00
for ( const auto & enclosure : entry - > enclosures ( ) ) {
processEnclosure ( enclosure , entry , url ) ;
}
2020-05-18 16:47:12 +02:00
}
2020-05-30 17:33:08 +02:00
void Fetcher : : processAuthor ( Syndication : : PersonPtr author , QString entryId , QString url )
2020-05-18 16:47:12 +02:00
{
QSqlQuery query ;
query . prepare ( QStringLiteral ( " INSERT INTO Authors VALUES(:feed, :id, :name, :uri, :email); " ) ) ;
2020-05-26 16:32:07 +02:00
query . bindValue ( QStringLiteral ( " :feed " ) , url ) ;
2020-05-30 17:33:08 +02:00
query . bindValue ( QStringLiteral ( " :id " ) , entryId ) ;
2020-05-18 16:47:12 +02:00
query . bindValue ( QStringLiteral ( " :name " ) , author - > name ( ) ) ;
query . bindValue ( QStringLiteral ( " :uri " ) , author - > uri ( ) ) ;
query . bindValue ( QStringLiteral ( " :email " ) , author - > email ( ) ) ;
Database : : instance ( ) . execute ( query ) ;
}
2020-05-26 16:32:07 +02:00
void Fetcher : : processEnclosure ( Syndication : : EnclosurePtr enclosure , Syndication : : ItemPtr entry , QString feedUrl )
2020-05-18 17:02:46 +02:00
{
QSqlQuery query ;
query . prepare ( QStringLiteral ( " INSERT INTO Enclosures VALUES (:feed, :id, :duration, :size, :title, :type, :url); " ) ) ;
2020-05-26 16:32:07 +02:00
query . bindValue ( QStringLiteral ( " :feed " ) , feedUrl ) ;
2020-05-18 17:02:46 +02:00
query . bindValue ( QStringLiteral ( " :id " ) , entry - > id ( ) ) ;
query . bindValue ( QStringLiteral ( " :duration " ) , enclosure - > duration ( ) ) ;
query . bindValue ( QStringLiteral ( " :size " ) , enclosure - > length ( ) ) ;
query . bindValue ( QStringLiteral ( " :title " ) , enclosure - > title ( ) ) ;
query . bindValue ( QStringLiteral ( " :type " ) , enclosure - > type ( ) ) ;
query . bindValue ( QStringLiteral ( " :url " ) , enclosure - > url ( ) ) ;
Database : : instance ( ) . execute ( query ) ;
}
2020-04-25 22:16:19 +02:00
QString Fetcher : : image ( QString url )
{
2020-05-18 21:00:05 +02:00
QString path = filePath ( url ) ;
2020-05-10 23:25:23 +02:00
if ( QFileInfo : : exists ( path ) ) {
2020-04-25 22:16:19 +02:00
return path ;
}
2020-05-18 21:20:23 +02:00
download ( url ) ;
return QLatin1String ( " " ) ;
}
void Fetcher : : download ( QString url )
{
2020-04-25 22:16:19 +02:00
QNetworkRequest request ( ( QUrl ( url ) ) ) ;
2020-07-02 19:14:07 +02:00
QNetworkReply * reply = get ( request ) ;
2020-05-18 21:20:23 +02:00
connect ( reply , & QNetworkReply : : finished , this , [ this , url , reply ] ( ) {
2020-04-25 22:16:19 +02:00
QByteArray data = reply - > readAll ( ) ;
2020-05-18 21:20:23 +02:00
QFile file ( filePath ( url ) ) ;
2020-04-25 22:16:19 +02:00
file . open ( QIODevice : : WriteOnly ) ;
file . write ( data ) ;
file . close ( ) ;
delete reply ;
} ) ;
}
2020-04-26 23:40:09 +02:00
void Fetcher : : removeImage ( QString url )
{
2020-05-18 21:00:05 +02:00
qDebug ( ) < < filePath ( url ) ;
QFile ( filePath ( url ) ) . remove ( ) ;
2020-04-26 23:40:09 +02:00
}
2020-05-18 21:00:05 +02:00
QString Fetcher : : filePath ( QString url )
2020-04-26 23:40:09 +02:00
{
return QStandardPaths : : writableLocation ( QStandardPaths : : AppDataLocation ) + QStringLiteral ( " / " ) + QString : : fromStdString ( QCryptographicHash : : hash ( url . toUtf8 ( ) , QCryptographicHash : : Md5 ) . toHex ( ) . toStdString ( ) ) ;
}
2020-07-02 19:14:07 +02:00
QNetworkReply * Fetcher : : get ( QNetworkRequest & request )
{
request . setRawHeader ( " User-Agent " , " Alligator/0.1; Syndication " ) ;
return manager - > get ( request ) ;
}