Add support for additional itunes tags

Most of the itunes fields are used as backup for non-existent regular
fields.
One exception is the entry image, which only exists in itunes tags.
This commit is contained in:
Bart De Vries 2021-04-05 12:43:35 +02:00
parent 030bc07bb5
commit 0aa9e91f19
4 changed files with 72 additions and 25 deletions

View File

@ -47,7 +47,7 @@ bool Database::migrateTo1()
{ {
qDebug() << "Migrating database to version 1"; qDebug() << "Migrating database to version 1";
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Feeds (name TEXT, url TEXT, image TEXT, link TEXT, description TEXT, deleteAfterCount INTEGER, deleteAfterType INTEGER, subscribed INTEGER, lastUpdated INTEGER, notify BOOL);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Feeds (name TEXT, url TEXT, image TEXT, link TEXT, description TEXT, deleteAfterCount INTEGER, deleteAfterType INTEGER, subscribed INTEGER, lastUpdated INTEGER, notify BOOL);")));
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Entries (feed TEXT, id TEXT UNIQUE, title TEXT, content TEXT, created INTEGER, updated INTEGER, link TEXT, read bool, hasEnclosure BOOL);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Entries (feed TEXT, id TEXT UNIQUE, title TEXT, content TEXT, created INTEGER, updated INTEGER, link TEXT, read bool, hasEnclosure BOOL, image TEXT);")));
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Authors (feed TEXT, id TEXT, name TEXT, uri TEXT, email TEXT);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Authors (feed TEXT, id TEXT, name TEXT, uri TEXT, email TEXT);")));
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Enclosures (feed TEXT, id TEXT, duration INTEGER, size INTEGER, title TEXT, type TEXT, url TEXT);"))); //, playposition INTEGER, filename TEXT);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Enclosures (feed TEXT, id TEXT, duration INTEGER, size INTEGER, title TEXT, type TEXT, url TEXT);"))); //, playposition INTEGER, filename TEXT);")));
TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Queue (listnr INTEGER, feed TEXT, id TEXT);"))); TRUE_OR_RETURN(execute(QStringLiteral("CREATE TABLE IF NOT EXISTS Queue (listnr INTEGER, feed TEXT, id TEXT);")));

View File

@ -45,7 +45,7 @@ DataManager::DataManager()
Database::instance().execute(query); Database::instance().execute(query);
while (query.next()) { while (query.next()) {
m_entrymap[feedurl] += query.value(QStringLiteral("id")).toString(); m_entrymap[feedurl] += query.value(QStringLiteral("id")).toString();
qDebug() << m_entrymap[feedurl]; //qDebug() << m_entrymap[feedurl];
} }
Q_EMIT feedEntriesUpdated(feedurl); Q_EMIT feedEntriesUpdated(feedurl);
}); });
@ -58,7 +58,7 @@ DataManager::DataManager()
while (query.next()) { while (query.next()) {
m_feedmap += query.value(QStringLiteral("url")).toString(); m_feedmap += query.value(QStringLiteral("url")).toString();
} }
qDebug() << m_feedmap; //qDebug() << m_feedmap;
for (auto &feedurl : m_feedmap) { for (auto &feedurl : m_feedmap) {
query.prepare(QStringLiteral("SELECT id FROM Entries WHERE feed=:feed ORDER BY updated DESC;")); query.prepare(QStringLiteral("SELECT id FROM Entries WHERE feed=:feed ORDER BY updated DESC;"));
@ -67,17 +67,17 @@ DataManager::DataManager()
while (query.next()) { while (query.next()) {
m_entrymap[feedurl] += query.value(QStringLiteral("id")).toString(); m_entrymap[feedurl] += query.value(QStringLiteral("id")).toString();
m_entries[query.value(QStringLiteral("id")).toString()] = nullptr; m_entries[query.value(QStringLiteral("id")).toString()] = nullptr;
qDebug() << m_entrymap[feedurl]; //qDebug() << m_entrymap[feedurl];
} }
} }
qDebug() << m_entrymap; //qDebug() << m_entrymap;
query.prepare(QStringLiteral("SELECT id FROM Queue ORDER BY listnr;")); query.prepare(QStringLiteral("SELECT id FROM Queue ORDER BY listnr;"));
Database::instance().execute(query); Database::instance().execute(query);
while (query.next()) { while (query.next()) {
m_queuemap += query.value(QStringLiteral("id")).toString(); m_queuemap += query.value(QStringLiteral("id")).toString();
} }
qDebug() << m_queuemap; //qDebug() << m_queuemap;
} }
Feed* DataManager::getFeed(int const index) const Feed* DataManager::getFeed(int const index) const
@ -247,7 +247,7 @@ void DataManager::addtoQueue(const QString &feedurl, const QString &id)
// Add to internal queuemap data structure // Add to internal queuemap data structure
m_queuemap += id; m_queuemap += id;
qDebug() << m_queuemap; //qDebug() << m_queuemap;
// Get index of this entry // Get index of this entry
const int index = m_queuemap.indexOf(id); // add new entry to end of queue const int index = m_queuemap.indexOf(id); // add new entry to end of queue
@ -281,7 +281,7 @@ void DataManager::removeQueueItem(const int &index)
// First remove the item from the internal data structure // First remove the item from the internal data structure
const QString id = m_queuemap[index]; const QString id = m_queuemap[index];
m_queuemap.removeAt(index); m_queuemap.removeAt(index);
qDebug() << m_queuemap; //qDebug() << m_queuemap;
// Then make sure that the database Queue table reflects these changes // Then make sure that the database Queue table reflects these changes
QSqlQuery query; QSqlQuery query;

