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());
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*)));
<< 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()");
}
Video::Video() : m_duration(0),
m_viewCount(-1),
+ m_license(LicenseYouTube),
definitionCode(0),
elIndex(0),
ageGate(false),
- m_license(LicenseYouTube),
loadingStreamUrl(false),
loadingThumbnail(false)
{ }
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;
// 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();
// 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);
}
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()));
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;
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);
}
#endif
- QRegExp jsPlayerRe("\"assets\":.+\"js\":\\s*\"([^\"]+)\"");
+ QRegExp jsPlayerRe(JsFunctions::instance()->jsPlayerRE());
if (jsPlayerRe.indexIn(html) != -1) {
QString jsPlayerUrl = jsPlayerRe.cap(1);
jsPlayerUrl.remove('\\');
}
}
-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<int> 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);
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);