From 4250cf62c1410d5c4e16720128264aeb59c8dc17 Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Sun, 7 Jun 2015 03:09:55 +0200 Subject: [PATCH] Imported Upstream version 2.3.1 --- src/jsfunctions.cpp | 43 ++++++++++--- src/jsfunctions.h | 14 ++++- src/main.cpp | 10 ++- src/mainwindow.cpp | 10 +-- src/refinesearchwidget.cpp | 3 - src/snapshotsettings.cpp | 4 ++ src/video.cpp | 122 ++++++++----------------------------- src/video.h | 3 - src/videoareawidget.cpp | 2 +- 9 files changed, 92 insertions(+), 119 deletions(-) diff --git a/src/jsfunctions.cpp b/src/jsfunctions.cpp index 961d3f6..b23098f 100644 --- a/src/jsfunctions.cpp +++ b/src/jsfunctions.cpp @@ -40,7 +40,7 @@ JsFunctions::JsFunctions(QObject *parent) : QObject(parent), engine(0) { else qWarning() << file.errorString() << file.fileName(); QFileInfo info(file); - if (info.lastModified().toTime_t() < QDateTime::currentDateTime().toTime_t() - 3600) + if (info.lastModified().toTime_t() < QDateTime::currentDateTime().toTime_t() - 1800) loadJs(); } else { QFile resFile(QLatin1String(":/") + jsFilename()); @@ -75,6 +75,7 @@ const QString & JsFunctions::jsPath() { void JsFunctions::loadJs() { QUrl url(QLatin1String(Constants::WEBSITE) + "-ws/" + jsFilename()); + url.addQueryItem("v", Constants::VERSION); NetworkReply* reply = The::http()->get(url); connect(reply, SIGNAL(data(QByteArray)), SLOT(gotJs(QByteArray))); connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorJs(QNetworkReply*))); @@ -94,21 +95,49 @@ void JsFunctions::errorJs(QNetworkReply *reply) { << reply->url().toString() << reply->errorString(); } -QString JsFunctions::evaluateFunction(const QString &function) { +QString JsFunctions::evaluate(const QString &js) { if (!engine) return QString(); - QScriptValue value = engine->evaluate(function); + QScriptValue value = engine->evaluate(js); if (value.isUndefined()) - qWarning() << "Undefined result for" << function; + qWarning() << "Undefined result for" << js; if (value.isError()) - qWarning() << "Error in" << function << value.toString(); + qWarning() << "Error in" << js << value.toString(); return value.toString(); } QString JsFunctions::decryptSignature(const QString &s) { - return evaluateFunction("decryptSignature('" + s + "')"); + return evaluate("decryptSignature('" + s + "')"); } QString JsFunctions::decryptAgeSignature(const QString &s) { - return evaluateFunction("decryptAgeSignature('" + s + "')"); + return evaluate("decryptAgeSignature('" + s + "')"); +} + +QString JsFunctions::videoIdRE() { + return evaluate("videoIdRE()"); +} + +QString JsFunctions::videoTokenRE() { + return evaluate("videoTokenRE()"); +} + +QString JsFunctions::videoInfoFmtMapRE() { + return evaluate("videoInfoFmtMapRE()"); +} + +QString JsFunctions::webPageFmtMapRE() { + return evaluate("webPageFmtMapRE()"); +} + +QString JsFunctions::ageGateRE() { + return evaluate("ageGateRE()"); +} + +QString JsFunctions::jsPlayerRE() { + return evaluate("jsPlayerRE()"); +} + +QString JsFunctions::signatureFunctionNameRE() { + return evaluate("signatureFunctionNameRE()"); } diff --git a/src/jsfunctions.h b/src/jsfunctions.h index 31d7aac..e623a4b 100644 --- a/src/jsfunctions.h +++ b/src/jsfunctions.h @@ -31,8 +31,21 @@ class JsFunctions : public QObject { public: static JsFunctions* instance(); + + // Specialized functions + // TODO move to subclass QString decryptSignature(const QString &s); QString decryptAgeSignature(const QString &s); + QString videoIdRE(); + QString videoTokenRE(); + QString videoInfoFmtMapRE(); + QString webPageFmtMapRE(); + QString ageGateRE(); + QString jsPlayerRE(); + QString signatureFunctionNameRE(); + +protected: + QString evaluate(const QString &js); private slots: void gotJs(QByteArray bytes); @@ -44,7 +57,6 @@ private: static const QString &jsPath(); void loadJs(); void parseJs(const QString &js); - QString evaluateFunction(const QString &function); QScriptEngine *engine; }; diff --git a/src/main.cpp b/src/main.cpp index 382d91a..91f2ed8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -50,10 +50,14 @@ int main(int argc, char **argv) { if (app.sendMessage(message)) return 0; - app.setApplicationName(QLatin1String(Constants::NAME)); - app.setOrganizationName(QLatin1String(Constants::ORG_NAME)); - app.setOrganizationDomain(QLatin1String(Constants::ORG_DOMAIN)); + app.setApplicationName(Constants::NAME); + app.setOrganizationName(Constants::ORG_NAME); + app.setOrganizationDomain(Constants::ORG_DOMAIN); + app.setApplicationVersion(Constants::VERSION); app.setAttribute(Qt::AA_DontShowIconsInMenus); +#ifndef APP_WIN + app.setWheelScrollLines(1); +#endif #ifdef APP_EXTRA Extra::appSetup(&app); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b03d59a..2795997 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1322,7 +1322,7 @@ void MainWindow::initPhonon() { volumeSlider->setAudioOutput(audioOutput); Phonon::createPath(mediaObject, audioOutput); QSettings settings; - audioOutput->setVolume(settings.value("volume", 1).toDouble()); + audioOutput->setVolume(settings.value("volume", 1).toReal()); // audioOutput->setMuted(settings.value("volumeMute").toBool()); } #endif @@ -1591,12 +1591,14 @@ void MainWindow::gotNewVersion(QString version) { QString checkedVersion = settings.value("checkedVersion").toString(); if (checkedVersion == version) return; -#ifdef APP_SIMPLEUPDATE - simpleUpdateDialog(version); -#elif defined(APP_ACTIVATION) && !defined(APP_MAC) +#ifdef APP_EXTRA +#ifndef APP_MAC UpdateDialog *dialog = new UpdateDialog(version, this); dialog->show(); #endif +#else + simpleUpdateDialog(version); +#endif } void MainWindow::simpleUpdateDialog(QString version) { diff --git a/src/refinesearchwidget.cpp b/src/refinesearchwidget.cpp index 46f4099..cba9079 100644 --- a/src/refinesearchwidget.cpp +++ b/src/refinesearchwidget.cpp @@ -160,9 +160,6 @@ void RefineSearchWidget::setupLabel(QString text, QBoxLayout *layout, QString pa QLabel *icon = new QLabel(this); icon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QString resource = paramName; -#ifdef APP_EXTRA - resource = Extra::resourceName(resource); -#endif QPixmap pixmap = QPixmap(":/images/search-" + resource + ".png"); QPixmap translucentPixmap(pixmap.size()); translucentPixmap.fill(Qt::transparent); diff --git a/src/snapshotsettings.cpp b/src/snapshotsettings.cpp index bfe5275..d9b31ae 100644 --- a/src/snapshotsettings.cpp +++ b/src/snapshotsettings.cpp @@ -84,6 +84,10 @@ QString SnapshotSettings::getCurrentLocation() { } QString SnapshotSettings::displayPath(const QString &path) { +#ifdef APP_MAC + return QDir(path).dirName(); +#endif + #if QT_VERSION >= 0x050000 QString home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); #else diff --git a/src/video.cpp b/src/video.cpp index 64cbf6d..e7c90da 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -35,10 +35,10 @@ static const QString jsNameChars = "a-zA-Z0-9\\$_"; Video::Video() : m_duration(0), m_viewCount(-1), + m_license(LicenseYouTube), definitionCode(0), elIndex(0), ageGate(false), - m_license(LicenseYouTube), loadingStreamUrl(false), loadingThumbnail(false) { } @@ -67,11 +67,8 @@ void Video::setWebpage(QUrl webpage) { m_webpage = webpage; // Get Video ID - // youtube-dl line 428 - // QRegExp re("^((?:http://)?(?:\\w+\\.)?youtube\\.com/(?:(?:v/)|(?:(?:watch(?:\\.php)?)?\\?(?:.+&)?v=)))?([0-9A-Za-z_-]+)(?(1).+)?$"); - QRegExp re("^https?://www\\.youtube\\.com/watch\\?v=([0-9A-Za-z_-]+).*"); - bool match = re.exactMatch(m_webpage.toString()); - if (!match) { + QRegExp re(JsFunctions::instance()->videoIdRE()); + if (re.indexIn(m_webpage.toString()) == -1) { qWarning() << QString("Cannot get video id for %1").arg(m_webpage.toString()); // emit errorStreamUrl(QString("Cannot get video id for %1").arg(m_webpage.toString())); // loadingStreamUrl = false; @@ -163,29 +160,26 @@ void Video::gotVideoInfo(QByteArray data) { // qDebug() << "videoInfo" << videoInfo; // get video token - QRegExp re = QRegExp("^.*&token=([^&]+).*$"); - bool match = re.exactMatch(videoInfo); - // handle regexp failure - if (!match) { - // qDebug() << "Cannot get token. Trying next el param"; + QRegExp videoTokeRE(JsFunctions::instance()->videoTokenRE()); + if (videoTokeRE.indexIn(videoInfo) == -1) { + // qWarning() << "Cannot get token. Trying next el param" << videoInfo << videoTokeRE.pattern(); // Don't panic! We're gonna try another magic "el" param elIndex++; getVideoInfo(); return; } - QString videoToken = re.cap(1); + QString videoToken = videoTokeRE.cap(1); + // qWarning() << "got token" << videoToken; while (videoToken.contains('%')) videoToken = QByteArray::fromPercentEncoding(videoToken.toLatin1()); // qDebug() << "videoToken" << videoToken; this->videoToken = videoToken; // get fmt_url_map - re = QRegExp("^.*&url_encoded_fmt_stream_map=([^&]+).*$"); - match = re.exactMatch(videoInfo); - // handle regexp failure - if (!match) { - // qDebug() << "Cannot get urlMap. Trying next el param"; + QRegExp fmtMapRE(JsFunctions::instance()->videoInfoFmtMapRE()); + if (fmtMapRE.indexIn(videoInfo) == -1) { + // qWarning() << "Cannot get urlMap. Trying next el param"; // Don't panic! We're gonna try another magic "el" param elIndex++; getVideoInfo(); @@ -194,7 +188,8 @@ void Video::gotVideoInfo(QByteArray data) { // qDebug() << "Got token and urlMap" << elIndex; - QString fmtUrlMap = re.cap(1); + QString fmtUrlMap = fmtMapRE.cap(1); + // qWarning() << "got fmtUrlMap" << fmtUrlMap; fmtUrlMap = QByteArray::fromPercentEncoding(fmtUrlMap.toUtf8()); parseFmtUrlMap(fmtUrlMap); } @@ -310,18 +305,6 @@ void Video::parseFmtUrlMap(const QString &fmtUrlMap, bool fromWebPage) { emit errorStreamUrl(tr("Cannot get video stream for %1").arg(m_webpage.toString())); } -void Video::foundVideoUrl(QString videoToken, int definitionCode) { - // qDebug() << "foundVideoUrl" << videoToken << definitionCode; - - QUrl videoUrl = QUrl(QString( - "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=&ps=&asv=&fmt=%3" - ).arg(videoId, videoToken, QString::number(definitionCode))); - - m_streamUrl = videoUrl; - loadingStreamUrl = false; - emit gotStreamUrl(videoUrl); -} - void Video::errorVideoInfo(QNetworkReply *reply) { loadingStreamUrl = false; emit errorStreamUrl(tr("Network error: %1 for %2").arg(reply->errorString(), reply->url().toString())); @@ -331,7 +314,8 @@ void Video::scrapeWebPage(QByteArray data) { QString html = QString::fromUtf8(data); // qWarning() << html; - if (html.contains("player-age-gate-content\"")) { + QRegExp ageGateRE(JsFunctions::instance()->ageGateRE()); + if (ageGateRE.indexIn(html) != -1) { // qDebug() << "Found ageGate"; ageGate = true; elIndex = 4; @@ -339,18 +323,16 @@ void Video::scrapeWebPage(QByteArray data) { return; } - QRegExp re(".*\"url_encoded_fmt_stream_map\":\\s+\"([^\"]+)\".*"); - bool match = re.exactMatch(html); - // on regexp failure, stop and report error - if (!match) { - qWarning() << "Error parsing video page"; + QRegExp fmtMapRE(JsFunctions::instance()->webPageFmtMapRE()); + if (fmtMapRE.indexIn(html) == -1) { + // qWarning() << "Error parsing video page"; // emit errorStreamUrl("Error parsing video page"); // loadingStreamUrl = false; elIndex++; getVideoInfo(); return; } - fmtUrlMap = re.cap(1); + fmtUrlMap = fmtMapRE.cap(1); fmtUrlMap.replace("\\u0026", "&"); // parseFmtUrlMap(fmtUrlMap, true); @@ -367,7 +349,7 @@ void Video::scrapeWebPage(QByteArray data) { } #endif - QRegExp jsPlayerRe("\"assets\":.+\"js\":\\s*\"([^\"]+)\""); + QRegExp jsPlayerRe(JsFunctions::instance()->jsPlayerRE()); if (jsPlayerRe.indexIn(html) != -1) { QString jsPlayerUrl = jsPlayerRe.cap(1); jsPlayerUrl.remove('\\'); @@ -384,55 +366,15 @@ void Video::scrapeWebPage(QByteArray data) { } } -void Video::gotHeadHeaders(QNetworkReply* reply) { - int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - // qDebug() << "gotHeaders" << statusCode; - if (statusCode == 200) { - foundVideoUrl(videoToken, definitionCode); - } else { - - // try next (lower quality) definition - /* - QStringList definitionNames = VideoDefinition::getDefinitionNames(); - int currentIndex = definitionNames.indexOf(currentDefinition); - int previousIndex = 0; - if (currentIndex > 0) { - previousIndex = currentIndex - 1; - } - if (previousIndex > 0) { - QString nextDefinitionName = definitionNames.at(previousIndex); - findVideoUrl(nextDefinitionName); - } else { - foundVideoUrl(videoToken, 18); - }*/ - - - QList definitionCodes = VideoDefinition::getDefinitionCodes(); - int currentIndex = definitionCodes.indexOf(definitionCode); - int previousIndex = 0; - if (currentIndex > 0) { - previousIndex = currentIndex - 1; - int definitionCode = definitionCodes.at(previousIndex); - if (definitionCode == 18) { - // This is assumed always available - foundVideoUrl(videoToken, 18); - } else { - findVideoUrl(definitionCode); - } - - } else { - foundVideoUrl(videoToken, 18); - } - - } -} - void Video::parseJsPlayer(QByteArray bytes) { QString js = QString::fromUtf8(bytes); // qWarning() << "jsPlayer" << js; - QRegExp funcNameRe("signature=([" + jsNameChars + "]+)"); + + // QRegExp funcNameRe("\"signature\"\\w*,\\w*([" + jsNameChars + "]+)"); + QRegExp funcNameRe(JsFunctions::instance()->signatureFunctionNameRE().arg(jsNameChars)); + if (funcNameRe.indexIn(js) == -1) { - qWarning() << "Cannot capture signature function name"; + qWarning() << "Cannot capture signature function name" << js; } else { sigFuncName = funcNameRe.cap(1); captureFunction(sigFuncName, js); @@ -548,20 +490,6 @@ QString Video::decryptSignature(const QString &s) { return value.toString(); } -void Video::findVideoUrl(int definitionCode) { - this->definitionCode = definitionCode; - - QUrl videoUrl = QUrl(QString( - "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=&ps=&asv=&fmt=%3" - ).arg(videoId, videoToken, QString::number(definitionCode))); - - QObject *reply = The::http()->head(videoUrl); - connect(reply, SIGNAL(finished(QNetworkReply*)), SLOT(gotHeadHeaders(QNetworkReply*))); - // connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*))); - - // see you in gotHeadHeaders() -} - QString Video::formattedDuration() const { QString format = m_duration > 3600 ? "h:mm:ss" : "m:ss"; return QTime().addSecs(m_duration).toString(format); diff --git a/src/video.h b/src/video.h index 48b2f1a..3a85233 100644 --- a/src/video.h +++ b/src/video.h @@ -97,14 +97,11 @@ private slots: void gotVideoInfo(QByteArray); void errorVideoInfo(QNetworkReply*); void scrapeWebPage(QByteArray); - void gotHeadHeaders(QNetworkReply*); void parseJsPlayer(QByteArray bytes); void parseDashManifest(QByteArray bytes); private: void getVideoInfo(); - void findVideoUrl(int definitionCode); - void foundVideoUrl(QString videoToken, int definitionCode); void parseFmtUrlMap(const QString &fmtUrlMap, bool fromWebPage = false); void captureFunction(const QString &name, const QString &js); void captureObject(const QString &name, const QString &js); diff --git a/src/videoareawidget.cpp b/src/videoareawidget.cpp index 4adc47e..2c6b055 100644 --- a/src/videoareawidget.cpp +++ b/src/videoareawidget.cpp @@ -138,7 +138,7 @@ void VideoAreaWidget::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton && isNormalWindow) { QPoint p = event->globalPos() - dragPosition; #ifdef Q_OS_MAC - mac::moveWindowTo(window()->winId(), p.x(), p.y()); + // FIXME mac::moveWindowTo(window()->winId(), p.x(), p.y()); #else window()->move(p); #endif -- 2.39.5