Clementine-audio-player-Mac.../src/internet/podcasts/podcastbackend.cpp

371 lines
9.4 KiB
C++

/* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com>
Copyright 2014, Krzysztof A. Sobiecki <sobkas@gmail.com>
Copyright 2014, John Maguire <john.maguire@gmail.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "podcastbackend.h"
#include <QMutexLocker>
#include "core/application.h"
#include "core/database.h"
#include "core/logging.h"
#include "core/scopedtransaction.h"
PodcastBackend::PodcastBackend(Application* app, QObject* parent)
: QObject(parent), app_(app), db_(app->database()) {}
void PodcastBackend::Subscribe(Podcast* podcast) {
// If this podcast is already in the database, do nothing
if (podcast->is_valid()) {
return;
}
// If there's an entry in the database with the same URL, take its data.
Podcast existing_podcast = GetSubscriptionByUrl(podcast->url());
if (existing_podcast.is_valid()) {
*podcast = existing_podcast;
return;
}
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
ScopedTransaction t(&db);
// Insert the podcast.
QSqlQuery q(db);
q.prepare("INSERT INTO podcasts (" + Podcast::kColumnSpec +
")"
" VALUES (" +
Podcast::kBindSpec + ")");
podcast->BindToQuery(&q);
q.exec();
if (db_->CheckErrors(q)) return;
// Update the database ID.
const int database_id = q.lastInsertId().toInt();
podcast->set_database_id(database_id);
// Update the IDs of any episodes.
PodcastEpisodeList* episodes = podcast->mutable_episodes();
for (auto it = episodes->begin(); it != episodes->end(); ++it) {
it->set_podcast_database_id(database_id);
}
// Add those episodes to the database.
AddEpisodes(episodes, &db);
t.Commit();
emit SubscriptionAdded(*podcast);
}
void PodcastBackend::Unsubscribe(const Podcast& podcast) {
// If this podcast is not already in the database, do nothing
if (!podcast.is_valid()) {
return;
}
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
ScopedTransaction t(&db);
// Remove the podcast.
QSqlQuery q(db);
q.prepare("DELETE FROM podcasts WHERE ROWID = :id");
q.bindValue(":id", podcast.database_id());
q.exec();
if (db_->CheckErrors(q)) return;
// Remove all episodes in the podcast
q.prepare("DELETE FROM podcast_episodes WHERE podcast_id = :id");
q.bindValue(":id", podcast.database_id());
q.exec();
if (db_->CheckErrors(q)) return;
t.Commit();
emit SubscriptionRemoved(podcast);
}
void PodcastBackend::AddEpisodes(PodcastEpisodeList* episodes,
QSqlDatabase* db) {
QSqlQuery q(*db);
q.prepare("INSERT INTO podcast_episodes (" + PodcastEpisode::kColumnSpec +
")"
" VALUES (" +
PodcastEpisode::kBindSpec + ")");
for (auto it = episodes->begin(); it != episodes->end(); ++it) {
it->BindToQuery(&q);
q.exec();
if (db_->CheckErrors(q)) continue;
const int database_id = q.lastInsertId().toInt();
it->set_database_id(database_id);
}
}
void PodcastBackend::AddEpisodes(PodcastEpisodeList* episodes) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
ScopedTransaction t(&db);
AddEpisodes(episodes, &db);
t.Commit();
emit EpisodesAdded(*episodes);
}
void PodcastBackend::UpdateEpisodes(const PodcastEpisodeList& episodes) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
ScopedTransaction t(&db);
QSqlQuery q(db);
q.prepare(
"UPDATE podcast_episodes"
" SET listened = :listened,"
" listened_date = :listened_date,"
" downloaded = :downloaded,"
" local_url = :local_url"
" WHERE ROWID = :id");
for (const PodcastEpisode& episode : episodes) {
q.bindValue(":listened", episode.listened());
q.bindValue(":listened_date", episode.listened_date().toTime_t());
q.bindValue(":downloaded", episode.downloaded());
q.bindValue(":local_url", episode.local_url().toEncoded());
q.bindValue(":id", episode.database_id());
q.exec();
db_->CheckErrors(q);
}
t.Commit();
emit EpisodesUpdated(episodes);
}
PodcastList PodcastBackend::GetAllSubscriptions() {
PodcastList ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + Podcast::kColumnSpec + " FROM podcasts");
q.exec();
if (db_->CheckErrors(q)) return ret;
while (q.next()) {
Podcast podcast;
podcast.InitFromQuery(q);
ret << podcast;
}
return ret;
}
Podcast PodcastBackend::GetSubscriptionById(int id) {
Podcast ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + Podcast::kColumnSpec +
" FROM podcasts"
" WHERE ROWID = :id");
q.bindValue(":id", id);
q.exec();
if (!db_->CheckErrors(q) && q.next()) {
ret.InitFromQuery(q);
}
return ret;
}
Podcast PodcastBackend::GetSubscriptionByUrl(const QUrl& url) {
Podcast ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + Podcast::kColumnSpec +
" FROM podcasts"
" WHERE url = :url");
q.bindValue(":url", url.toEncoded());
q.exec();
if (!db_->CheckErrors(q) && q.next()) {
ret.InitFromQuery(q);
}
return ret;
}
PodcastEpisodeList PodcastBackend::GetEpisodes(int podcast_id) {
PodcastEpisodeList ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE podcast_id = :id"
" ORDER BY publication_date DESC");
q.bindValue(":id", podcast_id);
q.exec();
if (db_->CheckErrors(q)) return ret;
while (q.next()) {
PodcastEpisode episode;
episode.InitFromQuery(q);
ret << episode;
}
return ret;
}
PodcastEpisode PodcastBackend::GetEpisodeById(int id) {
PodcastEpisode ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE ROWID = :id");
q.bindValue(":id", id);
q.exec();
if (!db_->CheckErrors(q) && q.next()) {
ret.InitFromQuery(q);
}
return ret;
}
PodcastEpisode PodcastBackend::GetEpisodeByUrl(const QUrl& url) {
PodcastEpisode ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE url = :url");
q.bindValue(":url", url.toEncoded());
q.exec();
if (!db_->CheckErrors(q) && q.next()) {
ret.InitFromQuery(q);
}
return ret;
}
PodcastEpisode PodcastBackend::GetEpisodeByUrlOrLocalUrl(const QUrl& url) {
PodcastEpisode ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE url = :url"
" OR local_url = :url");
q.bindValue(":url", url.toEncoded());
q.exec();
if (!db_->CheckErrors(q) && q.next()) {
ret.InitFromQuery(q);
}
return ret;
}
PodcastEpisodeList PodcastBackend::GetOldDownloadedEpisodes(
const QDateTime& max_listened_date) {
PodcastEpisodeList ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE downloaded = 'true'"
" AND listened_date <= :max_listened_date");
q.bindValue(":max_listened_date", max_listened_date.toTime_t());
q.exec();
if (db_->CheckErrors(q)) return ret;
while (q.next()) {
PodcastEpisode episode;
episode.InitFromQuery(q);
ret << episode;
}
return ret;
}
PodcastEpisode PodcastBackend::GetOldestDownloadedListenedEpisode() {
PodcastEpisode ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE downloaded = 'true'"
" AND listened = 'true'"
" ORDER BY listened_date ASC");
q.exec();
if (db_->CheckErrors(q)) return ret;
q.next();
ret.InitFromQuery(q);
return ret;
}
PodcastEpisodeList PodcastBackend::GetNewDownloadedEpisodes() {
PodcastEpisodeList ret;
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
QSqlQuery q(db);
q.prepare("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
" FROM podcast_episodes"
" WHERE downloaded = 'true'"
" AND listened = 'false'");
q.exec();
if (db_->CheckErrors(q)) return ret;
while (q.next()) {
PodcastEpisode episode;
episode.InitFromQuery(q);
ret << episode;
}
return ret;
}