X-Git-Url: https://git.sur5r.net/?p=minitube;a=blobdiff_plain;f=src%2Fyt%2Fytjs%2Fytjssearch.cpp;fp=src%2Fyt%2Fytjs%2Fytjssearch.cpp;h=efdd0a0f51911b56b5633491dd50974376534dfb;hp=0000000000000000000000000000000000000000;hb=9337294c49c89c5cb01db726835da60af566821f;hpb=be5ee6ccd9b29c9d5ca59e4a3d4adf53bac6bc8d diff --git a/src/yt/ytjs/ytjssearch.cpp b/src/yt/ytjs/ytjssearch.cpp new file mode 100644 index 0000000..efdd0a0 --- /dev/null +++ b/src/yt/ytjs/ytjssearch.cpp @@ -0,0 +1,244 @@ +#include "ytjssearch.h" + +#include "mainwindow.h" +#include "searchparams.h" +#include "video.h" +#include "ytsearch.h" + +#include "js.h" + +namespace { + +int parseDuration(const QString &s) { + const auto parts = s.splitRef(':'); + int secs = 0; + int p = 0; + for (auto i = parts.crbegin(); i != parts.crend(); ++i) { + if (p == 0) { + secs = i->toInt(); + } else if (p == 1) { + secs += i->toInt() * 60; + } else if (p == 2) { + secs += i->toInt() * 60 * 60; + } + p++; + } + return secs; +} + +QString parseChannelId(const QString &channelUrl) { + int pos = channelUrl.lastIndexOf('/'); + if (pos >= 0) return channelUrl.mid(pos + 1); + return QString(); +} + +QDateTime parsePublishedText(const QString &s) { + int num = 0; + const auto parts = s.splitRef(' '); + for (const auto &part : parts) { + num = part.toInt(); + if (num > 0) break; + } + if (num == 0) return QDateTime(); + + auto now = QDateTime::currentDateTimeUtc(); + if (s.contains("hour")) { + return now.addSecs(-num * 3600); + } else if (s.contains("day")) { + return now.addDays(-num); + } else if (s.contains("week")) { + return now.addDays(-num * 7); + } else if (s.contains("month")) { + return now.addMonths(-num); + } else if (s.contains("year")) { + return now.addDays(-num * 365); + } + return QDateTime(); +} + +} // namespace + +YTJSSearch::YTJSSearch(SearchParams *searchParams, QObject *parent) + : VideoSource(parent), searchParams(searchParams) {} + +void YTJSSearch::loadVideos(int max, int startIndex) { + aborted = false; + + auto &js = JS::instance(); + auto &engine = js.getEngine(); + + QString q; + if (!searchParams->keywords().isEmpty()) { + if (searchParams->keywords().startsWith("http://") || + searchParams->keywords().startsWith("https://")) { + q = YTSearch::videoIdFromUrl(searchParams->keywords()); + } else + q = searchParams->keywords(); + } + + // Options + + QJSValue options = engine.newObject(); + + if (startIndex > 1) { + if (!nextpageRef.isEmpty()) + options.setProperty("nextpageRef", nextpageRef); + else { + // non-first page was requested but we have no continuation token + emit error("No pagination token"); + return; + } + } + options.setProperty("limit", max); + + switch (searchParams->safeSearch()) { + case SearchParams::None: + options.setProperty("safeSearch", false); + break; + case SearchParams::Strict: + options.setProperty("safeSearch", true); + break; + } + + // Filters + + auto filterMap = engine.evaluate("new Map()"); + auto jsMapSet = filterMap.property("set"); + auto addFilter = [&filterMap, &jsMapSet](QString name, QString value) { + jsMapSet.callWithInstance(filterMap, {name, value}); + }; + + addFilter("Type", "Video"); + + switch (searchParams->sortBy()) { + case SearchParams::SortByNewest: + addFilter("Sort by", "Upload date"); + break; + case SearchParams::SortByViewCount: + addFilter("Sort by", "View count"); + break; + case SearchParams::SortByRating: + addFilter("Sort by", "Rating"); + break; + } + + switch (searchParams->duration()) { + case SearchParams::DurationShort: + addFilter("Duration", "Short"); + break; + case SearchParams::DurationMedium: + case SearchParams::DurationLong: + addFilter("Duration", "Long"); + break; + } + + switch (searchParams->time()) { + case SearchParams::TimeToday: + addFilter("Upload date", "Today"); + break; + case SearchParams::TimeWeek: + addFilter("Upload date", "This week"); + break; + case SearchParams::TimeMonth: + addFilter("Upload date", "This month"); + break; + } + + switch (searchParams->quality()) { + case SearchParams::QualityHD: + addFilter("Features", "HD"); + break; + case SearchParams::Quality4K: + addFilter("Features", "4K"); + break; + case SearchParams::QualityHDR: + addFilter("Features", "HDR"); + break; + } + + js.callFunction(new JSResult(this), "search", {q, options, filterMap}) + .onJson([this](auto &doc) { + if (aborted) return; + + auto obj = doc.object(); + + nextpageRef = obj["nextpageRef"].toString(); + + const auto items = obj["items"].toArray(); + QVector