diff --git a/src/libdanbooru/danbooruservice.cpp b/src/libdanbooru/danbooruservice.cpp deleted file mode 100644 index eb4bb7e..0000000 --- a/src/libdanbooru/danbooruservice.cpp +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright 2015 Luca Beltrame - * - * This file is part of Danbooru Client. - * - * Danbooru Client 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 3 of the License, or - * (at your option) any later version. - * - * Danbooru Client 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 Danbooru Client. If not, see . - */ - -// STL - -#include - -// Qt - -#include "libdanbooru_debug.h" - -// KDE - -#include -#include -#include -#include - -// Own - -#include "danbooruservice.h" -#include "danboorupost.h" -#include "danboorupool.h" -#include "danboorutag.h" -#include "utils.h" - -namespace Danbooru -{ - -using KIO::StoredTransferJob; -using KIO::MultiGetJob; - -const QString DanbooruService::POST_URL = QStringLiteral("post/index.json"); -const QString DanbooruService::TAG_URL = QStringLiteral("tag/index.xml"); -const QString DanbooruService::POOL_URL = QStringLiteral("pool/index.json"); -const QString DanbooruService::ARTIST_URL = QStringLiteral("artist/index.json"); -const QString DanbooruService::POOL_DATA_URL = QStringLiteral("pool/show.xml"); -const QString DanbooruService::RELATED_TAG_URL = QStringLiteral("tag/related.json"); - -DanbooruService::DanbooruService(QUrl boardUrl, QString username, - QString password, KImageCache *cache, - QObject *parent): - QObject(parent), - m_url(boardUrl), - m_username(username), - m_password(password), - m_maxRating(Danbooru::DanbooruPost::Safe), - m_maxPosts(10), - m_currentPage(1), - m_tags(QStringList()), - m_postsToFetch(0), - m_cache(cache) -{ -} - -DanbooruService::~DanbooruService() -{ - -} - -void DanbooruService::getPostList() -{ - - // We can't fetch more than 100 items, API limitation - - QMap parameters; - - parameters.insert(QStringLiteral("limit"), QString::number(m_maxPosts)); - parameters.insert(QStringLiteral("page"), QString::number(m_currentPage)); - - QUrl danbooruUrl = requestUrl(m_url, POST_URL, m_username, - m_password, parameters, m_tags); - - // qCDebug(LIBDANBOORU) << "Final constructed post URL" << danbooruUrl; - - KIO::StoredTransferJob *job = KIO::storedGet(danbooruUrl, KIO::NoReload, - KIO::HideProgressInfo); - - // This job can use JSON data - job->setProperty("needsXML", false); - - connect(job, &KIO::StoredTransferJob::result, this, &DanbooruService::processPostList); - -} - -void DanbooruService::getTagList(int limit, QString name) -{ - QMap parameters; - parameters.insert(QStringLiteral("limit"), QString::number(limit)); - - if (!name.isEmpty()) { - parameters.insert(QStringLiteral("name"), name); - } - parameters.insert(QStringLiteral("order"), QStringLiteral("date")); - - QUrl danbooruUrl = requestUrl(m_url, TAG_URL, m_username, m_password, - parameters); -// qCDebug(LIBDANBOORU) << "Final constructed tag URL" << danbooruUrl.url(); - - KIO::StoredTransferJob *job = KIO::storedGet(danbooruUrl, KIO::NoReload, - KIO::HideProgressInfo); - connect(job, &KIO::StoredTransferJob::result, this, &DanbooruService::processTagList); - -} - -void DanbooruService::getPool(int poolId, int page) -{ - - QMap parameters; - - parameters.insert(QStringLiteral("id"), QString::number(poolId)); - - if (page > 1) { - parameters.insert(QStringLiteral("page"), QString::number(page)); - } - - QUrl danbooruUrl = requestUrl(m_url, POOL_DATA_URL, m_username, - m_password, parameters); - -// qCDebug(LIBDANBOORU) << "Final constructed pool URL" << danbooruUrl.url(); - - KIO::StoredTransferJob *job = KIO::storedGet(danbooruUrl, KIO::NoReload, - KIO::HideProgressInfo); - - //HACK: Most Danbooru implementations don't provide valid data on - // pools via JSON, hence we set XML and parse the XML data. - - job->setProperty("needsXML", true); - - connect(job, &KIO::StoredTransferJob::result, this, &DanbooruService::processPostList); - -} - -void DanbooruService::getPoolList() -{ - - QUrl danbooruUrl; - - if (m_currentPage == 1) { - danbooruUrl = requestUrl(m_url, POOL_URL, m_username, m_password); - } else { - QMap map; - map.insert(QStringLiteral("page"), QString::number(m_currentPage)); - - danbooruUrl = requestUrl(m_url, POOL_URL, m_username, - m_password, map); - } - -// qCDebug(LIBDANBOORU) << "Final constructed pool list URL" << danbooruUrl.url(); - - KIO::StoredTransferJob *job = KIO::storedGet(danbooruUrl, KIO::NoReload, - KIO::HideProgressInfo); - // This job can use JSON data - job->setProperty("needsXML", false); - -// connect(job, &KIO::StoredTransferJob::result, this, &DanbooruService::processPoolList); - - connect(job, &KIO::StoredTransferJob::result, [this](KJob * job) { - - if (job->error()) { - Q_EMIT(downloadError(job->errorString())); - return; - } - - StoredTransferJob *jobResult = qobject_cast(job); - QByteArray data = jobResult->data(); - - bool ok; - QList poolList = parseDanbooruResult(data, &ok).toList(); - - if (!ok) { - Q_EMIT(downloadError(QStringLiteral("Unable to decode data"))); - return; - } - - Q_FOREACH(const auto & element, poolList) { - QVariantMap map = element.toMap(); - DanbooruPool *pool = new DanbooruPool(map); - Q_EMIT(poolDownloaded(pool)); - } - - //qCDebug(LIBDANBOORU) << "Pool download finished!"; - Q_EMIT(poolDownloadFinished()); - } - - ); - -} - -void DanbooruService::getRelatedTags(const QStringList &tags, - DanbooruTag::TagType tagType) -{ - - QString type; - switch (tagType) { - case DanbooruTag::General: - type = QStringLiteral("general"); - break; - case DanbooruTag::Artist: - type = QStringLiteral("artist"); - break; - case DanbooruTag::Copyright: - type = QStringLiteral("copyright"); - break; - case DanbooruTag::Character: - type = QStringLiteral("character"); - break; - case DanbooruTag::Unknown: - type = QStringLiteral("unknown"); - break; - } - - QMap parameters; - parameters.insert(QStringLiteral("type"), type); - - QUrl danbooruUrl = requestUrl(m_url, RELATED_TAG_URL, m_username, - m_password, parameters, tags); - - // qCDebug(LIBDANBOORU) << "Final constructed related tag URL" << danbooruUrl; - - StoredTransferJob *job = KIO::storedGet( - danbooruUrl, KIO::NoReload, - KIO::HideProgressInfo - ); - - connect(job, &StoredTransferJob::result, [this](KJob * job) { - - if (job->error()) { - Q_EMIT(downloadError(job->errorString())); - return; - } - - StoredTransferJob *jobResult = qobject_cast(job); - QByteArray data = jobResult->data(); - bool ok; - - QVariantMap tagList = parseDanbooruResult(data, &ok).toMap(); - - if (!ok) { - Q_EMIT(downloadError(QStringLiteral("Unable to decode data"))); - return; - } - - QVariantMap::const_iterator iter; - - // The service returns a list of key-related tag list pair, - // we iterate through them and remove the empty (not found) ones, then - // we call getTagList. Unfortunately Danbooru doesn't have a method to - // fetch all tags in batch, so this is done one by one. - - for (iter = tagList.constBegin(); iter != tagList.constEnd(); ++iter) { - - QList tags = iter.value().toList(); - - if (tags.isEmpty()) { - continue; - } - - Q_FOREACH(auto &tag, tags) { - // We get the first element in the list, the second is - // the ID which is useless (no API methods in Danbooru) - QString tagName = tag.toList()[0].toString(); - getTagList(1, tagName); - } - - } - - } - - ); - -} - -const QSet< QString > DanbooruService::blacklist() const -{ - return m_blacklist; -} - -const QStringList DanbooruService::allowedRatings() const -{ - QStringList ratings; - - if (m_maxRating.testFlag(DanbooruPost::Safe)) { - ratings.append(QStringLiteral("Safe")); - } - - if (m_maxRating.testFlag(DanbooruPost::Questionable)) { - ratings.append(QStringLiteral("Questionable")); - } - - if (m_maxRating.testFlag(DanbooruPost::Explicit)) { - ratings.append(QStringLiteral("Explicit")); - } - - return ratings; - -} - -int DanbooruService::currentPage() const -{ - return m_currentPage; -} - -const DanbooruPost::Ratings DanbooruService::maximumAllowedRating() const -{ - - return m_maxRating; - -} - -int DanbooruService::maxPosts() const -{ - return m_maxPosts; -} - -int DanbooruService::minimumWidth() const -{ - return m_minimumWidth > 0 ? m_minimumWidth: -1; -} - -int DanbooruService::minimumHeight() const -{ - return m_minimumHeight > 0 ? m_minimumHeight: -1; -} - -void DanbooruService::nextPostPage() -{ - m_currentPage++; - getPostList(); -} - -void DanbooruService::nextPoolPage() -{ - m_currentPage++; - getPoolList(); -} - -QStringList DanbooruService::postTags() const -{ - return m_tags; -} - -void DanbooruService::reset() -{ - m_currentPage = 1; - m_tags = QStringList(); - -} - -// Slots - -void DanbooruService::processTagList(KJob *job) -{ - - if (job->error()) { - Q_EMIT(downloadError(job->errorString())); - return; - } - - StoredTransferJob *jobResult = qobject_cast(job); - QByteArray data = jobResult->data(); - - bool ok; - - // Most Danbooru implementations return tags in wrong order when - // using JSON, so we have to fall back to XML - QList tagList = parseDanbooruResult(data, QStringLiteral("tag"), - &ok); - - if (!ok) { - Q_EMIT(downloadError(QStringLiteral("Unable to decode data"))); - return; - } - - Q_FOREACH(const auto& element, tagList) { - QVariantMap map = element.toMap(); - DanbooruTag *tag = new DanbooruTag(map); - - if (!tag) { - continue; - } - - Q_EMIT(tagDownloaded(tag)); - } -} - -void DanbooruService::processPostList(KJob *job) -{ - -// qCDebug(LIBDANBOORU) << "Got post data OK"; - - if (job->error()) { - Q_EMIT(downloadError(job->errorString())); - } - - StoredTransferJob *jobResult = qobject_cast(job); - - if (jobResult == 0) { - Q_EMIT(downloadError(QStringLiteral("Internal error"))); - return; - - } - - QByteArray data = jobResult->data(); - - bool ok; - bool needsXML = job->property("needsXML").toBool(); - - QList postList; - - if (needsXML) { - // Special cases for pools - postList = parseDanbooruResult(data, QStringLiteral("post"), &ok); - } else { - postList = parseDanbooruResult(data, &ok).toList(); - } - - if (!ok) { - Q_EMIT(downloadError(QStringLiteral("Unable to decode data"))); - return; - } - - // How many posts do we have to fetch? - - if (postList.isEmpty()) { - qCDebug(LIBDANBOORU) << "No posts found"; - Q_EMIT(postDownloadFinished()); - return; - } - - m_postsToFetch = postList.length(); - - Q_FOREACH(const auto& element, postList) { - - QVariantMap map = element.toMap(); - DanbooruPost *post = new DanbooruPost(map); - - // Remove unwanted posts - - if (isPostBlacklisted(post, m_blacklist, m_maxRating)) { - m_postsToFetch--; - delete post; - continue; - } - - if (post->width() < minimumWidth()) { - m_postsToFetch--; - delete post; - continue; - } - - if (post->height() < minimumHeight()) { - m_postsToFetch--; - delete post; - continue; - } - - QPixmap pix; - -// qCDebug(LIBDANBOORU) << "About to donwload images"; - - if (m_cache && m_cache->findPixmap(post->thumbnailUrl().url(), &pix)) { - - post->setPixmap(pix); - Q_EMIT(postDownloaded(post)); - m_postsToFetch--; - - if (m_postsToFetch == 0) { - qCDebug(LIBDANBOORU) << "Post download finished"; - Q_EMIT(postDownloadFinished()); - } - - } else { - - StoredTransferJob *pixmapJob = KIO::storedGet(post->thumbnailUrl(), - KIO::NoReload, KIO::HideProgressInfo); - - // We don't want to overload the servers, so set some rational - // priority - - KIO::Scheduler::setJobPriority(static_cast(job), 1); - - QVariant variant; - variant.setValue(post); - -// pixmapJob->setProperty("danbooruPost", variant); - - connect(pixmapJob, &StoredTransferJob::result, [post, this, pix](KJob * job) mutable { - - if (job->error()) - { - Q_EMIT(downloadError(job->errorString())); - return; - } - - StoredTransferJob *jobResult = qobject_cast(job); - - if (!pix.loadFromData(jobResult->data())) - { - Q_EMIT(downloadError(QStringLiteral("Pixmap data could not be loaded"))); - return; - } - - post->setPixmap(pix); - - if (m_cache) - { - //qCDebug(LIBDANBOORU) << "Inserting item in cache"; - m_cache->insertPixmap(post->thumbnailUrl().url(), pix); - } - - m_postsToFetch--; // One less post to do - - //qCDebug(LIBDANBOORU) << "Current posts remaining" << m_currentPosts; - Q_EMIT(postDownloaded(post)); - - if (m_postsToFetch == 0) - { - qCDebug(LIBDANBOORU) << "Post download finished"; - Q_EMIT(postDownloadFinished()); - } - - }); - - } - - } - -} - -void DanbooruService::downloadAllTags(KJob *job) -{ - Q_UNUSED(job) - -} - -void DanbooruService::setBlacklist(const QSet< QString > &blacklist) -{ - - if (!blacklist.isEmpty()) { - m_blacklist = blacklist; - } - -} - -void DanbooruService::setBlacklist(const QStringList &blacklist) -{ - - if (blacklist.isEmpty()) { - return; - } - - m_blacklist.clear(); - - for (const auto& element : blacklist) { - m_blacklist.insert(element); - } - -} - -void DanbooruService::setBoardUrl(const QUrl &url) -{ - m_url = url; -} - -void DanbooruService::setMaximumAllowedRating(DanbooruPost::Rating rating) -{ - DanbooruPost::Ratings flags; - - switch (rating) { - case DanbooruPost::Safe: - flags = DanbooruPost::Safe; - break; - case DanbooruPost::Questionable: - flags = DanbooruPost::Safe | DanbooruPost::Questionable; - break; - case DanbooruPost::Explicit: - flags = DanbooruPost::Safe | DanbooruPost::Questionable | DanbooruPost::Explicit; - break; - } - - m_maxRating = flags; - -} - -void DanbooruService::setUserName(const QString &username) -{ - if (username.isEmpty()) { - return; - } - m_username = username; -} - -void DanbooruService::setPassword(const QString &password) -{ - if (password.isEmpty()) { - return; - } - - m_password = password; -} - -void DanbooruService::setPostTags(const QStringList &tags) -{ - if (!tags.isEmpty()) { - m_tags = tags; - } -} - -void DanbooruService::setImageCache(KImageCache *cache) -{ - m_cache = cache; -} - -void DanbooruService::setMaxPosts(int number) -{ - - m_maxPosts = number < 100 ? number : 100; - -} - -void DanbooruService::setCurrentPage(int page) -{ - m_currentPage = page; -} - -void Danbooru::DanbooruService::setResolution(unsigned int width, unsigned int height) -{ - if (width > 0) { - m_minimumWidth = width; - } - - if (height > 0) { - m_minimumHeight = height; - } -} - - -} // namespace Danbooru diff --git a/src/libdanbooru/danbooruservice.h b/src/libdanbooru/danbooruservice.h deleted file mode 100644 index 18c81e5..0000000 --- a/src/libdanbooru/danbooruservice.h +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright 2015 Luca Beltrame - * - * This file is part of Danbooru Client. - * - * Danbooru Client 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 3 of the License, or - * (at your option) any later version. - * - * Danbooru Client 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 Danbooru Client. If not, see . - */ - -#ifndef DANBOORUSERVICE_H -#define DANBOORUSERVICE_H - -/** - * @brief Classes to interact with Danbooru boards - * @file danbooruservice.h - * -**/ - -// Qt - -#include -#include -#include -#include - -// KDE - -#include -#include -#include - -// Own - -#include "danbooru.h" -#include "danboorupost.h" -#include "danboorutag.h" - -class QPixmap; -class QUrl; -class KJob; - -using Danbooru::DanbooruTag; - -namespace Danbooru -{ - -class DanbooruPool; - -using KIO::StoredTransferJob; - -/** - * @brief A class which provides a wrapper around Danbooru's RESTful API. - * - * This class provides access to Danbooru-based image boards - * by making the appropriate RESTful calls to the service and then - * retrieving and parsing the reuslts. - * - * @author Luca Beltrame (lbeltrame@kde.org) - * - * - **/ -class DanbooruService : public QObject -{ - Q_OBJECT - -private: - - // URL fragments - - static const QString POST_URL; - static const QString TAG_URL; - static const QString POOL_URL; - static const QString ARTIST_URL; - static const QString POOL_DATA_URL; - static const QString RELATED_TAG_URL; - - // member variables - - QUrl m_url; - QString m_username; - QString m_password; - QSet m_blacklist; - Danbooru::Ratings m_maxRating; - int m_maxPosts; - int m_currentPage; - unsigned int m_minimumHeight = 0; - unsigned int m_minimumWidth = 0; - QStringList m_tags; - - unsigned int m_postsToFetch; // To tell when to quit - - KImageCache *m_cache; // Pixmap cache - -public: - - /** - * @brief Construct a default instance of the service. - * - * @param boardUrl The URL to connect to. - * @param username Username to use (optional) - * @param password Password to use (optional) - * @param cache A pointer to a KImageCache instance to enable caching - * of downloaded pixmaps. - * @param parent The parent QObject - * - **/ - DanbooruService(QUrl boardUrl = QUrl(), QString username = QString(), - QString password = QString(), KImageCache *cache = 0, - QObject *parent = 0); - - /** - * Default destructor. - **/ - ~DanbooruService(); - - int currentPage() const; - - /** - * @brief Get posts from a the board. - * - * @param page The page containing posts (default: 1) - * @param tags The specific tags to query for (default: all tags) - * @param limit The number of posts to fetch (maximum 100) - * - **/ - Q_INVOKABLE void getPostList(); - - /** - * @brief Get a list of pools from the board. - * - **/ - void getPoolList(); - - /** - * @brief Get the posts associated with a specific pool ID. - * - * @param poolId The ID of the pool to fetch posts from. - * @param page The page of the pool posts (if > 100) - * - **/ - void getPool(int poolId, int page = 1); - - /** - * @brief Get a list of tags from the board - * - * If name is supplied, a list of tags including the exact name of the - * tag is fetched from Danbooru, otherwise the most recent tags are - * retrieved. - * - * The tagDownloaded signal is emitted every time a tag has been - * retrieved. - * - * @param limit The number of tags to get. - * @param name The name of the tag to retrieve, or an empty string - * - **/ - void getTagList(int limit = 10, QString name = QStringLiteral("")); - - /** - * @brief Get tags related to a specific, user supplied list. - * - * @param tags The tags to query for related terms - * @param tagType The type of tag to query for - **/ - void getRelatedTags(const QStringList &tags, DanbooruTag::TagType tagType = DanbooruTag::General); - - /** - * @return The currently allowed ratings when downloading posts. - **/ - const QStringList allowedRatings() const; - - /** - * @return The maximum allowed rating for a post. - **/ - const Danbooru::Ratings maximumAllowedRating() const; - - /** - * @return The currently blacklisted tags. - **/ - const QSet blacklist() const; - - /** - * @return The number of posts downloaded for each page (max 100) - **/ - int maxPosts() const; - - /** - * @brief Fetches the next page of posts. - **/ - Q_INVOKABLE void nextPostPage(); - - /** - * @brief Fetches the next page of pools. - **/ - Q_INVOKABLE void nextPoolPage(); - - /** - * @return A QStringList containing the currently-selected tags - **/ - QStringList postTags() const; - - int minimumWidth() const; - - int minimumHeight() const; - - /** - * @brief Resets the service to the default state, clearing the page counters. - **/ - void reset(); - - /** - * @brief Set blacklisted tags. - * - * Posts with blacklisted tags are not downloaded. - * - * @param blacklist A QSet including unwanted tags. - * - **/ - void setBlacklist(const QStringList &blacklist); - - /** - * @brief Set the tag blacklist. - * - * If a tag is in the blacklist, posts tagged with it will not be downloaded. - * - **/ - - void setBlacklist(const QSet &blacklist); - - /** - * @brief Set the maximum allowed rating for the board. - * - * Posts whose rating is higher than the maximuk allowed will not be - * downloaded. - * - **/ - void setMaximumAllowedRating(Danbooru::Rating rating); - - /** - * @brief Set the board URL to connect to. - * - * @param url The URL to connect to. - * @return void - */ - void setBoardUrl(const QUrl &url); - - /** - * @brief Set an image cache to use. - * - * @param cache A pointer to an intsnaec of KImageCache. - * @return void - */ - void setImageCache(KImageCache *cache); - - /** - * @brief Set username for login. - * - * @param username The username to use. - * - **/ - void setUserName(const QString &username); - - /** - * @brief Set the password used for login. - * - * It should not be the password itself, but a SHA1 hash - * with a specific salt (which is board-dependent; check their - * API access rules). - * - * @param password The salted password to use. - * - **/ - void setPassword(const QString &password); - - void setMaxPosts(int number); - - void setCurrentPage(int page); - - void setPostTags(const QStringList &tags); - - void setResolution(unsigned int width = 0, unsigned int height = 0); - - void setMinimumWidth(unsigned int width) { m_minimumWidth = width; }; - void setMinimumHeight(unsigned int height) { m_minimumHeight = height; }; - -private Q_SLOTS: - void processPostList(KJob *job); - void processTagList(KJob *job); - void downloadAllTags(KJob *job); - -Q_SIGNALS: - - /** - * Emitted when there are no more posts to download. - * - * Connect to this signal to know when downloading is complete. - * - **/ - void postDownloadFinished(); - - /** - * Emitted when there are no more pools to download. - **/ - void poolDownloadFinished(); - - /** - * Emitted when a download error occurred. - * - * The parameter contains the error string. - * - **/ - void downloadError(QString error); - - /** - * Emitted when a post has been downloaded. - * - * The parameter contains a pointer to the post that has been - * downloaded. - **/ - void postDownloaded(Danbooru::DanbooruPost *post); - - /** - * Emitted when a pool has been downloaded. - * - * The parameter contains a pointer to the pool that has been - * downloaded. - **/ - void poolDownloaded(Danbooru::DanbooruPool *pool); - - /** - * Emitted when a tag has been downloaded. - * - * The parameter contains a pointer to the tag that has been - * downloaded. - **/ - void tagDownloaded(Danbooru::DanbooruTag *tag); - -}; -} -#endif // DANBOORUSERVICE_H