View File

@ -67,6 +67,9 @@ void Fetcher::processFeed(Syndication::FeedPtr feed, const QString &url)
if (feed.isNull()) if (feed.isNull())
return; return;
// Retrieve "other" fields; this will include the "itunes" tags
QMultiMap<QString, QDomElement> otherItems = feed->additionalProperties();
QSqlQuery query; QSqlQuery query;
query.prepare(QStringLiteral("UPDATE Feeds SET name=:name, image=:image, link=:link, description=:description, lastUpdated=:lastUpdated WHERE url=:url;")); query.prepare(QStringLiteral("UPDATE Feeds SET name=:name, image=:image, link=:link, description=:description, lastUpdated=:lastUpdated WHERE url=:url;"));
query.bindValue(QStringLiteral(":name"), feed->title()); query.bindValue(QStringLiteral(":name"), feed->title());
@ -77,15 +80,40 @@ void Fetcher::processFeed(Syndication::FeedPtr feed, const QString &url)
QDateTime current = QDateTime::currentDateTime(); QDateTime current = QDateTime::currentDateTime();
query.bindValue(QStringLiteral(":lastUpdated"), current.toSecsSinceEpoch()); query.bindValue(QStringLiteral(":lastUpdated"), current.toSecsSinceEpoch());
for (auto &author : feed->authors()) { // Process authors
processAuthor(author, QLatin1String(""), url); QString authorname, authoremail;
if (feed->authors().count() > 0) {
for (auto &author : feed->authors()) {
processAuthor(url, QLatin1String(""), author->name(), QLatin1String(""), QLatin1String(""));
}
} else {
// Try to find itunes fields if plain author doesn't exist
QString authorname, authoremail;
// First try the "itunes:owner" tag, if that doesn't succeed, then try the "itunes:author" tag
if (otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdowner")).hasChildNodes()) {
QDomNodeList nodelist = otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdowner")).childNodes();
for (int i=0; i < nodelist.length(); i++) {
if (nodelist.item(i).nodeName() == QStringLiteral("itunes:name")) {
authorname = nodelist.item(i).toElement().text();
} else if (nodelist.item(i).nodeName() == QStringLiteral("itunes:email")) {
authoremail = nodelist.item(i).toElement().text();
}
}
} else {
authorname = otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdauthor")).text();
qDebug() << "authorname" << authorname;
}
} }
QString image; QString image = feed->image()->url();
if (feed->image()->url().startsWith(QStringLiteral("/"))) // If there is no regular image tag, then try the itunes tags
image = QUrl(url).adjusted(QUrl::RemovePath).toString() + feed->image()->url(); if (image.isEmpty()) {
else if (otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdimage")).hasAttribute(QStringLiteral("href"))) {
image = feed->image()->url(); image = otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdimage")).attribute(QStringLiteral("href"));
}
}
if (image.startsWith(QStringLiteral("/")))
image = QUrl(url).adjusted(QUrl::RemovePath).toString() + image;
query.bindValue(QStringLiteral(":image"), image); query.bindValue(QStringLiteral(":image"), image);
Database::instance().execute(query); Database::instance().execute(query);
@ -103,6 +131,10 @@ void Fetcher::processFeed(Syndication::FeedPtr feed, const QString &url)
void Fetcher::processEntry(Syndication::ItemPtr entry, const QString &url) void Fetcher::processEntry(Syndication::ItemPtr entry, const QString &url)
{ {
qDebug() << "Processing" << entry->title(); qDebug() << "Processing" << entry->title();
// Retrieve "other" fields; this will include the "itunes" tags
QMultiMap<QString, QDomElement> otherItems = entry->additionalProperties();
QSqlQuery query; QSqlQuery query;
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries WHERE id=:id;")); query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries WHERE id=:id;"));
query.bindValue(QStringLiteral(":id"), entry->id()); query.bindValue(QStringLiteral(":id"), entry->id());
@ -112,7 +144,7 @@ void Fetcher::processEntry(Syndication::ItemPtr entry, const QString &url)
if (query.value(0).toInt() != 0) if (query.value(0).toInt() != 0)
return; return;
query.prepare(QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :content, :created, :updated, :link, 0, :hasEnclosure);")); query.prepare(QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :content, :created, :updated, :link, 0, :hasEnclosure, :image);"));
query.bindValue(QStringLiteral(":feed"), url); query.bindValue(QStringLiteral(":feed"), url);
query.bindValue(QStringLiteral(":id"), entry->id()); query.bindValue(QStringLiteral(":id"), entry->id());
query.bindValue(QStringLiteral(":title"), QTextDocumentFragment::fromHtml(entry->title()).toPlainText()); query.bindValue(QStringLiteral(":title"), QTextDocumentFragment::fromHtml(entry->title()).toPlainText());
@ -126,29 +158,44 @@ void Fetcher::processEntry(Syndication::ItemPtr entry, const QString &url)
else else
query.bindValue(QStringLiteral(":content"), entry->description()); query.bindValue(QStringLiteral(":content"), entry->description());
// Look for image in itunes tags
QString image;
if (otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdimage")).hasAttribute(QStringLiteral("href"))) {
image = otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdimage")).attribute(QStringLiteral("href"));
}
if (image.startsWith(QStringLiteral("/")))
image = QUrl(url).adjusted(QUrl::RemovePath).toString() + image;
query.bindValue(QStringLiteral(":image"), image);
//qDebug() << "Entry image found" << image;
Database::instance().execute(query); Database::instance().execute(query);
for (const auto &author : entry->authors()) { if (entry->authors().count() > 0) {
processAuthor(author, entry->id(), url); for (const auto &author : entry->authors()) {
processAuthor(url, entry->id(), author->name(), author->uri(), author->email());
}
} else {
// As fallback, check if there is itunes "author" information
QString authorName = otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdauthor")).text();
if (!authorName.isEmpty()) processAuthor(url, entry->id(), authorName, QLatin1String(""), QLatin1String(""));
} }
for (const auto &enclosure : entry->enclosures()) { for (const auto &enclosure : entry->enclosures()) {
processEnclosure(enclosure, entry, url); processEnclosure(enclosure, entry, url);
} }
QMultiMap<QString, QDomElement> otherItems = entry->additionalProperties();
qDebug() << "other items" << otherItems.value(QStringLiteral("http://www.itunes.com/dtds/podcast-1.0.dtdimage")).attribute(QStringLiteral("href"));
Q_EMIT entryAdded(url, entry->id()); Q_EMIT entryAdded(url, entry->id());
} }
void Fetcher::processAuthor(Syndication::PersonPtr author, const QString &entryId, const QString &url) void Fetcher::processAuthor(const QString &url, const QString &entryId, const QString &authorName, const QString &authorUri, const QString &authorEmail)
{ {
QSqlQuery query; QSqlQuery query;
query.prepare(QStringLiteral("INSERT INTO Authors VALUES(:feed, :id, :name, :uri, :email);")); query.prepare(QStringLiteral("INSERT INTO Authors VALUES(:feed, :id, :name, :uri, :email);"));
query.bindValue(QStringLiteral(":feed"), url); query.bindValue(QStringLiteral(":feed"), url);
query.bindValue(QStringLiteral(":id"), entryId); query.bindValue(QStringLiteral(":id"), entryId);
query.bindValue(QStringLiteral(":name"), author->name()); query.bindValue(QStringLiteral(":name"), authorName);
query.bindValue(QStringLiteral(":uri"), author->uri()); query.bindValue(QStringLiteral(":uri"), authorUri);
query.bindValue(QStringLiteral(":email"), author->email()); query.bindValue(QStringLiteral(":email"), authorEmail);
Database::instance().execute(query); Database::instance().execute(query);
} }

View File

@ -33,7 +33,7 @@ private:
void processFeed(Syndication::FeedPtr feed, const QString &url); void processFeed(Syndication::FeedPtr feed, const QString &url);
void processEntry(Syndication::ItemPtr entry, const QString &url); void processEntry(Syndication::ItemPtr entry, const QString &url);
void processAuthor(Syndication::PersonPtr author, const QString &entryId, const QString &url); void processAuthor(const QString &url, const QString &entryId, const QString &authorName, const QString &authorUri, const QString &authorEmail);
void processEnclosure(Syndication::EnclosurePtr enclosure, Syndication::ItemPtr entry, const QString &feedUrl); void processEnclosure(Syndication::EnclosurePtr enclosure, Syndication::ItemPtr entry, const QString &feedUrl);