Merge pull request #3567 from ngosang/rssfixes

[RSS] Several changes in RSSs. Closes #3560
This commit is contained in:
sledgehammer999 2015-09-02 14:24:25 -05:00
commit 70f2086202
5 changed files with 112 additions and 84 deletions

View file

@ -116,16 +116,12 @@
<widget class="QLabel" name="news_lbl"> <widget class="QLabel" name="news_lbl">
<property name="font"> <property name="font">
<font> <font>
<weight>50</weight> <weight>75</weight>
<bold>false</bold> <bold>true</bold>
</font> </font>
</property> </property>
<property name="text"> <property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt; <string>Torrents: (double-click to download)</string>
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Torrents:&lt;/span&gt; &lt;span style=&quot; font-style:italic;&quot;&gt;(double-click to download)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -139,7 +135,7 @@ p, li { white-space: pre-wrap; }
<enum>Qt::CustomContextMenu</enum> <enum>Qt::CustomContextMenu</enum>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum> <enum>QAbstractItemView::ExtendedSelection</enum>
</property> </property>
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum> <enum>QAbstractItemView::SelectItems</enum>

View file

@ -112,19 +112,30 @@ void RSSImp::displayItemsListMenu(const QPoint&)
{ {
QMenu myItemListMenu(this); QMenu myItemListMenu(this);
QList<QListWidgetItem*> selectedItems = listArticles->selectedItems(); QList<QListWidgetItem*> selectedItems = listArticles->selectedItems();
if (selectedItems.size() > 0) { if (selectedItems.size() <= 0)
bool has_attachment = false; return;
bool hasTorrent = false;
bool hasLink = false;
foreach (const QListWidgetItem* item, selectedItems) { foreach (const QListWidgetItem* item, selectedItems) {
if (m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()) if (!item) continue;
->getItem(item->data(Article::IdRole).toString())->hasAttachment()) { RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString());
has_attachment = true; if (!feed) continue;
RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString());
if (!article) continue;
if (!article->torrentUrl().isEmpty())
hasTorrent = true;
if (!article->link().isEmpty())
hasLink = true;
if (hasTorrent && hasLink)
break; break;
} }
} if (hasTorrent)
if (has_attachment)
myItemListMenu.addAction(actionDownload_torrent); myItemListMenu.addAction(actionDownload_torrent);
if (hasLink)
myItemListMenu.addAction(actionOpen_news_URL); myItemListMenu.addAction(actionOpen_news_URL);
} if (hasTorrent || hasLink)
myItemListMenu.exec(QCursor::pos()); myItemListMenu.exec(QCursor::pos());
} }
@ -238,21 +249,13 @@ void RSSImp::deleteSelectedItems()
QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems(); QList<QTreeWidgetItem*> selectedItems = m_feedList->selectedItems();
if (selectedItems.isEmpty()) if (selectedItems.isEmpty())
return; return;
if ((selectedItems.size() == 1) && (selectedItems.first() == m_feedList->stickyUnreadItem()))
int ret;
if (selectedItems.size() > 1) {
ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to delete these elements from the list?"),
tr("&Yes"), tr("&No"),
QString(), 0, 1);
}
else {
if (selectedItems.first() == m_feedList->stickyUnreadItem())
return; return;
ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to delete this element from the list?"),
tr("&Yes"), tr("&No"), QMessageBox::StandardButton answer = QMessageBox::question(this, tr("Deletion confirmation"),
QString(), 0, 1); tr("Are you sure you want to delete the selected RSS feeds?"),
} QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
if (ret) if (answer == QMessageBox::No)
return; return;
foreach (QTreeWidgetItem* item, selectedItems) { foreach (QTreeWidgetItem* item, selectedItems) {
@ -337,31 +340,57 @@ void RSSImp::refreshAllFeeds()
void RSSImp::downloadSelectedTorrents() void RSSImp::downloadSelectedTorrents()
{ {
QList<QListWidgetItem*> selected_items = listArticles->selectedItems(); QList<QListWidgetItem*> selected_items = listArticles->selectedItems();
foreach (const QListWidgetItem* item, selected_items) { if (selected_items.size() <= 0)
return;
foreach (QListWidgetItem* item, selected_items) {
if (!item) continue; if (!item) continue;
RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString());
if (!feed) continue; if (!feed) continue;
RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString());
if (!article) continue; if (!article) continue;
// Mark as read
article->markAsRead();
item->setData(Article::ColorRole, QVariant(QColor("grey")));
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png")));
if (article->torrentUrl().isEmpty())
continue;
if (Preferences::instance()->useAdditionDialog()) if (Preferences::instance()->useAdditionDialog())
AddNewTorrentDialog::show(article->torrentUrl()); AddNewTorrentDialog::show(article->torrentUrl());
else else
BitTorrent::Session::instance()->addTorrent(article->torrentUrl()); BitTorrent::Session::instance()->addTorrent(article->torrentUrl());
} }
// Decrement feed nb unread news
updateItemInfos(m_feedList->stickyUnreadItem());
updateItemInfos(m_feedList->getTreeItemFromUrl(selected_items.first()->data(Article::FeedUrlRole).toString()));
} }
// open the url of the selected RSS articles in the Web browser // open the url of the selected RSS articles in the Web browser
void RSSImp::openSelectedArticlesUrls() void RSSImp::openSelectedArticlesUrls()
{ {
QList<QListWidgetItem *> selected_items = listArticles->selectedItems(); QList<QListWidgetItem *> selected_items = listArticles->selectedItems();
foreach (const QListWidgetItem* item, selected_items) { if (selected_items.size() <= 0)
RssArticlePtr news = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()) return;
->getItem(item->data(Article::IdRole).toString()); foreach (QListWidgetItem* item, selected_items) {
const QString link = news->link(); if (!item) continue;
RssFeedPtr feed = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString());
if (!feed) continue;
RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString());
if (!article) continue;
// Mark as read
article->markAsRead();
item->setData(Article::ColorRole, QVariant(QColor("grey")));
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png")));
const QString link = article->link();
if (!link.isEmpty()) if (!link.isEmpty())
QDesktopServices::openUrl(QUrl(link)); QDesktopServices::openUrl(QUrl(link));
} }
// Decrement feed nb unread news
updateItemInfos(m_feedList->stickyUnreadItem());
updateItemInfos(m_feedList->getTreeItemFromUrl(selected_items.first()->data(Article::FeedUrlRole).toString()));
} }
//right-click on stream : give it an alias //right-click on stream : give it an alias
@ -538,21 +567,11 @@ void RSSImp::refreshTextBrowser()
{ {
QList<QListWidgetItem*> selection = listArticles->selectedItems(); QList<QListWidgetItem*> selection = listArticles->selectedItems();
if (selection.empty()) return; if (selection.empty()) return;
Q_ASSERT(selection.size() == 1);
QListWidgetItem *item = selection.first(); QListWidgetItem *item = selection.first();
Q_ASSERT(item); Q_ASSERT(item);
if (item == m_currentArticle) return; if (item == m_currentArticle) return;
// Stop displaying previous news if necessary
if (m_feedList->currentFeed() == m_feedList->stickyUnreadItem()) {
if (m_currentArticle) {
disconnect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser()));
listArticles->removeItemWidget(m_currentArticle);
Q_ASSERT(m_currentArticle);
delete m_currentArticle;
connect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser()));
}
m_currentArticle = item; m_currentArticle = item;
}
RssFeedPtr stream = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()); RssFeedPtr stream = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString());
RssArticlePtr article = stream->getItem(item->data(Article::IdRole).toString()); RssArticlePtr article = stream->getItem(item->data(Article::IdRole).toString());
QString html; QString html;
@ -675,15 +694,12 @@ void RSSImp::onFeedContentChanged(const QString& url)
qDebug() << Q_FUNC_INFO << url; qDebug() << Q_FUNC_INFO << url;
QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url); QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url);
// If the feed is selected, update the displayed news // If the feed is selected, update the displayed news
if (m_feedList->currentItem() == item ) { if (m_feedList->currentItem() == item)
populateArticleList(item); populateArticleList(item);
}
else {
// Update unread items // Update unread items
if (m_feedList->currentItem() == m_feedList->stickyUnreadItem()) else if (m_feedList->currentItem() == m_feedList->stickyUnreadItem())
populateArticleList(m_feedList->stickyUnreadItem()); populateArticleList(m_feedList->stickyUnreadItem());
} }
}
void RSSImp::updateRefreshInterval(uint val) void RSSImp::updateRefreshInterval(uint val)
{ {
@ -715,8 +731,6 @@ RSSImp::RSSImp(QWidget *parent):
m_feedList = new FeedListWidget(splitter_h, m_rssManager); m_feedList = new FeedListWidget(splitter_h, m_rssManager);
splitter_h->insertWidget(0, m_feedList); splitter_h->insertWidget(0, m_feedList);
listArticles->setSelectionBehavior(QAbstractItemView::SelectItems);
listArticles->setSelectionMode(QAbstractItemView::SingleSelection);
editHotkey = new QShortcut(QKeySequence("F2"), m_feedList, 0, 0, Qt::WidgetShortcut); editHotkey = new QShortcut(QKeySequence("F2"), m_feedList, 0, 0, Qt::WidgetShortcut);
connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRssFile())); connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRssFile()));
connect(m_feedList, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRssFile())); connect(m_feedList, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRssFile()));

