From 075bf32cc8fbcf979ba3cebc4ddb6c10a2005039 Mon Sep 17 00:00:00 2001 From: Gerasim Troeglazov <3dEyes@gmail.com> Date: Sat, 5 Dec 2020 11:55:21 +1000 Subject: [PATCH] QMPlay2: youtube fixes --- .../qmplay2/patches/qmplay2-20.07.04.patchset | 282 +++++++++++++++++- media-video/qmplay2/qmplay2-20.07.04.recipe | 2 +- 2 files changed, 278 insertions(+), 6 deletions(-) diff --git a/media-video/qmplay2/patches/qmplay2-20.07.04.patchset b/media-video/qmplay2/patches/qmplay2-20.07.04.patchset index 282d8b1f5..8a41f9491 100644 --- a/media-video/qmplay2/patches/qmplay2-20.07.04.patchset +++ b/media-video/qmplay2/patches/qmplay2-20.07.04.patchset @@ -1,4 +1,4 @@ -From d9031acc4ae269e8db40b793d9ae974c009055d2 Mon Sep 17 00:00:00 2001 +From b421b98416a2dbb4cea7c3b89c65b2ff66efc752 Mon Sep 17 00:00:00 2001 From: Gerasim Troeglazov <3dEyes@gmail.com> Date: Sun, 3 May 2020 11:29:15 +1000 Subject: Add haiku support @@ -733,10 +733,10 @@ index 0000000..24b4ebd + MediaKit.png + diff --git a/src/qmplay2/QMPlay2Core.cpp b/src/qmplay2/QMPlay2Core.cpp -index 5cf8f56..4b48fe4 100644 +index 092dcde..bed9ff7 100644 --- a/src/qmplay2/QMPlay2Core.cpp +++ b/src/qmplay2/QMPlay2Core.cpp -@@ -210,7 +210,7 @@ void QMPlay2CoreClass::init(bool loadModules, bool modulesInSubdirs, const QStri +@@ -204,7 +204,7 @@ void QMPlay2CoreClass::init(bool loadModules, bool modulesInSubdirs, const QStri settingsDir = QCoreApplication::applicationDirPath() + "/settings/"; else { @@ -745,7 +745,7 @@ index 5cf8f56..4b48fe4 100644 settingsDir = QFileInfo(QSettings(QSettings::IniFormat, QSettings::UserScope, QString()).fileName()).absolutePath() + "/QMPlay2/"; #elif defined(Q_OS_MACOS) settingsDir = Functions::cleanPath(QStandardPaths::standardLocations(QStandardPaths::DataLocation).value(0, settingsDir)); -@@ -409,6 +409,11 @@ QStringList QMPlay2CoreClass::getModules(const QString &type, int typeLen) const +@@ -460,6 +460,11 @@ QStringList QMPlay2CoreClass::getModules(const QString &type, int typeLen) const #elif defined Q_OS_WIN if (type == "videoWriters") defaultModules << "OpenGL 2" << "DirectDraw"; @@ -758,5 +758,277 @@ index 5cf8f56..4b48fe4 100644 if (type == "decoders") defaultModules << "FFmpeg Decoder"; -- -2.26.0 +2.28.0 + + +From 323101a7c0b3ff43ccaabc46027d60ee5311ca1d Mon Sep 17 00:00:00 2001 +From: Gerasim Troeglazov <3dEyes@gmail.com> +Date: Sat, 5 Dec 2020 11:46:40 +1000 +Subject: YouTube fixes from upstream + + +diff --git a/src/modules/Extensions/YouTube.cpp b/src/modules/Extensions/YouTube.cpp +index 8f16779..a12ba35 100644 +--- a/src/modules/Extensions/YouTube.cpp ++++ b/src/modules/Extensions/YouTube.cpp +@@ -18,6 +18,7 @@ + + #include + ++#include + #include + #include + +@@ -754,90 +755,96 @@ void YouTube::setSearchResults(const QByteArray &data) + { + const auto json = getYtInitialData(data); + +- const auto contents = json.object() ++ const auto sectionListRendererContents = json.object() + ["contents"].toObject() + ["twoColumnSearchResultsRenderer"].toObject() + ["primaryContents"].toObject() + ["sectionListRenderer"].toObject() +- ["contents"].toArray().at(0).toObject() +- ["itemSectionRenderer"].toObject() + ["contents"].toArray() + ; + +- for (auto &&obj : contents) ++ for (auto &&obj : sectionListRendererContents) + { +- const auto videoRenderer = obj.toObject()["videoRenderer"].toObject(); +- const auto playlistRenderer = obj.toObject()["playlistRenderer"].toObject(); +- +- const bool isVideo = !videoRenderer.isEmpty() && playlistRenderer.isEmpty(); +- +- QString title, contentId, length, user, publishedTime, viewCount, thumbnail, url; ++ const auto contents = obj.toObject() ++ ["itemSectionRenderer"].toObject() ++ ["contents"].toArray() ++ ; + +- if (isVideo) ++ for (auto &&obj : contents) + { +- title = videoRenderer["title"].toObject()["runs"].toArray().at(0).toObject()["text"].toString(); +- contentId = videoRenderer["videoId"].toString(); +- if (title.isEmpty() || contentId.isEmpty()) +- continue; +- +- length = videoRenderer["lengthText"].toObject()["simpleText"].toString(); +- user = videoRenderer["ownerText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString(); +- publishedTime = videoRenderer["publishedTimeText"].toObject()["simpleText"].toString(); +- viewCount = videoRenderer["shortViewCountText"].toObject()["simpleText"].toString(); +- thumbnail = videoRenderer["thumbnail"].toObject()["thumbnails"].toArray().at(0).toObject()["url"].toString(); ++ const auto videoRenderer = obj.toObject()["videoRenderer"].toObject(); ++ const auto playlistRenderer = obj.toObject()["playlistRenderer"].toObject(); + +- url = YOUTUBE_URL "/watch?v=" + contentId; +- } +- else +- { +- title = playlistRenderer["title"].toObject()["simpleText"].toString(); +- contentId = playlistRenderer["playlistId"].toString(); +- if (title.isEmpty() || contentId.isEmpty()) +- continue; ++ const bool isVideo = !videoRenderer.isEmpty() && playlistRenderer.isEmpty(); + +- user = playlistRenderer["longBylineText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString(); +- thumbnail = playlistRenderer +- ["thumbnailRenderer"].toObject() +- ["playlistVideoThumbnailRenderer"].toObject() +- ["thumbnail"].toObject() +- ["thumbnails"].toArray().at(0).toObject() +- ["url"].toString() +- ; ++ QString title, contentId, length, user, publishedTime, viewCount, thumbnail, url; + +- url = YOUTUBE_URL "/playlist?list=" + contentId; +- } ++ if (isVideo) ++ { ++ title = videoRenderer["title"].toObject()["runs"].toArray().at(0).toObject()["text"].toString(); ++ contentId = videoRenderer["videoId"].toString(); ++ if (title.isEmpty() || contentId.isEmpty()) ++ continue; ++ ++ length = videoRenderer["lengthText"].toObject()["simpleText"].toString(); ++ user = videoRenderer["ownerText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString(); ++ publishedTime = videoRenderer["publishedTimeText"].toObject()["simpleText"].toString(); ++ viewCount = videoRenderer["shortViewCountText"].toObject()["simpleText"].toString(); ++ thumbnail = videoRenderer["thumbnail"].toObject()["thumbnails"].toArray().at(0).toObject()["url"].toString(); ++ ++ url = YOUTUBE_URL "/watch?v=" + contentId; ++ } ++ else ++ { ++ title = playlistRenderer["title"].toObject()["simpleText"].toString(); ++ contentId = playlistRenderer["playlistId"].toString(); ++ if (title.isEmpty() || contentId.isEmpty()) ++ continue; ++ ++ user = playlistRenderer["longBylineText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString(); ++ thumbnail = playlistRenderer ++ ["thumbnailRenderer"].toObject() ++ ["playlistVideoThumbnailRenderer"].toObject() ++ ["thumbnail"].toObject() ++ ["thumbnails"].toArray().at(0).toObject() ++ ["url"].toString() ++ ; ++ ++ url = YOUTUBE_URL "/playlist?list=" + contentId; ++ } + +- auto tWI = new QTreeWidgetItem(resultsW); ++ auto tWI = new QTreeWidgetItem(resultsW); + +- tWI->setText(0, title); +- tWI->setText(1, isVideo ? length : tr("Playlist")); +- tWI->setText(2, user); ++ tWI->setText(0, title); ++ tWI->setText(1, isVideo ? length : tr("Playlist")); ++ tWI->setText(2, user); + +- QString tooltip; +- tooltip += QString("%1: %2\n").arg(resultsW->headerItem()->text(0), tWI->text(0)); +- tooltip += QString("%1: %2\n").arg(isVideo ? resultsW->headerItem()->text(1) : tr("Playlist"), isVideo ? tWI->text(1) : tr("yes")); +- tooltip += QString("%1: %2\n").arg(resultsW->headerItem()->text(2), tWI->text(2)); +- tooltip += QString("%1: %2\n").arg(tr("Published time"), publishedTime); +- tooltip += QString("%1: %2").arg(tr("View count"), viewCount); +- tWI->setToolTip(0, tooltip); ++ QString tooltip; ++ tooltip += QString("%1: %2\n").arg(resultsW->headerItem()->text(0), tWI->text(0)); ++ tooltip += QString("%1: %2\n").arg(isVideo ? resultsW->headerItem()->text(1) : tr("Playlist"), isVideo ? tWI->text(1) : tr("yes")); ++ tooltip += QString("%1: %2\n").arg(resultsW->headerItem()->text(2), tWI->text(2)); ++ tooltip += QString("%1: %2\n").arg(tr("Published time"), publishedTime); ++ tooltip += QString("%1: %2").arg(tr("View count"), viewCount); ++ tWI->setToolTip(0, tooltip); + +- tWI->setData(0, Qt::UserRole, url); +- tWI->setData(1, Qt::UserRole, !isVideo); ++ tWI->setData(0, Qt::UserRole, url); ++ tWI->setData(1, Qt::UserRole, !isVideo); + +- if (!isVideo) +- { +- tWI->setDisabled(true); ++ if (!isVideo) ++ { ++ tWI->setDisabled(true); + +- auto linkReply = net.start(url); +- linkReply->setProperty("tWI", QVariant::fromValue((void *)tWI)); +- linkReplies += linkReply; +- } ++ auto linkReply = net.start(url); ++ linkReply->setProperty("tWI", QVariant::fromValue((void *)tWI)); ++ linkReplies += linkReply; ++ } + +- if (!thumbnail.isEmpty()) +- { +- auto imageReply = net.start(thumbnail); +- imageReply->setProperty("tWI", QVariant::fromValue((void *)tWI)); +- imageReplies += imageReply; ++ if (!thumbnail.isEmpty()) ++ { ++ auto imageReply = net.start(thumbnail); ++ imageReply->setProperty("tWI", QVariant::fromValue((void *)tWI)); ++ imageReplies += imageReply; ++ } + } + } + +@@ -1042,7 +1049,7 @@ void YouTube::preparePlaylist(const QByteArray &data, QTreeWidgetItem *tWI) + { + const auto playlistRenderer = obj.toObject()["playlistVideoRenderer"].toObject(); + +- const auto title = playlistRenderer["title"].toObject()["simpleText"].toString(); ++ const auto title = playlistRenderer["title"].toObject()["runs"].toArray().at(0).toObject()["text"].toString(); + const auto videoId = playlistRenderer["videoId"].toString(); + if (title.isEmpty() || videoId.isEmpty()) + continue; +@@ -1070,13 +1077,10 @@ QJsonDocument YouTube::getYtInitialData(const QByteArray &data) + if (idx < 0) + return QJsonDocument(); + +- int idx2 = data.indexOf("\n", idx); ++ int idx2 = Functions::findJsonEnd(data, idx); + if (idx2 < 0) + return QJsonDocument(); + +- auto jsonData = data.mid(idx, idx2 - idx); +- if (jsonData.endsWith(';')) +- jsonData.chop(1); +- ++ const auto jsonData = data.mid(idx, idx2 - idx); + return QJsonDocument::fromJson(jsonData); + } +diff --git a/src/qmplay2/Functions.cpp b/src/qmplay2/Functions.cpp +index 7319a2b..dce416c 100644 +--- a/src/qmplay2/Functions.cpp ++++ b/src/qmplay2/Functions.cpp +@@ -940,6 +940,46 @@ QByteArray Functions::textWithFallbackEncoding(const QByteArray &data) + return data; + } + ++int Functions::findJsonEnd(const QByteArray &data, int idx) ++{ ++ const int dataLen = data.length(); ++ ++ if (dataLen < 1 || idx < 0 || idx >= dataLen || data.at(idx) != '{') ++ return -1; ++ ++ int brackets = 1; ++ bool inString = false; ++ char prevChr = '\0'; ++ ++ for (int i = idx + 1; i < dataLen; ++i) ++ { ++ const char chr = data.at(i); ++ ++ if (chr == '"') ++ { ++ if (!inString) ++ inString = true; ++ else if (prevChr != '\\') ++ inString = false; ++ } ++ ++ prevChr = chr; ++ ++ if (inString) ++ continue; ++ ++ if (chr == '{') ++ ++brackets; ++ else if (chr == '}') ++ --brackets; ++ ++ if (brackets == 0) ++ return i + 1; ++ } ++ ++ return -1; ++} ++ + Functions::LumaCoefficients Functions::getLumaCoeff(AVColorSpace colorSpace) + { + switch (colorSpace) +diff --git a/src/qmplay2/Functions.hpp b/src/qmplay2/Functions.hpp +index 395b9fd..0187a50 100644 +--- a/src/qmplay2/Functions.hpp ++++ b/src/qmplay2/Functions.hpp +@@ -149,6 +149,8 @@ namespace Functions + + QMPLAY2SHAREDLIB_EXPORT QByteArray textWithFallbackEncoding(const QByteArray &data); + ++ QMPLAY2SHAREDLIB_EXPORT int findJsonEnd(const QByteArray &data, int idx = 0); ++ + struct LumaCoefficients + { + float cR, cG, cB; +-- +2.28.0 diff --git a/media-video/qmplay2/qmplay2-20.07.04.recipe b/media-video/qmplay2/qmplay2-20.07.04.recipe index b698c69a8..2ad6aa2b2 100644 --- a/media-video/qmplay2/qmplay2-20.07.04.recipe +++ b/media-video/qmplay2/qmplay2-20.07.04.recipe @@ -6,7 +6,7 @@ MyFreeMP3 browser." HOMEPAGE="http://zaps166.sourceforge.net" COPYRIGHT="2010-2020 Błażej Szczygieł" LICENSE="GNU GPL v3" -REVISION="1" +REVISION="2" SOURCE_URI="https://github.com/zaps166/QMPlay2/archive/$portVersion.tar.gz" CHECKSUM_SHA256="5f30785e215da6e106304b2a0a94c84e4cb679e51b2119fa2dd9f24e91b740cb" SOURCE_DIR="QMPlay2-$portVersion"