mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-06-28 01:16:30 -04:00
parent
71af105a89
commit
41d7d672ce
5 changed files with 92 additions and 48 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
#include "base/utils/bytearray.h"
|
||||||
#include "base/utils/foreignapps.h"
|
#include "base/utils/foreignapps.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "searchpluginmanager.h"
|
#include "searchpluginmanager.h"
|
||||||
|
@ -139,28 +140,23 @@ void SearchHandler::processFinished(const int exitcode)
|
||||||
// line to SearchResult calling parseSearchResult().
|
// line to SearchResult calling parseSearchResult().
|
||||||
void SearchHandler::readSearchOutput()
|
void SearchHandler::readSearchOutput()
|
||||||
{
|
{
|
||||||
QByteArray output = m_searchProcess->readAllStandardOutput();
|
const QByteArray output = m_searchResultLineTruncated + m_searchProcess->readAllStandardOutput();
|
||||||
output.replace('\r', "");
|
QList<QByteArrayView> lines = Utils::ByteArray::splitToViews(output, "\n", Qt::KeepEmptyParts);
|
||||||
|
|
||||||
QList<QByteArray> lines = output.split('\n');
|
m_searchResultLineTruncated = lines.takeLast().trimmed().toByteArray();
|
||||||
if (!m_searchResultLineTruncated.isEmpty())
|
|
||||||
lines.prepend(m_searchResultLineTruncated + lines.takeFirst());
|
|
||||||
m_searchResultLineTruncated = lines.takeLast().trimmed();
|
|
||||||
|
|
||||||
QList<SearchResult> searchResultList;
|
QList<SearchResult> searchResultList;
|
||||||
searchResultList.reserve(lines.size());
|
searchResultList.reserve(lines.size());
|
||||||
|
|
||||||
for (const QByteArray &line : asConst(lines))
|
for (const QByteArrayView &line : asConst(lines))
|
||||||
{
|
{
|
||||||
SearchResult searchResult;
|
if (SearchResult searchResult; parseSearchResult(line, searchResult))
|
||||||
if (parseSearchResult(QString::fromUtf8(line), searchResult))
|
searchResultList.append(std::move(searchResult));
|
||||||
searchResultList << searchResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!searchResultList.isEmpty())
|
if (!searchResultList.isEmpty())
|
||||||
{
|
{
|
||||||
for (const SearchResult &result : searchResultList)
|
m_results.append(searchResultList);
|
||||||
m_results.append(result);
|
|
||||||
emit newSearchResults(searchResultList);
|
emit newSearchResults(searchResultList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,17 +170,17 @@ void SearchHandler::processFailed()
|
||||||
// Parse one line of search results list
|
// Parse one line of search results list
|
||||||
// Line is in the following form:
|
// Line is in the following form:
|
||||||
// file url | file name | file size | nb seeds | nb leechers | Search engine url
|
// file url | file name | file size | nb seeds | nb leechers | Search engine url
|
||||||
bool SearchHandler::parseSearchResult(const QStringView line, SearchResult &searchResult)
|
bool SearchHandler::parseSearchResult(const QByteArrayView line, SearchResult &searchResult)
|
||||||
{
|
{
|
||||||
const QList<QStringView> parts = line.split(u'|');
|
const QList<QByteArrayView> parts = Utils::ByteArray::splitToViews(line, "|");
|
||||||
const int nbFields = parts.size();
|
const int nbFields = parts.size();
|
||||||
|
|
||||||
if (nbFields <= PL_ENGINE_URL)
|
if (nbFields <= PL_ENGINE_URL)
|
||||||
return false; // Anything after ENGINE_URL is optional
|
return false; // Anything after ENGINE_URL is optional
|
||||||
|
|
||||||
searchResult = SearchResult();
|
searchResult = SearchResult();
|
||||||
searchResult.fileUrl = parts.at(PL_DL_LINK).trimmed().toString(); // download URL
|
searchResult.fileUrl = QString::fromUtf8(parts.at(PL_DL_LINK).trimmed()); // download URL
|
||||||
searchResult.fileName = parts.at(PL_NAME).trimmed().toString(); // Name
|
searchResult.fileName = QString::fromUtf8(parts.at(PL_NAME).trimmed()); // Name
|
||||||
searchResult.fileSize = parts.at(PL_SIZE).trimmed().toLongLong(); // Size
|
searchResult.fileSize = parts.at(PL_SIZE).trimmed().toLongLong(); // Size
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
@ -197,11 +193,11 @@ bool SearchHandler::parseSearchResult(const QStringView line, SearchResult &sear
|
||||||
if (!ok || (searchResult.nbLeechers < 0))
|
if (!ok || (searchResult.nbLeechers < 0))
|
||||||
searchResult.nbLeechers = -1;
|
searchResult.nbLeechers = -1;
|
||||||
|
|
||||||
searchResult.siteUrl = parts.at(PL_ENGINE_URL).trimmed().toString(); // Search engine site URL
|
searchResult.siteUrl = QString::fromUtf8(parts.at(PL_ENGINE_URL).trimmed()); // Search engine site URL
|
||||||
searchResult.engineName = m_manager->pluginNameBySiteURL(searchResult.siteUrl); // Search engine name
|
searchResult.engineName = m_manager->pluginNameBySiteURL(searchResult.siteUrl); // Search engine name
|
||||||
|
|
||||||
if (nbFields > PL_DESC_LINK)
|
if (nbFields > PL_DESC_LINK)
|
||||||
searchResult.descrLink = parts.at(PL_DESC_LINK).trimmed().toString(); // Description Link
|
searchResult.descrLink = QString::fromUtf8(parts.at(PL_DESC_LINK).trimmed()); // Description Link
|
||||||
|
|
||||||
if (nbFields > PL_PUB_DATE)
|
if (nbFields > PL_PUB_DATE)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -81,7 +81,7 @@ private:
|
||||||
void readSearchOutput();
|
void readSearchOutput();
|
||||||
void processFailed();
|
void processFailed();
|
||||||
void processFinished(int exitcode);
|
void processFinished(int exitcode);
|
||||||
bool parseSearchResult(QStringView line, SearchResult &searchResult);
|
bool parseSearchResult(QByteArrayView line, SearchResult &searchResult);
|
||||||
|
|
||||||
const QString m_pattern;
|
const QString m_pattern;
|
||||||
const QString m_category;
|
const QString m_category;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2023-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -34,16 +34,32 @@
|
||||||
#include <QByteArrayView>
|
#include <QByteArrayView>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
|
||||||
QList<QByteArrayView> Utils::ByteArray::splitToViews(const QByteArrayView in, const QByteArrayView sep)
|
QList<QByteArrayView> Utils::ByteArray::splitToViews(const QByteArrayView in, const QByteArrayView sep, const Qt::SplitBehavior behavior)
|
||||||
{
|
{
|
||||||
if (in.isEmpty())
|
if (behavior == Qt::SkipEmptyParts)
|
||||||
return {};
|
{
|
||||||
if (sep.isEmpty())
|
if (in.isEmpty())
|
||||||
return {in};
|
return {};
|
||||||
|
|
||||||
|
if (sep.isEmpty())
|
||||||
|
return {in};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (in.isEmpty())
|
||||||
|
{
|
||||||
|
if (sep.isEmpty())
|
||||||
|
return {{}, {}};
|
||||||
|
|
||||||
|
return {{}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const QByteArrayMatcher matcher {sep};
|
const QByteArrayMatcher matcher {sep};
|
||||||
QList<QByteArrayView> ret;
|
QList<QByteArrayView> ret;
|
||||||
ret.reserve(1 + (in.size() / (sep.size() + 1)));
|
ret.reserve((behavior == Qt::SkipEmptyParts)
|
||||||
|
? (1 + (in.size() / (sep.size() + 1)))
|
||||||
|
: (1 + (in.size() / sep.size())));
|
||||||
qsizetype head = 0;
|
qsizetype head = 0;
|
||||||
while (head < in.size())
|
while (head < in.size())
|
||||||
{
|
{
|
||||||
|
@ -51,14 +67,16 @@ QList<QByteArrayView> Utils::ByteArray::splitToViews(const QByteArrayView in, co
|
||||||
if (end < 0)
|
if (end < 0)
|
||||||
end = in.size();
|
end = in.size();
|
||||||
|
|
||||||
// omit empty parts
|
|
||||||
const QByteArrayView part = in.sliced(head, (end - head));
|
const QByteArrayView part = in.sliced(head, (end - head));
|
||||||
if (!part.isEmpty())
|
if (!part.isEmpty() || (behavior == Qt::KeepEmptyParts))
|
||||||
ret += part;
|
ret += part;
|
||||||
|
|
||||||
head = end + sep.size();
|
head = end + sep.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((behavior == Qt::KeepEmptyParts) && (head == in.size()))
|
||||||
|
ret.emplaceBack();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2023-2025 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -37,8 +37,8 @@ class QByteArrayView;
|
||||||
|
|
||||||
namespace Utils::ByteArray
|
namespace Utils::ByteArray
|
||||||
{
|
{
|
||||||
// Inspired by QStringView(in).split(sep, Qt::SkipEmptyParts)
|
// Inspired by QStringView(in).split(sep, behavior)
|
||||||
QList<QByteArrayView> splitToViews(QByteArrayView in, QByteArrayView sep);
|
QList<QByteArrayView> splitToViews(QByteArrayView in, QByteArrayView sep, Qt::SplitBehavior behavior = Qt::SkipEmptyParts);
|
||||||
QByteArray asQByteArray(QByteArrayView view);
|
QByteArray asQByteArray(QByteArrayView view);
|
||||||
|
|
||||||
QByteArray toBase32(const QByteArray &in);
|
QByteArray toBase32(const QByteArray &in);
|
||||||
|
|
|
@ -47,7 +47,7 @@ private slots:
|
||||||
{
|
{
|
||||||
using BAViews = QList<QByteArrayView>;
|
using BAViews = QList<QByteArrayView>;
|
||||||
|
|
||||||
const auto check = [](const QByteArrayView in, const QByteArrayView sep, const BAViews expected)
|
const auto checkSkipEmptyParts = [](const QByteArrayView in, const QByteArrayView sep, const BAViews expected)
|
||||||
{
|
{
|
||||||
// verify it works
|
// verify it works
|
||||||
QCOMPARE(Utils::ByteArray::splitToViews(in, sep), expected);
|
QCOMPARE(Utils::ByteArray::splitToViews(in, sep), expected);
|
||||||
|
@ -56,26 +56,56 @@ private slots:
|
||||||
using Latin1Views = QList<QLatin1StringView>;
|
using Latin1Views = QList<QLatin1StringView>;
|
||||||
|
|
||||||
const Latin1Views reference = QLatin1StringView(in)
|
const Latin1Views reference = QLatin1StringView(in)
|
||||||
.tokenize(QLatin1StringView(sep), Qt::SkipEmptyParts).toContainer();
|
.tokenize(QLatin1StringView(sep), Qt::SkipEmptyParts).toContainer();
|
||||||
Latin1Views expectedStrings;
|
Latin1Views expectedStrings;
|
||||||
for (const auto &string : expected)
|
for (const auto &string : expected)
|
||||||
expectedStrings.append(QLatin1StringView(string));
|
expectedStrings.append(QLatin1StringView(string));
|
||||||
QCOMPARE(reference, expectedStrings);
|
QCOMPARE(reference, expectedStrings);
|
||||||
};
|
};
|
||||||
|
|
||||||
check({}, {}, {});
|
checkSkipEmptyParts({}, {}, {});
|
||||||
check({}, "/", {});
|
checkSkipEmptyParts({}, "/", {});
|
||||||
check("/", "/", {});
|
checkSkipEmptyParts("/", "/", {});
|
||||||
check("/a", "/", {"a"});
|
checkSkipEmptyParts("/a", "/", {"a"});
|
||||||
check("/a/", "/", {"a"});
|
checkSkipEmptyParts("/a/", "/", {"a"});
|
||||||
check("/a/b", "/", (BAViews {"a", "b"}));
|
checkSkipEmptyParts("/a/b", "/", (BAViews {"a", "b"}));
|
||||||
check("/a/b/", "/", (BAViews {"a", "b"}));
|
checkSkipEmptyParts("/a/b/", "/", (BAViews {"a", "b"}));
|
||||||
check("/a/b", "//", {"/a/b"});
|
checkSkipEmptyParts("/a/b", "//", {"/a/b"});
|
||||||
check("//a/b", "//", {"a/b"});
|
checkSkipEmptyParts("//a/b", "//", {"a/b"});
|
||||||
check("//a//b", "//", (BAViews {"a", "b"}));
|
checkSkipEmptyParts("//a//b", "//", (BAViews {"a", "b"}));
|
||||||
check("//a//b/", "//", (BAViews {"a", "b/"}));
|
checkSkipEmptyParts("//a//b/", "//", (BAViews {"a", "b/"}));
|
||||||
check("//a//b//", "//", (BAViews {"a", "b"}));
|
checkSkipEmptyParts("//a//b//", "//", (BAViews {"a", "b"}));
|
||||||
check("///a//b//", "//", (BAViews {"/a", "b"}));
|
checkSkipEmptyParts("///a//b//", "//", (BAViews {"/a", "b"}));
|
||||||
|
|
||||||
|
const auto checkKeepEmptyParts = [](const QByteArrayView in, const QByteArrayView sep, const BAViews expected)
|
||||||
|
{
|
||||||
|
// verify it works
|
||||||
|
QCOMPARE(Utils::ByteArray::splitToViews(in, sep, Qt::KeepEmptyParts), expected);
|
||||||
|
|
||||||
|
// verify it has the same behavior as `split(Qt::KeepEmptyParts)`
|
||||||
|
using Latin1Views = QList<QLatin1StringView>;
|
||||||
|
|
||||||
|
const Latin1Views reference = QLatin1StringView(in)
|
||||||
|
.tokenize(QLatin1StringView(sep), Qt::KeepEmptyParts).toContainer();
|
||||||
|
Latin1Views expectedStrings;
|
||||||
|
for (const auto &string : expected)
|
||||||
|
expectedStrings.append(QLatin1StringView(string));
|
||||||
|
QCOMPARE(reference, expectedStrings);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkKeepEmptyParts({}, {}, {{}, {}});
|
||||||
|
checkKeepEmptyParts({}, "/", {{}});
|
||||||
|
checkKeepEmptyParts("/", "/", {"", ""});
|
||||||
|
checkKeepEmptyParts("/a", "/", {"", "a"});
|
||||||
|
checkKeepEmptyParts("/a/", "/", {"", "a", ""});
|
||||||
|
checkKeepEmptyParts("/a/b", "/", (BAViews {"", "a", "b"}));
|
||||||
|
checkKeepEmptyParts("/a/b/", "/", (BAViews {"", "a", "b", ""}));
|
||||||
|
checkKeepEmptyParts("/a/b", "//", {"/a/b"});
|
||||||
|
checkKeepEmptyParts("//a/b", "//", {"", "a/b"});
|
||||||
|
checkKeepEmptyParts("//a//b", "//", (BAViews {"", "a", "b"}));
|
||||||
|
checkKeepEmptyParts("//a//b/", "//", (BAViews {"", "a", "b/"}));
|
||||||
|
checkKeepEmptyParts("//a//b//", "//", (BAViews {"", "a", "b", ""}));
|
||||||
|
checkKeepEmptyParts("///a//b//", "//", (BAViews {"", "/a", "b", ""}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void testAsQByteArray() const
|
void testAsQByteArray() const
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue