commits
tags
#include "torlinkc/sources/nyaa.hpp"
#include <regex>
#include "torlinkc/sources/magnet.hpp"
#include "torlinkc/sources/rss.hpp"
#include "torlinkc/util/date_parse.hpp"
#include "torlinkc/util/format.hpp"
#include "torlinkc/util/net.hpp"
#include "torlinkc/util/url_encode.hpp"
namespace torlinkc {
namespace {
std::string trim(const std::string& s) {
auto first = s.find_first_not_of(" \t\r\n");
if (first == std::string::npos) return "";
auto last = s.find_last_not_of(" \t\r\n");
return s.substr(first, last - first + 1);
}
std::string toLower(std::string s) {
for (auto& c : s) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
return s;
}
// [\s\S]*? stands in for JS's dotall `.*?` (std::regex has no dotall flag).
std::string tagValue(const std::string& item, const std::string& name) {
const std::regex re("<" + name + R"(>(?:<!\[CDATA\[)?([\s\S]*?)(?:\]\]>)?</)" + name + ">");
std::smatch m;
if (!std::regex_search(item, m, re)) return "";
return trim(m[1].str());
}
int toIntOrZero(const std::string& s) {
try {
return std::stoi(s);
} catch (...) {
return 0;
}
}
} // namespace
Source nyaaSource() {
Source s;
s.id = "nyaa";
s.label = "Nyaa";
s.groups = {SourceGroup::Anime};
s.homepage = "https://nyaa.si";
s.reportsHealth = true;
s.search = [](const std::string& query, const SearchOptions& opts) {
const std::string url = "https://nyaa.si/?page=rss&q=" + encodeURIComponent(trim(query)) + "&c=0_0&f=0";
FetchOptions fetchOpts;
fetchOpts.stopToken = opts.stopToken;
HttpResponse res = fetchResilient(url, fetchOpts);
if (!res.ok()) throw HttpError(res.status, "Nyaa returned " + std::to_string(res.status));
std::vector<TorrentResult> out;
const std::string marker = "<item>";
std::size_t pos = res.body.find(marker);
while (pos != std::string::npos) {
const std::size_t start = pos + marker.size();
const std::size_t next = res.body.find(marker, start);
const std::string item = res.body.substr(start, next == std::string::npos ? std::string::npos : next - start);
pos = next;
const std::string infoHash = toLower(tagValue(item, "nyaa:infoHash"));
const std::string name = unescapeEntities(tagValue(item, "title"));
if (infoHash.empty() || name.empty()) continue;
TorrentResult r;
r.infoHash = infoHash;
r.name = name;
r.sizeBytes = parseSize(tagValue(item, "nyaa:size"));
r.seeders = toIntOrZero(tagValue(item, "nyaa:seeders"));
r.leechers = toIntOrZero(tagValue(item, "nyaa:leechers"));
r.source = "nyaa";
r.magnet = buildMagnet(infoHash, name);
r.added = parseDateToUnixSeconds(tagValue(item, "pubDate"));
out.push_back(std::move(r));
}
return out;
};
return s;
}
} // namespace torlinkc