From 42b87963fda183d985426b4d1e4d6c31b4e97985 Mon Sep 17 00:00:00 2001 From: ducalex Date: Mon, 29 Apr 2024 14:10:24 -0400 Subject: [PATCH] Add date column to the built-in search engine Adds a date column to the built-in search engine to show when a torrent was published/uploaded on the engine site. When a plugin wants to show a date, it can now add a `pub_date` entry to its result dict. The value format is a unix timestamp (an integer representing seconds since epoch). Plugins with no date support will keep working. PR #20703. --- src/base/search/searchhandler.cpp | 13 +++++++++++-- src/base/search/searchhandler.h | 2 ++ src/gui/search/searchjobwidget.cpp | 2 ++ src/gui/search/searchsortmodel.h | 1 + src/searchengine/nova3/novaprinter.py | 18 +++++++++++------- src/webui/api/searchcontroller.cpp | 5 ++++- src/webui/www/private/scripts/dynamicTable.js | 8 ++++++++ src/webui/www/private/views/search.html | 1 + 8 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/base/search/searchhandler.cpp b/src/base/search/searchhandler.cpp index 2fe4812e9..c5cace333 100644 --- a/src/base/search/searchhandler.cpp +++ b/src/base/search/searchhandler.cpp @@ -55,6 +55,7 @@ namespace PL_LEECHS, PL_ENGINE_URL, PL_DESC_LINK, + PL_PUB_DATE, NB_PLUGIN_COLUMNS }; } @@ -176,7 +177,7 @@ bool SearchHandler::parseSearchResult(const QStringView line, SearchResult &sear const QList parts = line.split(u'|'); const int nbFields = parts.size(); - if (nbFields < (NB_PLUGIN_COLUMNS - 1)) return false; // -1 because desc_link is optional + if (nbFields <= PL_ENGINE_URL) return false; // Anything after ENGINE_URL is optional searchResult = SearchResult(); searchResult.fileUrl = parts.at(PL_DL_LINK).trimmed().toString(); // download URL @@ -194,9 +195,17 @@ bool SearchHandler::parseSearchResult(const QStringView line, SearchResult &sear searchResult.nbLeechers = -1; searchResult.siteUrl = parts.at(PL_ENGINE_URL).trimmed().toString(); // Search site URL - if (nbFields == NB_PLUGIN_COLUMNS) + + if (nbFields > PL_DESC_LINK) searchResult.descrLink = parts.at(PL_DESC_LINK).trimmed().toString(); // Description Link + if (nbFields > PL_PUB_DATE) + { + const qint64 secs = parts.at(PL_PUB_DATE).trimmed().toLongLong(&ok); + if (ok && (secs > 0)) + searchResult.pubDate = QDateTime::fromSecsSinceEpoch(secs); // Date + } + return true; } diff --git a/src/base/search/searchhandler.h b/src/base/search/searchhandler.h index b11a24de7..4085b20fa 100644 --- a/src/base/search/searchhandler.h +++ b/src/base/search/searchhandler.h @@ -30,6 +30,7 @@ #pragma once #include +#include #include #include #include @@ -47,6 +48,7 @@ struct SearchResult qlonglong nbLeechers = 0; QString siteUrl; QString descrLink; + QDateTime pubDate; }; class SearchPluginManager; diff --git a/src/gui/search/searchjobwidget.cpp b/src/gui/search/searchjobwidget.cpp index d2fcc4556..4c86ea4de 100644 --- a/src/gui/search/searchjobwidget.cpp +++ b/src/gui/search/searchjobwidget.cpp @@ -71,6 +71,7 @@ SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, IGUIApplication * m_searchListModel->setHeaderData(SearchSortModel::SEEDS, Qt::Horizontal, tr("Seeders", "i.e: Number of full sources")); m_searchListModel->setHeaderData(SearchSortModel::LEECHES, Qt::Horizontal, tr("Leechers", "i.e: Number of partial sources")); m_searchListModel->setHeaderData(SearchSortModel::ENGINE_URL, Qt::Horizontal, tr("Search engine")); + m_searchListModel->setHeaderData(SearchSortModel::PUB_DATE, Qt::Horizontal, tr("Published On")); // Set columns text alignment m_searchListModel->setHeaderData(SearchSortModel::SIZE, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole); m_searchListModel->setHeaderData(SearchSortModel::SEEDS, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole); @@ -533,6 +534,7 @@ void SearchJobWidget::appendSearchResults(const QVector &results) setModelData(SearchSortModel::SIZE, Utils::Misc::friendlyUnit(result.fileSize), result.fileSize, (Qt::AlignRight | Qt::AlignVCenter)); setModelData(SearchSortModel::SEEDS, QString::number(result.nbSeeders), result.nbSeeders, (Qt::AlignRight | Qt::AlignVCenter)); setModelData(SearchSortModel::LEECHES, QString::number(result.nbLeechers), result.nbLeechers, (Qt::AlignRight | Qt::AlignVCenter)); + setModelData(SearchSortModel::PUB_DATE, QLocale().toString(result.pubDate.toLocalTime(), QLocale::ShortFormat), result.pubDate); } updateResultsCount(); diff --git a/src/gui/search/searchsortmodel.h b/src/gui/search/searchsortmodel.h index 32f239d34..cbd3f8ff2 100644 --- a/src/gui/search/searchsortmodel.h +++ b/src/gui/search/searchsortmodel.h @@ -45,6 +45,7 @@ public: SEEDS, LEECHES, ENGINE_URL, + PUB_DATE, DL_LINK, DESC_LINK, NB_SEARCH_COLUMNS diff --git a/src/searchengine/nova3/novaprinter.py b/src/searchengine/nova3/novaprinter.py index fdd423ba0..80f73aae3 100644 --- a/src/searchengine/nova3/novaprinter.py +++ b/src/searchengine/nova3/novaprinter.py @@ -1,4 +1,4 @@ -#VERSION: 1.47 +#VERSION: 1.48 # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -26,12 +26,16 @@ def prettyPrinter(dictionary): - dictionary['size'] = anySizeToBytes(dictionary['size']) - outtext = "|".join((dictionary["link"], dictionary["name"].replace("|", " "), - str(dictionary["size"]), str(dictionary["seeds"]), - str(dictionary["leech"]), dictionary["engine_url"])) - if 'desc_link' in dictionary: - outtext = "|".join((outtext, dictionary["desc_link"])) + outtext = "|".join(( + dictionary["link"], + dictionary["name"].replace("|", " "), + str(anySizeToBytes(dictionary['size'])), + str(dictionary["seeds"]), + str(dictionary["leech"]), + dictionary["engine_url"], + dictionary.get("desc_link", ""), # Optional + str(dictionary.get("pub_date", -1)), # Optional + )) # fd 1 is stdout with open(1, 'w', encoding='utf-8', closefd=False) as utf8stdout: diff --git a/src/webui/api/searchcontroller.cpp b/src/webui/api/searchcontroller.cpp index f8b1c5664..44a88a7ac 100644 --- a/src/webui/api/searchcontroller.cpp +++ b/src/webui/api/searchcontroller.cpp @@ -39,6 +39,7 @@ #include "base/global.h" #include "base/logger.h" #include "base/search/searchhandler.h" +#include "base/utils/datetime.h" #include "base/utils/foreignapps.h" #include "base/utils/random.h" #include "base/utils/string.h" @@ -301,6 +302,7 @@ int SearchController::generateSearchId() const * - "nbLeechers" * - "siteUrl" * - "descrLink" + * - "pubDate" */ QJsonObject SearchController::getResults(const QList &searchResults, const bool isSearchActive, const int totalResults) const { @@ -315,7 +317,8 @@ QJsonObject SearchController::getResults(const QList &searchResult {u"nbSeeders"_s, searchResult.nbSeeders}, {u"nbLeechers"_s, searchResult.nbLeechers}, {u"siteUrl"_s, searchResult.siteUrl}, - {u"descrLink"_s, searchResult.descrLink} + {u"descrLink"_s, searchResult.descrLink}, + {u"pubDate"_s, Utils::DateTime::toSecsSinceEpoch(searchResult.pubDate)} }; } diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index e96769998..c1aec6372 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -1685,6 +1685,7 @@ window.qBittorrent.DynamicTable = (function() { this.newColumn('nbSeeders', '', 'QBT_TR(Seeders)QBT_TR[CONTEXT=SearchResultsTable]', 100, true); this.newColumn('nbLeechers', '', 'QBT_TR(Leechers)QBT_TR[CONTEXT=SearchResultsTable]', 100, true); this.newColumn('siteUrl', '', 'QBT_TR(Search engine)QBT_TR[CONTEXT=SearchResultsTable]', 250, true); + this.newColumn('pubDate', '', 'QBT_TR(Published On)QBT_TR[CONTEXT=SearchResultsTable]', 200, true); this.initColumnsFunctions(); }, @@ -1701,10 +1702,17 @@ window.qBittorrent.DynamicTable = (function() { td.set('text', formattedValue); td.set('title', formattedValue); }; + const displayDate = function(td, row) { + const value = this.getRowValue(row) * 1000; + const formattedValue = (isNaN(value) || (value <= 0)) ? "" : (new Date(value).toLocaleString()); + td.set('text', formattedValue); + td.set('title', formattedValue); + }; this.columns['fileSize'].updateTd = displaySize; this.columns['nbSeeders'].updateTd = displayNum; this.columns['nbLeechers'].updateTd = displayNum; + this.columns['pubDate'].updateTd = displayDate; }, getFilteredAndSortedRows: function() { diff --git a/src/webui/www/private/views/search.html b/src/webui/www/private/views/search.html index 8f9df7734..cad0a8d0a 100644 --- a/src/webui/www/private/views/search.html +++ b/src/webui/www/private/views/search.html @@ -988,6 +988,7 @@ nbLeechers: result.nbLeechers, nbSeeders: result.nbSeeders, siteUrl: result.siteUrl, + pubDate: result.pubDate, }; newRows.push(row);