- Timestamp:
- May 3, 2016, 2:14:41 PM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
smplayer/vendor/current/src/youtube/retrieveyoutubeurl.cpp
r168 r175 1 1 /* smplayer, GUI front-end for mplayer. 2 Copyright (C) 2006-2014 Ricardo Villalba <[email protected]> 3 Copyright (C) 2010 Ori Rejwan 2 Copyright (C) 2006-2016 Ricardo Villalba <[email protected]> 4 3 5 4 This program is free software; you can redistribute it and/or modify … … 19 18 20 19 #include "retrieveyoutubeurl.h" 20 21 21 22 #include <QUrl> 23 22 24 #include <QRegExp> 23 25 #include <QStringList> 24 #include <QFile> 26 #include <QSslSocket> 27 28 #ifdef YT_USE_YTSIG 25 29 #include "ytsig.h" 30 26 31 27 32 #if QT_VERSION >= 0x050000 … … 29 34 #endif 30 35 31 #define USE_PLAYER_NAME 32 33 QString RetrieveYoutubeUrl::user_agent; 34 bool RetrieveYoutubeUrl::use_https_main = false; 35 bool RetrieveYoutubeUrl::use_https_vi = false; 36 37 RetrieveYoutubeUrl::RetrieveYoutubeUrl( QObject* parent ) : QObject(parent) 36 37 RetrieveYoutubeUrl::RetrieveYoutubeUrl(QObject* parent) 38 : QObject(parent) 39 #ifdef YT_USE_SIG 40 , set(0) 41 #endif 42 , preferred_quality(MP4_360p) 43 , use_https_main(false) 44 , use_https_vi(false) 38 45 { 39 reply = 0;40 46 manager = new QNetworkAccessManager(this); 41 47 42 preferred_quality = FLV_360p; 48 dl_video_page = new LoadPage(manager, this); 49 connect(dl_video_page, SIGNAL(pageLoaded(QByteArray)), this, SLOT(videoPageLoaded(QByteArray))); 50 connect(dl_video_page, SIGNAL(errorOcurred(int, QString)), this, SIGNAL(errorOcurred(int, QString))); 51 52 #ifdef YT_GET_VIDEOINFO 53 dl_video_info_page = new LoadPage(manager, this); 54 connect(dl_video_info_page, SIGNAL(pageLoaded(QByteArray)), this, SLOT(videoInfoPageLoaded(QByteArray))); 55 connect(dl_video_info_page, SIGNAL(errorOcurred(int, QString)), this, SIGNAL(errorOcurred(int, QString))); 56 #endif 57 58 #ifdef YT_USE_SIG 59 dl_player_page = new LoadPage(manager, this); 60 connect(dl_player_page, SIGNAL(pageLoaded(QByteArray)), this, SLOT(playerPageLoaded(QByteArray))); 61 connect(dl_player_page, SIGNAL(errorOcurred(int, QString)), this, SIGNAL(errorOcurred(int, QString))); 62 #endif 63 64 #ifdef YT_LIVE_STREAM 65 dl_stream_page = new LoadPage(manager, this); 66 connect(dl_stream_page, SIGNAL(pageLoaded(QByteArray)), this, SLOT(streamPageLoaded(QByteArray))); 67 connect(dl_stream_page, SIGNAL(errorOcurred(int, QString)), this, SIGNAL(errorOcurred(int, QString))); 68 #endif 43 69 } 44 70 … … 46 72 } 47 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 48 155 void RetrieveYoutubeUrl::fetchPage(const QString & url) { 49 qDebug("RetrieveYoutubeUrl::fetchPage: url: %s", url.toUtf8().constData()); 50 qDebug("RetrieveYoutubeUrl::fetchPage: user agent: '%s'", user_agent.toLatin1().constData()); 51 52 QNetworkRequest req(url); 53 req.setRawHeader("User-Agent", user_agent.toLatin1()); 54 req.setRawHeader("Accept-Language", "en-us,en;q=0.5"); 55 reply = manager->get(req); 56 connect(reply, SIGNAL(finished()), this, SLOT(gotResponse())); 57 orig_url = url; 156 yt_url = url; 157 fetchVideoPage(url); 158 //fetchVideoInfoPage(url); 159 } 160 161 void RetrieveYoutubeUrl::fetchVideoPage(const QString & url) { 162 qDebug() << "RetrieveYoutubeUrl::fetchVideoPage: url:" << url; 163 164 if (url.toLower().startsWith("https") && !QSslSocket::supportsSsl()) { 165 qDebug() << "RetrieveYoutubeUrl::fetchVideoPage: no support for ssl"; 166 emit noSslSupport(); 167 return; 168 } 169 170 dl_video_page->fetchPage(url); 58 171 59 172 emit connecting(QUrl(url).host()); 173 60 174 61 175 #ifdef YT_GET_VIDEOINFO 176 62 177 video_id = getVideoID(url); 63 #endif 64 } 65 66 #ifdef YT_GET_VIDEOINFO 67 void RetrieveYoutubeUrl::fetchVideoInfoPage(QString url) { 68 if (url.isEmpty()) { 69 QString scheme = use_https_vi ? "https" : "http"; 70 url = QString("%2://www.youtube.com/get_video_info?el=detailpage&ps=default&eurl=&gl=US&hl=en&video_id=%1").arg(video_id).arg(scheme); 71 } 72 qDebug("RetrieveYoutubeUrl::fetchVideoInfoPage: url: %s...", url.left(20).toUtf8().constData()); 73 74 qDebug("RetrieveYoutubeUrl::fetchPage: user agent: '%s'", user_agent.toLatin1().constData()); 75 76 YTSig::check(url); 77 QNetworkRequest req(url); 78 req.setRawHeader("User-Agent", user_agent.toLatin1()); 79 req.setRawHeader("Accept-Language", "en-us,en;q=0.5"); 80 reply = manager->get(req); 81 connect(reply, SIGNAL(finished()), this, SLOT(gotVideoInfoResponse())); 82 83 emit connecting(QUrl(url).host()); 84 } 85 #endif 86 87 void RetrieveYoutubeUrl::close() { 88 if (reply) reply->abort(); 89 } 90 91 void RetrieveYoutubeUrl::gotResponse() { 92 qDebug("RetrieveYoutubeUrl::gotResponse"); 93 94 QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); 95 96 if (reply->error() == QNetworkReply::NoError) { 97 int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); 98 qDebug("RetrieveYoutubeUrl::gotResponse: status: %d", status); 99 switch (status) { 100 case 301: 101 case 302: 102 case 307: 103 QString r_url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl().toString(); 104 qDebug("RetrieveYoutubeUrl::gotResponse: redirected: %s", r_url.toLatin1().constData()); 105 fetchPage(r_url); 106 return; 107 } 108 } else { 109 qDebug("RetrieveYoutubeUrl::gotResponse: error %d: '%s'", (int)reply->error(), reply->errorString().toUtf8().constData()); 110 emit errorOcurred((int)reply->error(), reply->errorString()); 178 179 QString scheme = use_https_vi ? "https" : "http"; 180 QString u = QString("%2://www.youtube.com/get_video_info?video_id=%1&el=vevo&ps=default&eurl=&gl=US&hl=en").arg(video_id).arg(scheme); 181 182 qDebug() << "RetrieveYoutubeUrl::fetchVideoInfoPage: url:" << url.left(20); 183 184 if (u.toLower().startsWith("https") && !QSslSocket::supportsSsl()) { 185 qDebug() << "RetrieveYoutubeUrl::fetchVideoInfoPage: no support for ssl"; 186 emit noSslSupport(); 111 187 return; 112 188 } 113 parse(reply->readAll()); 114 } 115 116 #ifdef YT_GET_VIDEOINFO 117 void RetrieveYoutubeUrl::gotVideoInfoResponse() { 118 qDebug("RetrieveYoutubeUrl::gotVideoInfoResponse"); 119 120 QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); 121 122 if (reply->error() == QNetworkReply::NoError) { 123 int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); 124 qDebug("RetrieveYoutubeUrl::gotVideoInfoResponse: status: %d", status); 125 switch (status) { 126 case 301: 127 case 302: 128 case 307: 129 QString r_url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl().toString(); 130 //qDebug("RetrieveYoutubeUrl::gotVideoInfoResponse: redirected: %s", r_url.toLatin1().constData()); 131 fetchVideoInfoPage(r_url); 132 return; 133 } 134 } else { 135 qDebug("RetrieveYoutubeUrl::gotVideoInfoResponse: error %d: '%s'", (int)reply->error(), reply->errorString().toUtf8().constData()); 136 emit errorOcurred((int)reply->error(), reply->errorString()); 137 return; 138 } 139 parseVideoInfo(reply->readAll()); 140 } 141 #endif 142 143 void RetrieveYoutubeUrl::parse(QByteArray text) { 144 qDebug("RetrieveYoutubeUrl::parse"); 145 146 urlMap.clear(); 147 148 QString replyString = QString::fromUtf8(text); 189 190 #if defined(YT_USE_YTSIG) && !defined(YT_USE_SIG) 191 YTSig::check(u); 192 #endif 193 194 #ifdef YT_USE_SIG 195 if (!sig.sts.isEmpty()) { 196 u = u + "&sts=" + sig.sts; 197 } 198 #ifdef YT_USE_YTSIG 199 else { 200 YTSig::check(u); 201 } 202 #endif 203 #endif 204 205 dl_video_info_page->fetchPage(u); 206 207 emit connecting(QUrl(u).host()); 208 } 209 #endif 210 211 #ifdef YT_USE_SIG 212 void RetrieveYoutubeUrl::fetchPlayerPage(const QString & player_name) { 213 qDebug() << "RetrieveYoutubeUrl::fetchPlayerPage:" << player_name; 214 215 if (!player_name.isEmpty()) { 216 QString url; 217 /* 218 if (player_name.startsWith("new-")) { 219 url = QString("http://s.ytimg.com/yts/jsbin/html5player-%1/html5player-new.js").arg(player_name); 220 } else { 221 url = QString("http://s.ytimg.com/yts/jsbin/html5player-%1/html5player.js").arg(player_name); 222 } 223 */ 224 url = QString("http://s.ytimg.com/yts/jsbin/player-%1/base.js").arg(player_name); 225 qDebug() << "RetrieveYoutubeUrl::fetchPlayerPage: url:" << url; 226 dl_player_page->fetchPage(url); 227 } 228 } 229 #endif 230 231 #ifdef YT_LIVE_STREAM 232 void RetrieveYoutubeUrl::fetchStreamPage(const QString & url) { 233 qDebug() << "RetrieveYoutubeUrl::fetchStreamPage:" << url; 234 dl_stream_page->fetchPage(url); 235 } 236 #endif 237 238 void RetrieveYoutubeUrl::videoPageLoaded(QByteArray page) { 239 qDebug() << "RetrieveYoutubeUrl::videoPageLoaded"; 240 241 QString replyString = QString::fromUtf8(page); 149 242 150 243 QRegExp rx_title(".*<title>(.*)</title>.*"); 151 244 if (rx_title.indexIn(replyString) != -1) { 152 245 url_title = rx_title.cap(1).simplified(); 153 url_title = QString(url_title).replace("&","&").replace(">", ">").replace("<", "<").replace(""","\"").replace("'","'") /*.replace(" - YouTube", "")*/;154 qDebug( "RetrieveYoutubeUrl::parse: title '%s'", url_title.toUtf8().constData());246 url_title = QString(url_title).replace("&","&").replace(">", ">").replace("<", "<").replace(""","\"").replace("'","'"); 247 qDebug(; 155 248 } else { 156 url_title = "Youtube video"; 157 } 158 159 //qDebug("RetrieveYoutubeUrl::parse: replyString: %s",replyString.toLatin1().constData()); 160 161 QString player; 162 QRegExp rxplayer("html5player-([\\d,\\w,-]+)\\\\"); 249 url_title = "YouTube video"; 250 } 251 252 #ifdef YT_USE_SIG 253 QString html5_player; 254 #endif 255 //QRegExp rxplayer("player-([\\d,\\w,-]+)\\\\"); 256 QRegExp rxplayer("jsbin\\/player-([\\d\\w-]+)\\/base\\.js"); 163 257 if (rxplayer.indexIn(replyString) != -1) { 164 player = rxplayer.cap(1); 165 qDebug("RetrieveYoutubeUrl::parse: html5player: %s", player.toLatin1().constData()); 166 } 258 html5_player = rxplayer.cap(1); 259 qDebug() << "RetrieveYoutubeUrl::videoPageLoaded: html5player:" << html5_player; 260 } 261 262 video_page = replyString; 263 264 #ifdef YT_USE_SIG 265 if (!html5_player.isEmpty() && html5_player != sig.html5_player) { 266 sig.clear(); 267 sig.html5_player = html5_player; 268 fetchPlayerPage(html5_player); 269 } else { 270 processVideoPage(); 271 } 272 #else 273 processVideoPage(); 274 #endif 275 } 276 277 void RetrieveYoutubeUrl::processVideoPage() { 278 QString replyString = video_page; 279 280 #ifdef YT_LIVE_STREAM 281 QRegExp rxhlsvp("\"hlsvp\":\"([a-zA-Z0-9\\\\\\/_%\\+:\\.-]+)\""); 282 if (rxhlsvp.indexIn(replyString) != -1) { 283 QString hlsvp = QUrl::fromPercentEncoding(rxhlsvp.cap(1).toLatin1()).replace("\\/", "/"); 284 qDebug() << "RetrieveYoutubeUrl::processVideoPage: hlsvp:" << hlsvp; 285 286 if (!hlsvp.isEmpty()) { 287 fetchStreamPage(hlsvp); 288 return; 289 } 290 } 291 #endif 167 292 168 293 QString fmtArray; … … 172 297 } 173 298 174 #ifdef YT_DASH_SUPPORT299 #ifdef YT_DASH_SUPPORT 175 300 QRegExp regex2("\\\"adaptive_fmts\\\"\\s*:\\s*\\\"([^\\\"]*)"); 176 301 if (regex2.indexIn(replyString) != -1) { … … 178 303 fmtArray += regex2.cap(1); 179 304 } 180 #endif305 #endif 181 306 182 307 fmtArray = sanitizeForUnicodePoint(fmtArray); 183 308 fmtArray.replace(QRegExp("\\\\(.)"), "\\1"); 184 309 185 #ifndef YT_GET_VIDEOINFO 186 bool failed_to_decrypt_signature = false; 187 #endif 310 //qDebug() << "RetrieveYoutubeUrl::videoPageLoaded: fmtArray:" << fmtArray; 311 312 #ifdef YT_DISCARD_HTTPS 313 bool allow_https = false; 314 #else 315 bool allow_https = true; 316 #endif 317 318 UrlMap url_map = extractURLs(fmtArray, allow_https, true); 319 #ifdef YT_GET_VIDEOINFO 320 if (url_map.isEmpty()) { 321 fetchVideoInfoPage(yt_url); 322 } else { 323 finish(url_map); 324 } 325 #else 326 finish(url_map); 327 #endif 328 } 329 330 #ifdef YT_GET_VIDEOINFO 331 void RetrieveYoutubeUrl::videoInfoPageLoaded(QByteArray page) { 332 qDebug() << "RetrieveYoutubeUrl::videoInfoPageLoaded"; 333 334 #if QT_VERSION >= 0x050000 335 QUrlQuery all; 336 all.setQuery(page); 337 #else 338 QUrl all; 339 all.setEncodedQuery(page); 340 #endif 341 342 QByteArray fmtArray; 343 #if QT_VERSION >= 0x050000 344 fmtArray = all.queryItemValue("url_encoded_fmt_stream_map", QUrl::FullyDecoded).toLatin1(); 345 #else 346 fmtArray = all.queryItemValue("url_encoded_fmt_stream_map").toLatin1(); 347 #endif 348 349 #ifdef YT_DASH_SUPPORT 350 if (!fmtArray.isEmpty()) fmtArray += ","; 351 #if QT_VERSION >= 0x050000 352 fmtArray += all.queryItemValue("adaptive_fmts", QUrl::FullyDecoded).toLatin1(); 353 #else 354 fmtArray += all.queryItemValue("adaptive_fmts").toLatin1(); 355 #endif 356 #endif 357 358 //qDebug() <<"RetrieveYoutubeUrl::videoInfoPageLoaded: fmtArray:" << fmtArray; 359 360 UrlMap url_map = extractURLs(fmtArray, true, false); 361 362 if ((url_map.count() == 0) && (failed_to_decrypt_signature)) { 363 qDebug() << "RetrieveYoutubeUrl::videoInfoPageLoaded: no url found with valid signature"; 364 emit signatureNotFound(url_title); 365 return; 366 } 367 368 finish(url_map); 369 } 370 #endif 371 372 #ifdef YT_USE_SIG 373 void RetrieveYoutubeUrl::playerPageLoaded(QByteArray page) { 374 qDebug() << "RetrieveYoutubeUrl::playerPageLoaded"; 375 376 QString replyString = QString::fromUtf8(page); 377 QString signature_code = sig.findFunctions(replyString); 378 qDebug() << "RetrieveYoutubeUrl::playerPageLoaded: signature_code:" << signature_code; 379 380 if (!signature_code.isEmpty() && set) sig.save(set); 381 382 processVideoPage(); 383 } 384 #endif 385 386 #ifdef YT_LIVE_STREAM 387 void RetrieveYoutubeUrl::streamPageLoaded(QByteArray page) { 388 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded"; 389 390 //qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: page:" << page; 391 392 QRegExp rx("#EXT-X-STREAM-INF:.*RESOLUTION=\\d+x(\\d+)"); 393 394 QMap<int, QString> url_map; 395 int best_resolution = 0; 396 int res_height = 0; 397 398 QTextStream stream(page); 399 QString line; 400 do { 401 line = stream.readLine(); 402 if (!line.isEmpty()) { 403 //qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: line:" << line; 404 if (rx.indexIn(line) != -1) { 405 res_height = rx.cap(1).toInt(); 406 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: height:" << res_height; 407 if (res_height > best_resolution) best_resolution = res_height; 408 } 409 else 410 if (!line.startsWith("#") && res_height != 0) { 411 url_map[res_height] = line; 412 res_height = 0; 413 } 414 } 415 } while (!line.isNull()); 416 417 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: best_resolution:" << best_resolution; 418 419 // Try to find a URL with the user's preferred quality 420 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: preferred_quality:" << preferred_quality; 421 422 int selected_quality = 0; 423 int q = preferred_quality; 424 425 if (q == WEBM_1080p || q == MP4_1080p) { 426 if (url_map.contains(1080)) { 427 selected_quality = 1080; 428 } else q = MP4_720p; 429 } 430 431 if (q == WEBM_720p || q == MP4_720p) { 432 if (url_map.contains(720)) { 433 selected_quality = 720; 434 } else q = WEBM_480p; 435 } 436 437 if (q == WEBM_480p || q == FLV_480p) { 438 if (url_map.contains(480)) { 439 selected_quality = 480; 440 } else q = MP4_360p; 441 } 442 443 if (q == WEBM_360p || q == FLV_360p || q == MP4_360p) { 444 if (url_map.contains(360)) { 445 selected_quality = 360; 446 } else q = FLV_240p; 447 } 448 449 if (q == FLV_240p) { 450 if (url_map.contains(240)) { 451 selected_quality = 240; 452 } 453 } 454 455 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: selected_quality:" << selected_quality; 456 457 if (selected_quality == 0) selected_quality = best_resolution; 458 459 if (url_map.contains(selected_quality)) { 460 QString p_url = url_map.value(selected_quality); 461 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: p_url:" << p_url; 462 emit gotPreferredUrl(p_url, 0); 463 latest_preferred_url = p_url; 464 } else { 465 emit gotEmptyList(); 466 } 467 } 468 #endif 469 470 void RetrieveYoutubeUrl::finish(const UrlMap & url_map) { 471 qDebug() << "RetrieveYoutubeUrl::finish"; 472 473 int itag = findPreferredUrl(url_map, preferred_quality); 474 475 QString p_url; 476 if (itag != -1) p_url = url_map[itag]; 477 qDebug() << "RetrieveYoutubeUrl::finish: p_url:" << p_url; 478 479 latest_preferred_url = p_url; 480 481 #if 0 && defined(YT_DASH_SUPPORT) 482 // Test findBestAudio 483 { 484 int itag = findBestAudio(url_map); 485 QString audio_url; 486 if (itag != -1) audio_url = url_map[itag]; 487 qDebug() << "RetrieveYoutubeUrl::finish: audio itag:" << itag; 488 qDebug() << "RetrieveYoutubeUrl::finish: audio url:" << audio_url; 489 } 490 #endif 491 492 if (!p_url.isNull()) { 493 emit gotUrls(url_map); 494 //emit gotPreferredUrl(p_url); 495 emit gotPreferredUrl(p_url, itag); 496 } else { 497 emit gotEmptyList(); 498 } 499 } 500 501 #ifdef YT_USE_SCRIPT 502 QString RetrieveYoutubeUrl::aclara(const QString & text, const QString & player) { 503 QString res; 504 505 #if defined(YT_USE_YTSIG) && !defined(YT_USE_SIG) 506 if (!player.isNull()) { 507 res = YTSig::aclara(text, player); 508 } else { 509 res = YTSig::aclara(text, "", "aclara_f"); 510 } 511 #endif 512 513 #ifdef YT_USE_SIG 514 if (!sig.decrypt_function.isEmpty()) { 515 res = sig.aclara(text); 516 } 517 #ifdef YT_USE_YTSIG 518 else { 519 if (!player.isNull()) { 520 res = YTSig::aclara(text, player); 521 } else { 522 res = YTSig::aclara(text, "", "aclara_f"); 523 } 524 } 525 #endif 526 #endif 527 528 return res; 529 } 530 #endif 531 532 UrlMap RetrieveYoutubeUrl::extractURLs(QString fmtArray, bool allow_https, bool use_player) { 533 UrlMap url_map; 534 535 failed_to_decrypt_signature = false; 188 536 189 537 #if QT_VERSION >= 0x050000 … … 191 539 #endif 192 540 541 542 193 543 QList<QByteArray> codeList = fmtArray.toLatin1().split(','); 544 545 194 546 foreach(QByteArray code, codeList) { 195 547 code = QUrl::fromPercentEncoding(code).toLatin1(); 196 //qDebug( "code: %s", code.constData());548 //qDebug(; 197 549 198 550 QUrl line; … … 212 564 #if QT_VERSION >= 0x050000 213 565 q->setQuery( q->query(QUrl::FullyDecoded) + "&" + url.query(QUrl::FullyDecoded) ); 214 /*215 QList < QPair < QString,QString > > l = q->queryItems();216 for (int n=0; n < l.count(); n++) {217 qDebug("n: %d, %s = %s", n, l[n].first.toLatin1().constData(), l[n].second.toLatin1().constData());218 }219 */220 566 #else 221 567 q->setEncodedQuery( q->encodedQuery() + "&" + url.encodedQuery() ); 222 /*223 QList < QPair < QString,QString > > l = q->queryItems();224 for (int n=0; n < l.count(); n++) {225 qDebug("n: %d, %s = %s", n, l[n].first.toLatin1().constData(), l[n].second.toLatin1().constData());226 }227 */228 568 #endif 229 569 … … 234 574 else 235 575 if (q->hasQueryItem("s")) { 236 #ifdef USE_PLAYER_NAME 237 QString signature = YTSig::aclara(q->queryItemValue("s"), player); 576 #ifdef YT_USE_SCRIPT 577 #ifdef YT_USE_SIG 578 QString player = sig.html5_player; 238 579 #else 239 QString signature = YTSig::aclara(q->queryItemValue("s"));580 QString ; 240 581 #endif 582 241 583 if (!signature.isEmpty()) { 242 584 q->addQueryItem("signature", signature); 243 585 } else { 244 #ifndef YT_GET_VIDEOINFO245 586 failed_to_decrypt_signature = true; 587 588 589 246 590 #endif 247 }248 591 q->removeQueryItem("s"); 249 592 } … … 268 611 #endif 269 612 270 #ifdef YT_GET_VIDEOINFO 271 if (!line.toString().startsWith("https")) { 272 urlMap[itag.toInt()] = line.toString(); 613 if (!line.toString().startsWith("https") || allow_https) { 614 url_map[itag.toInt()] = line.toString(); 615 } else { 616 qDebug() << "RetrieveYoutubeUrl::extractURLs: discarted url with itag:" << itag.toInt(); 273 617 } 274 #else 275 urlMap[itag.toInt()] = line.toString(); 276 #endif 277 278 //qDebug("itag: %s line: %s", itag.toLatin1().constData(), line.toString().toLatin1().constData()); 618 619 //qDebug() << "RetrieveYoutubeUrl::extractURLs: itag:" << itag << "line:" << line.toString(); 279 620 } 280 621 } … … 285 626 #endif 286 627 287 qDebug("RetrieveYoutubeUrl::parse: url count: %d", urlMap.count()); 288 289 #ifndef YT_GET_VIDEOINFO 290 if ((urlMap.count() == 0) && (failed_to_decrypt_signature)) { 291 qDebug("RetrieveYoutubeUrl::parse: no url found with valid signature"); 292 emit signatureNotFound(url_title); 293 return; 294 } 295 #else 296 if (urlMap.count() == 0) { 297 qDebug("RetrieveYoutubeUrl::parse: no url found with valid signature"); 298 fetchVideoInfoPage(); 299 return; 300 } 301 #endif 302 303 QString p_url = findPreferredUrl(); 304 //qDebug("p_url: '%s'", p_url.toLatin1().constData()); 305 306 if (!p_url.isNull()) { 307 emit gotUrls(urlMap); 308 emit gotPreferredUrl(p_url); 309 #ifdef YT_GET_VIDEOINFO 310 emit gotVideoInfo(urlMap, url_title, video_id); 311 #endif 312 } else { 313 emit gotEmptyList(); 314 } 315 } 316 317 QString RetrieveYoutubeUrl::getVideoID(QString video_url) { 318 if (video_url.contains("youtu.be/")) { 319 video_url.replace("youtu.be/", "youtube.com/watch?v="); 320 } 321 else 322 if (video_url.contains("y2u.be/")) { 323 video_url.replace("y2u.be/", "youtube.com/watch?v="); 324 } 325 else 326 if (video_url.contains("m.youtube.com")) { 327 video_url.replace("m.youtube.com", "www.youtube.com"); 328 } 329 330 if ((video_url.startsWith("youtube.com")) || (video_url.startsWith("www.youtube.com"))) video_url = "http://" + video_url; 331 332 //qDebug("RetrieveYoutubeUrl::getVideoID: video_url: %s", video_url.toLatin1().constData()); 333 334 QUrl url(video_url); 335 336 QString ID; 337 338 #if QT_VERSION >= 0x050000 339 QUrlQuery * q = new QUrlQuery(url); 340 #else 341 const QUrl * q = &url; 342 #endif 343 344 if ((url.host().contains("youtube")) && (url.path().contains("watch_videos"))) { 345 if (q->hasQueryItem("video_ids")) { 346 int index = 0; 347 if (q->hasQueryItem("index")) index = q->queryItemValue("index").toInt(); 348 QStringList list = q->queryItemValue("video_ids").split(","); 349 if (index < list.count()) ID = list[index]; 350 } 351 } 352 else 353 if ((url.host().contains("youtube")) && (url.path().contains("watch"))) { 354 if (q->hasQueryItem("v")) { 355 ID = q->queryItemValue("v"); 356 } 357 } 358 359 #if QT_VERSION >= 0x050000 360 delete q; 361 #endif 362 363 //qDebug("RetrieveYoutubeUrl::getVideoID: ID: %s", ID.toLatin1().constData()); 364 365 return ID; 366 } 367 368 bool RetrieveYoutubeUrl::isUrlSupported(const QString & url) { 369 return (!getVideoID(url).isEmpty()); 370 } 371 372 QString RetrieveYoutubeUrl::fullUrl(const QString & url) { 373 QString r; 374 QString ID = getVideoID(url); 375 if (!ID.isEmpty()) { 376 QString scheme = use_https_main ? "https" : "http"; 377 r = scheme + "://www.youtube.com/watch?v=" + ID; 378 } 379 return r; 380 } 381 382 #ifdef YT_GET_VIDEOINFO 383 void RetrieveYoutubeUrl::parseVideoInfo(QByteArray text) { 384 urlMap.clear(); 385 386 #if QT_VERSION >= 0x050000 387 QUrlQuery all; 388 all.setQuery(text); 389 #else 390 QUrl all; 391 all.setEncodedQuery(text); 392 #endif 393 394 QByteArray fmtArray; 395 fmtArray = all.queryItemValue("url_encoded_fmt_stream_map").toLatin1(); 396 397 #ifdef YT_DASH_SUPPORT 398 if (!fmtArray.isEmpty()) fmtArray += ","; 399 fmtArray += all.queryItemValue("adaptive_fmts").toLatin1(); 400 #endif 401 402 /* 403 qDebug("fmtArray: %s", fmtArray.constData()); 404 return; 405 */ 406 407 bool failed_to_decrypt_signature = false; 408 409 #if QT_VERSION >= 0x050000 410 QUrlQuery * q = new QUrlQuery(); 411 #endif 412 413 QList<QByteArray> codeList = fmtArray.split(','); 414 foreach(QByteArray code, codeList) { 415 code = QUrl::fromPercentEncoding(code).toLatin1(); 416 //qDebug("code: %s", code.constData()); 417 418 QUrl line; 419 #if QT_VERSION >= 0x050000 420 q->setQuery(code); 421 #else 422 QUrl * q = &line; 423 q->setEncodedQuery(code); 424 #endif 425 426 if (q->hasQueryItem("url")) { 427 QUrl url( q->queryItemValue("url") ); 428 line.setScheme(url.scheme()); 429 line.setHost(url.host()); 430 line.setPath(url.path()); 431 q->removeQueryItem("url"); 432 #if QT_VERSION >= 0x050000 433 q->setQuery( q->query(QUrl::FullyDecoded) + "&" + url.query(QUrl::FullyDecoded) ); 434 /* 435 QList < QPair < QString,QString > > l = q->queryItems(); 436 for (int n=0; n < l.count(); n++) { 437 qDebug("n: %d, %s = %s", n, l[n].first.toLatin1().constData(), l[n].second.toLatin1().constData()); 438 } 439 */ 440 #else 441 q->setEncodedQuery( q->encodedQuery() + "&" + url.encodedQuery() ); 442 /* 443 QList < QPair < QString,QString > > l = q->queryItems(); 444 for (int n=0; n < l.count(); n++) { 445 qDebug("n: %d, %s = %s", n, l[n].first.toLatin1().constData(), l[n].second.toLatin1().constData()); 446 } 447 */ 448 #endif 449 450 if (q->hasQueryItem("sig")) { 451 q->addQueryItem("signature", q->queryItemValue("sig")); 452 q->removeQueryItem("sig"); 453 } 454 else 455 if (q->hasQueryItem("s")) { 456 QString signature = YTSig::aclara(q->queryItemValue("s"), "", "aclara_f"); 457 if (!signature.isEmpty()) { 458 q->addQueryItem("signature", signature); 459 } else { 460 failed_to_decrypt_signature = true; 461 } 462 q->removeQueryItem("s"); 463 } 464 q->removeAllQueryItems("fallback_host"); 465 q->removeAllQueryItems("type"); 466 467 if (!q->hasQueryItem("ratebypass")) q->addQueryItem("ratebypass", "yes"); 468 469 if ((q->hasQueryItem("itag")) && (q->hasQueryItem("signature"))) { 470 QString itag = q->queryItemValue("itag"); 471 472 // Remove duplicated queries 473 QPair <QString,QString> item; 474 QList<QPair<QString, QString> > items = q->queryItems(); 475 foreach(item, items) { 476 q->removeAllQueryItems(item.first); 477 q->addQueryItem(item.first, item.second); 478 } 479 480 #if QT_VERSION >= 0x050000 481 line.setQuery(q->query(QUrl::FullyDecoded)); 482 #endif 483 urlMap[itag.toInt()] = line.toString(); 484 //qDebug("itag: %s line: %s", itag.toLatin1().constData(), line.toString().toLatin1().constData()); 485 } 486 } 487 } 488 489 #if QT_VERSION >= 0x050000 490 delete q; 491 #endif 492 493 qDebug("RetrieveYoutubeUrl::parseVideoInfo: url count: %d", urlMap.count()); 494 495 if ((urlMap.count() == 0) && (failed_to_decrypt_signature)) { 496 qDebug("RetrieveYoutubeUrl::parseVideoInfo: no url found with valid signature"); 497 emit signatureNotFound(url_title); 498 return; 499 } 500 501 QString p_url = findPreferredUrl(); 502 //qDebug("p_url: '%s'", p_url.toLatin1().constData()); 503 504 if (!p_url.isNull()) { 505 emit gotUrls(urlMap); 506 emit gotPreferredUrl(p_url); 507 #ifdef YT_GET_VIDEOINFO 508 emit gotVideoInfo(urlMap, url_title, video_id); 509 #endif 510 } else { 511 emit gotEmptyList(); 512 } 513 } 514 #endif 515 516 QString RetrieveYoutubeUrl::findPreferredUrl() { 517 latest_preferred_url = findPreferredUrl(urlMap, preferred_quality); 518 return latest_preferred_url; 519 } 520 521 QString RetrieveYoutubeUrl::findPreferredUrl(const QMap<int, QString>& urlMap, Quality q) { 628 qDebug() << "RetrieveYoutubeUrl::extractURLs: url count:" << url_map.count(); 629 630 return url_map; 631 } 632 633 int RetrieveYoutubeUrl::findPreferredUrl(const UrlMap & url_map, Quality q) { 522 634 // Choose a url according to preferred quality 523 635 QString p_url; 524 636 //Quality q = preferred_quality; 525 637 638 639 640 641 642 643 644 645 526 646 if (q==MP4_1080p) { 527 p_url = urlMap.value(MP4_1080p, QString());528 if (p_url.isNull()) p_url= urlMap.value(WEBM_1080p, QString());647 648 if (p_url.isNull()) 529 649 if (p_url.isNull()) q = MP4_720p; 530 650 } 531 651 532 652 if (q==WEBM_1080p) { 533 p_url = urlMap.value(WEBM_1080p, QString());534 if (p_url.isNull()) p_url= urlMap.value(MP4_1080p, QString());653 654 if (p_url.isNull()) 535 655 if (p_url.isNull()) q = WEBM_720p; 536 656 } 537 657 538 658 if (q==MP4_720p) { 539 p_url = urlMap.value(MP4_720p, QString());540 if (p_url.isNull()) p_url= urlMap.value(WEBM_720p, QString());541 if (p_url.isNull()) p_url = urlMap.value(WEBM_480p, QString());659 660 if (p_url.isNull()) 661 if (p_url.isNull()) 542 662 if (p_url.isNull()) q = MP4_360p; 543 663 } 544 664 545 665 if (q==WEBM_720p) { 546 p_url = urlMap.value(WEBM_720p, QString());547 if (p_url.isNull()) p_url= urlMap.value(MP4_720p, QString());666 667 if (p_url.isNull()) 548 668 if (p_url.isNull()) q = WEBM_480p; 549 669 } 550 670 551 671 if (q==WEBM_480p) { 552 p_url = urlMap.value(WEBM_480p, QString());672 553 673 if (p_url.isNull()) q = WEBM_360p; 554 674 } 555 675 556 676 if (q==MP4_360p) { 557 p_url = urlMap.value(MP4_360p, QString());558 if (p_url.isNull()) p_url= urlMap.value(WEBM_360p, QString());677 678 if (p_url.isNull()) 559 679 if (p_url.isNull()) q = FLV_360p; 560 680 } 561 681 562 682 if (q==WEBM_360p) { 563 p_url = urlMap.value(WEBM_360p, QString());564 if (p_url.isNull()) p_url= urlMap.value(MP4_360p, QString());683 684 if (p_url.isNull()) 565 685 if (p_url.isNull()) q = FLV_360p; 566 686 } … … 568 688 // FLV, low priority 569 689 if (q==FLV_480p) { 570 p_url = urlMap.value(FLV_480p, QString());690 571 691 if (p_url.isNull()) q = FLV_360p; 572 692 } 573 693 574 694 if (q==FLV_360p) { 575 p_url = urlMap.value(FLV_360p, QString());695 576 696 if (p_url.isNull()) q = FLV_240p; 577 697 } 578 698 579 699 if (q==FLV_240p) { 580 p_url = urlMap.value(q, QString()); 581 } 582 583 return p_url; 584 } 585 586 #ifdef YT_DASH_SUPPORT 587 QString RetrieveYoutubeUrl::findBestAudio(const QMap<int, QString>& urlMap) { 588 QString url; 589 590 url = urlMap.value(DASH_AUDIO_MP4_256, QString()); 591 if (!url.isEmpty()) return url; 592 593 url = urlMap.value(DASH_AUDIO_WEBM_192, QString()); 594 if (!url.isEmpty()) return url; 595 596 url = urlMap.value(DASH_AUDIO_MP4_128, QString()); 597 if (!url.isEmpty()) return url; 598 599 url = urlMap.value(DASH_AUDIO_WEBM_128, QString()); 600 if (!url.isEmpty()) return url; 601 602 url = urlMap.value(DASH_AUDIO_MP4_48, QString()); 603 if (!url.isEmpty()) return url; 604 605 return url; 606 } 607 #endif 700 SETPURL(q) 701 } 702 703 // If everything fails, take the first url in the map 704 if (p_url.isEmpty()) { 705 QList<int> keys = url_map.keys(); 706 if (!keys.isEmpty()) SETPURL(keys[0]) 707 } 708 709 qDebug("RetrieveYoutubeUrl::findPreferredUrl: chosen_quality: %d", chosen_quality); 710 return chosen_quality; 711 } 608 712 609 713 QString RetrieveYoutubeUrl::sanitizeForUnicodePoint(QString string) { … … 615 719 } 616 720 617 void RetrieveYoutubeUrl::htmlDecode(QString& string) { 618 string.replace("%3A", ":", Qt::CaseInsensitive); 619 string.replace("%2F", "/", Qt::CaseInsensitive); 620 string.replace("%3F", "?", Qt::CaseInsensitive); 621 string.replace("%3D", "=", Qt::CaseInsensitive); 622 string.replace("%25", "%", Qt::CaseInsensitive); 623 string.replace("%26", "&", Qt::CaseInsensitive); 624 string.replace("%3D", "=", Qt::CaseInsensitive); 625 } 721 QString RetrieveYoutubeUrl::extensionForItag(int itag) { 722 QString ext = ".mp4"; 723 switch (itag) { 724 case RetrieveYoutubeUrl::FLV_240p: 725 case RetrieveYoutubeUrl::FLV_360p: 726 case RetrieveYoutubeUrl::FLV_480p: 727 ext = ".flv"; 728 break; 729 case RetrieveYoutubeUrl::WEBM_360p: 730 case RetrieveYoutubeUrl::WEBM_480p: 731 case RetrieveYoutubeUrl::WEBM_720p: 732 case RetrieveYoutubeUrl::WEBM_1080p: 733 ext = ".webm"; 734 break; 735 case RetrieveYoutubeUrl::DASH_AUDIO_MP4_48: 736 case RetrieveYoutubeUrl::DASH_AUDIO_MP4_128: 737 case RetrieveYoutubeUrl::DASH_AUDIO_MP4_256: 738 ext = ".m4a"; 739 break; 740 case RetrieveYoutubeUrl::DASH_AUDIO_WEBM_128: 741 case RetrieveYoutubeUrl::DASH_AUDIO_WEBM_192: 742 ext = ".webm"; 743 break; 744 } 745 746 return ext; 747 } 748 749 #ifdef YT_DASH_SUPPORT 750 int RetrieveYoutubeUrl::findBestAudio(const QMap<int, QString>& url_map) { 751 QString url; 752 753 #define CHECKAQ(QUALITY) { \ 754 url = url_map.value(QUALITY, QString()); \ 755 if (!url.isNull()) return QUALITY; \ 756 } 757 758 CHECKAQ(DASH_AUDIO_MP4_256); 759 CHECKAQ(DASH_AUDIO_WEBM_192); 760 CHECKAQ(DASH_AUDIO_MP4_128); 761 CHECKAQ(DASH_AUDIO_WEBM_128); 762 CHECKAQ(DASH_AUDIO_MP4_48); 763 764 return -1; 765 } 766 #endif 626 767 627 768 #include "moc_retrieveyoutubeurl.cpp"
Note:
See TracChangeset
for help on using the changeset viewer.
