2012-03-04 21:47:58 +01:00
|
|
|
/* This file is part of Clementine.
|
|
|
|
Copyright 2012, David Sansome <me@davidsansome.com>
|
2014-11-01 19:38:39 +01:00
|
|
|
Copyright 2014, Krzysztof A. Sobiecki <sobkas@gmail.com>
|
|
|
|
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
2014-02-07 16:34:20 +01:00
|
|
|
|
2012-03-04 21:47:58 +01:00
|
|
|
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.
|
2014-02-07 16:34:20 +01:00
|
|
|
|
2012-03-04 21:47:58 +01:00
|
|
|
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.
|
2014-02-07 16:34:20 +01:00
|
|
|
|
2012-03-04 21:47:58 +01:00
|
|
|
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"
|
2014-12-12 15:38:34 +01:00
|
|
|
|
|
|
|
#include <QMutexLocker>
|
|
|
|
|
2012-03-04 21:47:58 +01:00
|
|
|
#include "core/application.h"
|
|
|
|
#include "core/database.h"
|
2012-03-09 13:15:24 +01:00
|
|
|
#include "core/logging.h"
|
2012-03-04 21:47:58 +01:00
|
|
|
#include "core/scopedtransaction.h"
|
|
|
|
|
|
|
|
PodcastBackend::PodcastBackend(Application* app, QObject* parent)
|
2014-02-07 16:34:20 +01:00
|
|
|
: QObject(parent), app_(app), db_(app->database()) {}
|
2012-03-04 21:47:58 +01:00
|
|
|
|
|
|
|
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.
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q("INSERT INTO podcasts (" + Podcast::kColumnSpec +
|
|
|
|
")"
|
|
|
|
" VALUES (" +
|
|
|
|
Podcast::kBindSpec + ")",
|
|
|
|
db);
|
2012-03-04 21:47:58 +01:00
|
|
|
podcast->BindToQuery(&q);
|
|
|
|
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2012-03-04 21:47:58 +01:00
|
|
|
|
|
|
|
// 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();
|
2014-02-07 16:34:20 +01:00
|
|
|
for (auto it = episodes->begin(); it != episodes->end(); ++it) {
|
2012-03-04 21:47:58 +01:00
|
|
|
it->set_podcast_database_id(database_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add those episodes to the database.
|
|
|
|
AddEpisodes(episodes, &db);
|
2012-03-06 19:37:46 +01:00
|
|
|
|
|
|
|
t.Commit();
|
2012-03-07 12:04:47 +01:00
|
|
|
|
|
|
|
emit SubscriptionAdded(*podcast);
|
2012-03-04 21:47:58 +01:00
|
|
|
}
|
|
|
|
|
2012-03-07 12:22:43 +01:00
|
|
|
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("DELETE FROM podcasts WHERE ROWID = :id", db);
|
|
|
|
q.bindValue(":id", podcast.database_id());
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2012-03-07 12:22:43 +01:00
|
|
|
|
|
|
|
// Remove all episodes in the podcast
|
|
|
|
q = QSqlQuery("DELETE FROM podcast_episodes WHERE podcast_id = :id", db);
|
|
|
|
q.bindValue(":id", podcast.database_id());
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2012-03-07 12:22:43 +01:00
|
|
|
|
|
|
|
t.Commit();
|
|
|
|
|
|
|
|
emit SubscriptionRemoved(podcast);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PodcastBackend::AddEpisodes(PodcastEpisodeList* episodes,
|
|
|
|
QSqlDatabase* db) {
|
|
|
|
QSqlQuery q("INSERT INTO podcast_episodes (" + PodcastEpisode::kColumnSpec +
|
|
|
|
")"
|
|
|
|
" VALUES (" +
|
|
|
|
PodcastEpisode::kBindSpec + ")",
|
|
|
|
*db);
|
2012-03-04 21:47:58 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
for (auto it = episodes->begin(); it != episodes->end(); ++it) {
|
2012-03-04 21:47:58 +01:00
|
|
|
it->BindToQuery(&q);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) continue;
|
2012-03-04 21:47:58 +01:00
|
|
|
|
|
|
|
const int database_id = q.lastInsertId().toInt();
|
|
|
|
it->set_database_id(database_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
void PodcastBackend::AddEpisodes(PodcastEpisodeList* episodes) {
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
ScopedTransaction t(&db);
|
|
|
|
|
|
|
|
AddEpisodes(episodes, &db);
|
|
|
|
t.Commit();
|
|
|
|
|
|
|
|
emit EpisodesAdded(*episodes);
|
2012-03-06 19:37:46 +01:00
|
|
|
}
|
|
|
|
|
2012-03-09 17:47:56 +01:00
|
|
|
void PodcastBackend::UpdateEpisodes(const PodcastEpisodeList& episodes) {
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
ScopedTransaction t(&db);
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(
|
|
|
|
"UPDATE podcast_episodes"
|
|
|
|
" SET listened = :listened,"
|
|
|
|
" listened_date = :listened_date,"
|
|
|
|
" downloaded = :downloaded,"
|
|
|
|
" local_url = :local_url"
|
|
|
|
" WHERE ROWID = :id",
|
|
|
|
db);
|
2012-03-09 17:47:56 +01:00
|
|
|
|
2014-01-28 16:04:17 +01:00
|
|
|
for (const PodcastEpisode& episode : episodes) {
|
2012-03-09 17:47:56 +01:00
|
|
|
q.bindValue(":listened", episode.listened());
|
2012-03-12 20:35:47 +01:00
|
|
|
q.bindValue(":listened_date", episode.listened_date().toTime_t());
|
2012-03-09 17:47:56 +01:00
|
|
|
q.bindValue(":downloaded", episode.downloaded());
|
2012-03-11 13:27:48 +01:00
|
|
|
q.bindValue(":local_url", episode.local_url().toEncoded());
|
2012-03-09 17:47:56 +01:00
|
|
|
q.bindValue(":id", episode.database_id());
|
|
|
|
q.exec();
|
|
|
|
db_->CheckErrors(q);
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Commit();
|
|
|
|
|
|
|
|
emit EpisodesUpdated(episodes);
|
|
|
|
}
|
|
|
|
|
2012-03-04 21:47:58 +01:00
|
|
|
PodcastList PodcastBackend::GetAllSubscriptions() {
|
|
|
|
PodcastList ret;
|
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
QSqlQuery q("SELECT ROWID, " + Podcast::kColumnSpec + " FROM podcasts", db);
|
2012-03-04 21:47:58 +01:00
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return ret;
|
2012-03-04 21:47:58 +01:00
|
|
|
|
|
|
|
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());
|
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
QSqlQuery q("SELECT ROWID, " + Podcast::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcasts"
|
|
|
|
" WHERE ROWID = :id",
|
|
|
|
db);
|
2012-03-04 21:47:58 +01:00
|
|
|
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());
|
|
|
|
|
2012-03-09 13:15:24 +01:00
|
|
|
QSqlQuery q("SELECT ROWID, " + Podcast::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcasts"
|
|
|
|
" WHERE url = :url",
|
|
|
|
db);
|
2012-03-04 21:47:58 +01:00
|
|
|
q.bindValue(":url", url.toEncoded());
|
|
|
|
q.exec();
|
|
|
|
if (!db_->CheckErrors(q) && q.next()) {
|
|
|
|
ret.InitFromQuery(q);
|
2012-03-09 13:15:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
PodcastEpisodeList PodcastBackend::GetEpisodes(int podcast_id) {
|
|
|
|
PodcastEpisodeList ret;
|
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QSqlQuery q("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcast_episodes"
|
2014-12-11 01:25:56 +01:00
|
|
|
" WHERE podcast_id = :id"
|
|
|
|
" ORDER BY publication_date DESC" ,
|
2014-02-07 16:34:20 +01:00
|
|
|
db);
|
2012-03-09 13:15:24 +01:00
|
|
|
q.bindValue(":db", podcast_id);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return ret;
|
2012-03-09 13:15:24 +01:00
|
|
|
|
|
|
|
while (q.next()) {
|
|
|
|
PodcastEpisode episode;
|
|
|
|
episode.InitFromQuery(q);
|
|
|
|
ret << episode;
|
2012-03-04 21:47:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2012-03-10 22:05:57 +01:00
|
|
|
|
|
|
|
PodcastEpisode PodcastBackend::GetEpisodeById(int id) {
|
|
|
|
PodcastEpisode ret;
|
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QSqlQuery q("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcast_episodes"
|
|
|
|
" WHERE ROWID = :id",
|
|
|
|
db);
|
2012-03-10 22:05:57 +01:00
|
|
|
q.bindValue(":db", 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("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcast_episodes"
|
|
|
|
" WHERE url = :url",
|
|
|
|
db);
|
2012-03-11 13:27:48 +01:00
|
|
|
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("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcast_episodes"
|
|
|
|
" WHERE url = :url"
|
|
|
|
" OR local_url = :url",
|
|
|
|
db);
|
2012-03-11 13:27:48 +01:00
|
|
|
q.bindValue(":url", url.toEncoded());
|
2012-03-10 22:05:57 +01:00
|
|
|
q.exec();
|
|
|
|
if (!db_->CheckErrors(q) && q.next()) {
|
|
|
|
ret.InitFromQuery(q);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2012-03-12 20:35:47 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
PodcastEpisodeList PodcastBackend::GetOldDownloadedEpisodes(
|
|
|
|
const QDateTime& max_listened_date) {
|
2012-03-12 20:35:47 +01:00
|
|
|
PodcastEpisodeList ret;
|
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QSqlQuery q("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcast_episodes"
|
|
|
|
" WHERE downloaded = 'true'"
|
|
|
|
" AND listened_date <= :max_listened_date",
|
|
|
|
db);
|
2012-03-12 20:35:47 +01:00
|
|
|
q.bindValue(":max_listened_date", max_listened_date.toTime_t());
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return ret;
|
2012-03-12 20:35:47 +01:00
|
|
|
|
|
|
|
while (q.next()) {
|
|
|
|
PodcastEpisode episode;
|
|
|
|
episode.InitFromQuery(q);
|
|
|
|
ret << episode;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2014-01-01 18:23:52 +01:00
|
|
|
|
2014-12-06 19:44:12 +01:00
|
|
|
PodcastEpisode PodcastBackend::GetOldestDownloadedListenedEpisode() {
|
|
|
|
PodcastEpisode ret;
|
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QSqlQuery q("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
|
|
|
|
" FROM podcast_episodes"
|
|
|
|
" WHERE downloaded = 'true'"
|
|
|
|
" AND listened = 'true'"
|
|
|
|
" ORDER BY listened_date ASC",
|
|
|
|
db);
|
|
|
|
q.exec();
|
|
|
|
if (db_->CheckErrors(q)) return ret;
|
|
|
|
q.next();
|
|
|
|
ret.InitFromQuery(q);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-01-01 18:23:52 +01:00
|
|
|
PodcastEpisodeList PodcastBackend::GetNewDownloadedEpisodes() {
|
|
|
|
PodcastEpisodeList ret;
|
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QSqlQuery q("SELECT ROWID, " + PodcastEpisode::kColumnSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" FROM podcast_episodes"
|
|
|
|
" WHERE downloaded = 'true'"
|
|
|
|
" AND listened = 'false'",
|
|
|
|
db);
|
2014-01-01 18:23:52 +01:00
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return ret;
|
2014-01-01 18:23:52 +01:00
|
|
|
|
|
|
|
while (q.next()) {
|
|
|
|
PodcastEpisode episode;
|
|
|
|
episode.InitFromQuery(q);
|
|
|
|
ret << episode;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|