View file

@ -82,7 +82,7 @@ const QString& RssArticle::author() const {
} }
const QString& RssArticle::torrentUrl() const { const QString& RssArticle::torrentUrl() const {
return m_torrentUrl.isEmpty() ? m_link : m_torrentUrl; return m_torrentUrl;
} }
const QString& RssArticle::link() const { const QString& RssArticle::link() const {
@ -123,6 +123,6 @@ const QString& RssArticle::title() const
} }
void RssArticle::handleTorrentDownloadSuccess(const QString &url) { void RssArticle::handleTorrentDownloadSuccess(const QString &url) {
if (url == m_torrentUrl || url == m_link) if (url == m_torrentUrl)
markAsRead(); markAsRead();
} }

View file

@ -364,6 +364,12 @@ void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList* rules, const
rules->saveRulesToStorage(); rules->saveRulesToStorage();
// Download the torrent // Download the torrent
const QString& torrent_url = article->torrentUrl(); const QString& torrent_url = article->torrentUrl();
if (torrent_url.isEmpty()) {
Logger::instance()->addMessage(tr("Automatic download %1 from %2 RSS feed failed because it doesn't contain a torrent or a magnet link...").arg(article->title()).arg(displayName()), Log::WARNING);
article->markAsRead();
return;
}
Logger::instance()->addMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(article->title()).arg(displayName())); Logger::instance()->addMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(article->title()).arg(displayName()));
connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection);
connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection);

