From c3592304b67d6671f7b655c534e4b40d53a5c559 Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 4 Aug 2015 01:04:00 +0200 Subject: [PATCH 1/9] [RSS] Handle magnet links as torrents instead of news URLs. Closes #3560 --- src/gui/rss/rssparser.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/gui/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp index 9f9b87207..0aaf19582 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/gui/rss/rssparser.cpp @@ -259,8 +259,13 @@ void RssParser::parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl) if (xml.attributes().value("type") == "application/x-bittorrent") article["torrent_url"] = xml.attributes().value("url").toString(); } - else if (xml.name() == "link") - article["news_link"] = xml.readElementText(); + else if (xml.name() == "link") { + 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") article["description"] = xml.readElementText(); else if (xml.name() == "pubDate") @@ -341,17 +346,18 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, article["title"] = doc.toPlainText(); } else if (xml.name() == "link") { - QString theLink = ( xml.attributes().isEmpty() ? - xml.readElementText() : - xml.attributes().value("href").toString() ); + QString link = ( xml.attributes().isEmpty() ? + xml.readElementText().trimmed() : + xml.attributes().value("href").toString() ); - // Atom feeds can have relative links, work around this and - // take the stress of figuring article full URI from UI + 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 + // take the stress of figuring article full URI from UI + // Assemble full URI + article["news_link"] = ( baseUrl.isEmpty() ? link : baseUrl + link ); - // Assemble full URI - article["news_link"] = ( baseUrl.isEmpty() ? - theLink : - baseUrl + theLink ); } else if (xml.name() == "summary" || xml.name() == "content"){ if(double_content) { // Duplicate content -> ignore From b9b778647720b8a1145cf0a3e06d9e52f415a67d Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 4 Aug 2015 01:15:40 +0200 Subject: [PATCH 2/9] [RSS] Trim elements text in RSS articles --- src/gui/rss/rssparser.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gui/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp index 0aaf19582..fe4c5ab5d 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/gui/rss/rssparser.cpp @@ -254,7 +254,7 @@ void RssParser::parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl) if (xml.isStartElement()) { if (xml.name() == "title") - article["title"] = xml.readElementText(); + article["title"] = xml.readElementText().trimmed(); else if (xml.name() == "enclosure") { if (xml.attributes().value("type") == "application/x-bittorrent") article["torrent_url"] = xml.attributes().value("url").toString(); @@ -267,13 +267,13 @@ void RssParser::parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl) article["news_link"] = link; } else if (xml.name() == "description") - article["description"] = xml.readElementText(); + article["description"] = xml.readElementText().trimmed(); else if (xml.name() == "pubDate") - article["date"] = parseDate(xml.readElementText()); + article["date"] = parseDate(xml.readElementText().trimmed()); else if (xml.name() == "author") - article["author"] = xml.readElementText(); + article["author"] = xml.readElementText().trimmed(); else if (xml.name() == "guid") - article["id"] = xml.readElementText(); + article["id"] = xml.readElementText().trimmed(); } } @@ -343,7 +343,7 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, // Workaround for CDATA (QString cannot parse html escapes on it's own) QTextDocument doc; doc.setHtml(xml.readElementText()); - article["title"] = doc.toPlainText(); + article["title"] = doc.toPlainText().trimmed(); } else if (xml.name() == "link") { QString link = ( xml.attributes().isEmpty() ? @@ -373,13 +373,13 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, // Actually works great for non-broken content too QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements); if (!feedText.isEmpty()) - article["description"] = feedText; + article["description"] = feedText.trimmed(); double_content = true; } else if (xml.name() == "updated"){ // 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() ? articleDate : QDateTime::currentDateTime() ); @@ -388,12 +388,12 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, xml.readNext(); while(xml.name() != "author") { if(xml.name() == "name") - article["author"] = xml.readElementText(); + article["author"] = xml.readElementText().trimmed(); xml.readNext(); } } else if (xml.name() == "id") - article["id"] = xml.readElementText(); + article["id"] = xml.readElementText().trimmed(); } } From b78ccf289eea09c8ad677d16874eb0f7b0912e2b Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 4 Aug 2015 01:30:31 +0200 Subject: [PATCH 3/9] [RSS] Fix contextual menu in RSS torrents list --- src/gui/rss/rss_imp.cpp | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index f31edc1fb..8a8e8820d 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -112,20 +112,31 @@ void RSSImp::displayItemsListMenu(const QPoint&) { QMenu myItemListMenu(this); QList selectedItems = listArticles->selectedItems(); - if (selectedItems.size() > 0) { - bool has_attachment = false; - foreach (const QListWidgetItem* item, selectedItems) { - if (m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()) - ->getItem(item->data(Article::IdRole).toString())->hasAttachment()) { - has_attachment = true; - break; - } - } - if (has_attachment) - myItemListMenu.addAction(actionDownload_torrent); - myItemListMenu.addAction(actionOpen_news_URL); + if (selectedItems.size() <= 0) + return; + + bool hasTorrent = false; + bool hasLink = false; + foreach (const QListWidgetItem* item, selectedItems) { + 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; + + if (!article->torrentUrl().isEmpty()) + hasTorrent = true; + if (!article->link().isEmpty()) + hasLink = true; + if (hasTorrent && hasLink) + break; } - myItemListMenu.exec(QCursor::pos()); + if (hasTorrent) + myItemListMenu.addAction(actionDownload_torrent); + if (hasLink) + myItemListMenu.addAction(actionOpen_news_URL); + if (hasTorrent || hasLink) + myItemListMenu.exec(QCursor::pos()); } void RSSImp::on_actionManage_cookies_triggered() From 2955bb5488c178bc0f5649a7fe20c35fa13c2726 Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 4 Aug 2015 02:43:45 +0200 Subject: [PATCH 4/9] [RSS] Improve error handling when a RSS feed doesn't contain torrents --- src/gui/rss/rss_imp.cpp | 2 ++ src/gui/rss/rssarticle.cpp | 4 ++-- src/gui/rss/rssfeed.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index 8a8e8820d..78ddda16a 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -355,6 +355,8 @@ void RSSImp::downloadSelectedTorrents() RssArticlePtr article = feed->getItem(item->data(Article::IdRole).toString()); if (!article) continue; + if (article->torrentUrl().isEmpty()) + continue; if (Preferences::instance()->useAdditionDialog()) AddNewTorrentDialog::show(article->torrentUrl()); else diff --git a/src/gui/rss/rssarticle.cpp b/src/gui/rss/rssarticle.cpp index a0262c1de..fbc7218bf 100644 --- a/src/gui/rss/rssarticle.cpp +++ b/src/gui/rss/rssarticle.cpp @@ -82,7 +82,7 @@ const QString& RssArticle::author() const { } const QString& RssArticle::torrentUrl() const { - return m_torrentUrl.isEmpty() ? m_link : m_torrentUrl; + return m_torrentUrl; } const QString& RssArticle::link() const { @@ -123,6 +123,6 @@ const QString& RssArticle::title() const } void RssArticle::handleTorrentDownloadSuccess(const QString &url) { - if (url == m_torrentUrl || url == m_link) + if (url == m_torrentUrl) markAsRead(); } diff --git a/src/gui/rss/rssfeed.cpp b/src/gui/rss/rssfeed.cpp index 837f5470b..6587e9123 100644 --- a/src/gui/rss/rssfeed.cpp +++ b/src/gui/rss/rssfeed.cpp @@ -364,6 +364,12 @@ void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList* rules, const rules->saveRulesToStorage(); // Download the torrent 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())); connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); From 7c6da307058648a7f3aaaaa26c3a288d630805a1 Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 4 Aug 2015 01:41:34 +0200 Subject: [PATCH 5/9] [RSS] More precise message and code simplification in RSS feeds deletion --- src/gui/rss/rss_imp.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index 78ddda16a..371cc1a97 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -249,21 +249,13 @@ void RSSImp::deleteSelectedItems() QList selectedItems = m_feedList->selectedItems(); if (selectedItems.isEmpty()) return; + if ((selectedItems.size() == 1) && (selectedItems.first() == m_feedList->stickyUnreadItem())) + return; - 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; - 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"), - QString(), 0, 1); - } - if (ret) + QMessageBox::StandardButton answer = QMessageBox::question(this, tr("Deletion confirmation"), + tr("Are you sure you want to delete the selected RSS feeds?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + if (answer == QMessageBox::No) return; foreach (QTreeWidgetItem* item, selectedItems) { From 7c7da446227157028213da5a9273ce75e8b86687 Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 4 Aug 2015 17:21:04 +0200 Subject: [PATCH 6/9] [RSS] Don't hide the elements in Unread list when clicked --- src/gui/rss/rss_imp.cpp | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index 371cc1a97..a9cef41e1 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -547,17 +547,8 @@ void RSSImp::refreshTextBrowser() QListWidgetItem *item = selection.first(); Q_ASSERT(item); 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()); RssArticlePtr article = stream->getItem(item->data(Article::IdRole).toString()); QString html; @@ -680,14 +671,11 @@ void RSSImp::onFeedContentChanged(const QString& url) qDebug() << Q_FUNC_INFO << url; QTreeWidgetItem *item = m_feedList->getTreeItemFromUrl(url); // If the feed is selected, update the displayed news - if (m_feedList->currentItem() == item ) { + if (m_feedList->currentItem() == item) populateArticleList(item); - } - else { - // Update unread items - if (m_feedList->currentItem() == m_feedList->stickyUnreadItem()) - populateArticleList(m_feedList->stickyUnreadItem()); - } + // Update unread items + else if (m_feedList->currentItem() == m_feedList->stickyUnreadItem()) + populateArticleList(m_feedList->stickyUnreadItem()); } void RSSImp::updateRefreshInterval(uint val) From 2eaeead67ca973115983959c9efb518c81fa921a Mon Sep 17 00:00:00 2001 From: ngosang Date: Tue, 4 Aug 2015 17:48:41 +0200 Subject: [PATCH 7/9] [RSS] Allow multiple selection in RSS torrents list --- src/gui/rss/rss.ui | 2 +- src/gui/rss/rss_imp.cpp | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/gui/rss/rss.ui b/src/gui/rss/rss.ui index 7d0d348bb..be0509184 100644 --- a/src/gui/rss/rss.ui +++ b/src/gui/rss/rss.ui @@ -139,7 +139,7 @@ p, li { white-space: pre-wrap; } Qt::CustomContextMenu - QAbstractItemView::SingleSelection + QAbstractItemView::ExtendedSelection QAbstractItemView::SelectItems diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index a9cef41e1..613288a2d 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -340,13 +340,20 @@ void RSSImp::refreshAllFeeds() void RSSImp::downloadSelectedTorrents() { QList selected_items = listArticles->selectedItems(); - foreach (const QListWidgetItem* item, selected_items) { + if (selected_items.size() <= 0) + return; + foreach (QListWidgetItem* item, selected_items) { 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"))); + if (article->torrentUrl().isEmpty()) continue; if (Preferences::instance()->useAdditionDialog()) @@ -354,19 +361,36 @@ void RSSImp::downloadSelectedTorrents() else 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 void RSSImp::openSelectedArticlesUrls() { QList selected_items = listArticles->selectedItems(); - foreach (const QListWidgetItem* item, selected_items) { - RssArticlePtr news = m_feedList->getRSSItemFromUrl(item->data(Article::FeedUrlRole).toString()) - ->getItem(item->data(Article::IdRole).toString()); - const QString link = news->link(); + if (selected_items.size() <= 0) + return; + foreach (QListWidgetItem* item, selected_items) { + 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()) 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 @@ -543,7 +567,6 @@ void RSSImp::refreshTextBrowser() { QList selection = listArticles->selectedItems(); if (selection.empty()) return; - Q_ASSERT(selection.size() == 1); QListWidgetItem *item = selection.first(); Q_ASSERT(item); if (item == m_currentArticle) return; @@ -708,8 +731,6 @@ RSSImp::RSSImp(QWidget *parent): m_feedList = new FeedListWidget(splitter_h, m_rssManager); 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); connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRssFile())); connect(m_feedList, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRssFile())); From b6c707c0808b46ea9463e0d9297858aa4ab79755 Mon Sep 17 00:00:00 2001 From: ngosang Date: Wed, 2 Sep 2015 20:00:38 +0200 Subject: [PATCH 8/9] [RSS] Simplify string translation --- src/gui/rss/rss.ui | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/gui/rss/rss.ui b/src/gui/rss/rss.ui index be0509184..d83b562fa 100644 --- a/src/gui/rss/rss.ui +++ b/src/gui/rss/rss.ui @@ -116,16 +116,12 @@ - 50 - false + 75 + true - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Torrents:</span> <span style=" font-style:italic;">(double-click to download)</span></p></body></html> + Torrents: (double-click to download) From ee21562426640324137b137188cf6929c269000a Mon Sep 17 00:00:00 2001 From: ngosang Date: Wed, 2 Sep 2015 20:10:44 +0200 Subject: [PATCH 9/9] [RSS] Handle more types of RSS feeds --- src/gui/rss/rssparser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp index fe4c5ab5d..fb0932d59 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/gui/rss/rssparser.cpp @@ -277,6 +277,9 @@ void RssParser::parseRssArticle(QXmlStreamReader& xml, const QString& feedUrl) } } + if (!article.contains("torrent_url") && article.contains("news_link")) + article["torrent_url"] = article["news_link"]; + if (!article.contains("id")) { // Item does not have a guid, fall back to some other identifier const QString link = article.value("news_link").toString(); @@ -397,6 +400,9 @@ void RssParser::parseAtomArticle(QXmlStreamReader& xml, const QString& feedUrl, } } + if (!article.contains("torrent_url") && article.contains("news_link")) + article["torrent_url"] = article["news_link"]; + if (!article.contains("id")) { // Item does not have a guid, fall back to some other identifier const QString link = article.value("news_link").toString();