diff --git a/saori.pro b/saori.pro index a6bcbec..b0f39d0 100644 --- a/saori.pro +++ b/saori.pro @@ -29,7 +29,8 @@ saoriview.cpp \ saoriaddaccountdialog.cpp \ saoriaccount.cpp \ - saoriapplication.cpp + saoriapplication.cpp \ + saoricache.cpp HEADERS += \ saoriwindow.h \ @@ -38,7 +39,8 @@ saoriaddaccountdialog.h \ saoridef.h \ saoriaccount.h \ - saoriapplication.h + saoriapplication.h \ + saoricache.h FORMS += \ saoriwindow.ui \ diff --git a/saoriapplication.cpp b/saoriapplication.cpp index a35b8a3..b101ac7 100644 --- a/saoriapplication.cpp +++ b/saoriapplication.cpp @@ -59,6 +59,8 @@ m_db = QSqlDatabase::addDatabase("QSQLITE"); m_db.setDatabaseName(m_dataDirectory.absolutePath() + "/" + SAORI_SQLFILE); m_db.open(); + + m_cache = new SaoriCache(this); } SaoriApplication::~SaoriApplication() @@ -109,6 +111,16 @@ return m_dataDirectory; } +QSqlDatabase *SaoriApplication::database() +{ + return &m_db; +} + +SaoriCache *SaoriApplication::cache() +{ + return m_cache; +} + void SaoriApplication::loadSettings() { m_config->beginGroup("instances"); diff --git a/saoriapplication.h b/saoriapplication.h index 7c6ae94..3178849 100644 --- a/saoriapplication.h +++ b/saoriapplication.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,8 @@ static SaoriApplication * saori(); QDir cacheDirectory(); QDir dataDirectory(); + QSqlDatabase * database(); + SaoriCache * cache(); protected: QSettings *m_config; @@ -57,6 +60,7 @@ QDir m_cacheDirectory; QDir m_dataDirectory; QSqlDatabase m_db; + SaoriCache *m_cache; protected: void loadSettings(); diff --git a/saoricache.cpp b/saoricache.cpp new file mode 100644 index 0000000..03cdc40 --- /dev/null +++ b/saoricache.cpp @@ -0,0 +1,149 @@ +/*** + +The MIT License + +Copyright (c) 2018 Teppei Tamra (TAM) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +***/ + +#include "saoricache.h" +#include "saoriapplication.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * table file_cache + * url(text unique) + * filename(text) + * size(integer) + * timestamp(integer) + * + * table status_cache + * + */ + +QList SaoriCache::m_nowloading; + +SaoriCache::SaoriCache(QObject *parent) : QObject(parent) +{ + migration(); +} + +void SaoriCache::removeFileCache(const QUrl url) +{ + QSqlQuery query(*SaoriApplication::saori()->database()); + query.prepare("DELETE from file_cache WHERE url=?;"); + query.addBindValue(url.toString()); + query.exec(); + QFile localfile(urlToFilename(url)); + if (localfile.exists()) localfile.remove(); +} + +void SaoriCache::reloadFileCache(const QUrl url) +{ + removeFileCache(url); + download(url); +} + +const QString SaoriCache::fileCache(const QUrl url) +{ + download(url); + if (isCached(url)) return urlToFilename(url); + else download(url); + return QString(":/icons/ionicons/load-a.svg"); +} + +bool SaoriCache::migration() +{ + if (SaoriApplication::saori()->database()->tables().contains("file_cache")) return true; + QSqlQuery query(*SaoriApplication::saori()->database()); + return query.exec("CREATE TABLE file_cache(url TEXT UNIQUE,filename TEXT,size INTEGER,timestamp INTEGER);"); +} + +void SaoriCache::download(const QUrl url) +{ + if (m_nowloading.contains(url)) return; + if (isCached(url)) return; + m_nowloading.append(url); + QNetworkRequest request; + request.setUrl(url); + auto manager = new QNetworkAccessManager; + auto *reply = manager->get(request); + connect(manager,&QNetworkAccessManager::finished,this,[=](){ + if (reply->error() != QNetworkReply::NoError) { + QFile localfile(urlToFilename(url)); + if (localfile.exists()) localfile.remove(); + localfile.open(QIODevice::WriteOnly); + localfile.write(reply->readAll()); + // TODO 100MBのファイルに100MBのメモリを使うのは如何なものか。 + qint64 size = localfile.size(); + localfile.close(); + QSqlQuery query(*SaoriApplication::saori()->database()); + query.prepare("INSERT INTO file_cache(url,filename,size,timestamp) values(?,?,?,?);"); + query.addBindValue(url.toString()); + query.addBindValue(urlToFilename(url)); + query.addBindValue(size); + query.addBindValue(QDateTime::currentSecsSinceEpoch()); + query.exec(); + emit downloaded(url); + } else { + QSqlQuery query(*SaoriApplication::saori()->database()); + query.prepare("INSERT INTO file_cache(url,filename,size,timestamp) values(?,?,?,?);"); + query.addBindValue(url.toString()); + query.addBindValue(":/icons/ionicons/alert-circled.svg"); + query.addBindValue("-1"); + query.addBindValue(QDateTime::currentSecsSinceEpoch()); + query.exec(); + emit downloaded(url); + } + m_nowloading.removeAll(url); + manager->deleteLater(); + }); +} + +bool SaoriCache::isCached(const QUrl url) +{ + QSqlQuery query(*SaoriApplication::saori()->database()); + query.prepare("SELECT size from file_cache WHERE url=?;"); + query.addBindValue(url.toString()); + query.exec(); + if (query.next()) { + if (query.value(0).toInt() >= 0) return true; + } + return false; +} + +const QString SaoriCache::urlToFilename(QUrl url) +{ + QString dir = QString::fromUtf8(QCryptographicHash::hash(url.adjusted(QUrl::RemovePath).toString().toLatin1(),QCryptographicHash::Md5).toHex()); + QString file = QString::fromUtf8(QCryptographicHash::hash(url.path().toLatin1(),QCryptographicHash::Md5).toHex()); + QDir d; + d.mkpath(SaoriApplication::saori()->cacheDirectory().absolutePath() + "/" + dir); + return QString(SaoriApplication::saori()->cacheDirectory().absolutePath() + "/" + dir + "/" + file); +} diff --git a/saoricache.h b/saoricache.h new file mode 100644 index 0000000..834e139 --- /dev/null +++ b/saoricache.h @@ -0,0 +1,59 @@ +/*** + +The MIT License + +Copyright (c) 2018 Teppei Tamra (TAM) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +***/ + +#ifndef SAORICACHE_H +#define SAORICACHE_H + +#include +#include +#include + +class SaoriCache : public QObject +{ + Q_OBJECT +public: + explicit SaoriCache(QObject *parent = nullptr); + void removeFileCache(const QUrl url); + void reloadFileCache(const QUrl url); + const QString fileCache(const QUrl url); + +protected: + static QList m_nowloading; + +protected: + bool migration(); + void download(const QUrl url); + bool isCached(const QUrl url); + static const QString urlToFilename(QUrl url); + const QString localfileName(const QUrl url); + +signals: + void downloaded(const QUrl); + +public slots: +}; + +#endif // SAORICACHE_H