View file

@ -254,24 +254,32 @@ void RssParser::parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl)
if (xml.isStartElement()) { if (xml.isStartElement()) {
if (xml.name() == "title") if (xml.name() == "title")
article["title"] = xml.readElementText(); article["title"] = xml.readElementText().trimmed();
else if (xml.name() == "enclosure") { else if (xml.name() == "enclosure") {
if (xml.attributes().value("type") == "application/x-bittorrent") if (xml.attributes().value("type") == "application/x-bittorrent")
article["torrent_url"] = xml.attributes().value("url").toString(); article["torrent_url"] = xml.attributes().value("url").toString();
} }
else if (xml.name() == "link") else if (xml.name() == "link") {
article["news_link"] = xml.readElementText(); QString link = xml.readElementText().trimmed();
if (link.startsWith("magnet:", Qt::CaseInsensitive))
article["torrent_url"] = link; // magnet link instead of a news URL
else
article["news_link"] = link;
}
else if (xml.name() == "description") else if (xml.name() == "description")
article["description"] = xml.readElementText(); article["description"] = xml.readElementText().trimmed();
else if (xml.name() == "pubDate") else if (xml.name() == "pubDate")
article["date"] = parseDate(xml.readElementText()); article["date"] = parseDate(xml.readElementText().trimmed());
else if (xml.name() == "author") else if (xml.name() == "author")
article["author"] = xml.readElementText(); article["author"] = xml.readElementText().trimmed();
else if (xml.name() == "guid") else if (xml.name() == "guid")
article["id"] = xml.readElementText(); article["id"] = xml.readElementText().trimmed();
} }
} }
if (!article.contains("torrent_url") && article.contains("news_link"))
article["torrent_url"] = article["news_link"];
if (!article.contains("id")) { if (!article.contains("id")) {
// Item does not have a guid, fall back to some other identifier // Item does not have a guid, fall back to some other identifier
const QString link = article.value("news_link").toString(); const QString link = article.value("news_link").toString();
@ -338,20 +346,21 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl,
// Workaround for CDATA (QString cannot parse html escapes on it's own) // Workaround for CDATA (QString cannot parse html escapes on it's own)
QTextDocument doc; QTextDocument doc;
doc.setHtml(xml.readElementText()); doc.setHtml(xml.readElementText());
article["title"] = doc.toPlainText(); article["title"] = doc.toPlainText().trimmed();
} }
else if (xml.name() == "link") { else if (xml.name() == "link") {
QString theLink = ( xml.attributes().isEmpty() ? QString link = ( xml.attributes().isEmpty() ?
xml.readElementText() : xml.readElementText().trimmed() :
xml.attributes().value("href").toString() ); xml.attributes().value("href").toString() );
if (link.startsWith("magnet:", Qt::CaseInsensitive))
article["torrent_url"] = link; // magnet link instead of a news URL
else
// Atom feeds can have relative links, work around this and // Atom feeds can have relative links, work around this and
// take the stress of figuring article full URI from UI // take the stress of figuring article full URI from UI
// Assemble full URI // Assemble full URI
article["news_link"] = ( baseUrl.isEmpty() ? article["news_link"] = ( baseUrl.isEmpty() ? link : baseUrl + link );
theLink :
baseUrl + theLink );
} }
else if (xml.name() == "summary" || xml.name() == "content"){ else if (xml.name() == "summary" || xml.name() == "content"){
if(double_content) { // Duplicate content -> ignore if(double_content) { // Duplicate content -> ignore
@ -367,13 +376,13 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl,
// Actually works great for non-broken content too // Actually works great for non-broken content too
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements); QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements);
if (!feedText.isEmpty()) if (!feedText.isEmpty())
article["description"] = feedText; article["description"] = feedText.trimmed();
double_content = true; double_content = true;
} }
else if (xml.name() == "updated"){ else if (xml.name() == "updated"){
// ATOM uses standard compliant date, don't do fancy stuff // ATOM uses standard compliant date, don't do fancy stuff
QDateTime articleDate = QDateTime::fromString(xml.readElementText(), Qt::ISODate); QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate);
article["date"] = ( articleDate.isValid() ? article["date"] = ( articleDate.isValid() ?
articleDate : articleDate :
QDateTime::currentDateTime() ); QDateTime::currentDateTime() );
@ -382,15 +391,18 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl,
xml.readNext(); xml.readNext();
while(xml.name() != "author") { while(xml.name() != "author") {
if(xml.name() == "name") if(xml.name() == "name")
article["author"] = xml.readElementText(); article["author"] = xml.readElementText().trimmed();
xml.readNext(); xml.readNext();
} }
} }
else if (xml.name() == "id") else if (xml.name() == "id")
article["id"] = xml.readElementText(); article["id"] = xml.readElementText().trimmed();
} }
} }
if (!article.contains("torrent_url") && article.contains("news_link"))
article["torrent_url"] = article["news_link"];
if (!article.contains("id")) { if (!article.contains("id")) {
// Item does not have a guid, fall back to some other identifier // Item does not have a guid, fall back to some other identifier
const QString link = article.value("news_link").toString(); const QString link = article.value("news_link").toString();