source: smplayer/trunk/src/youtube/retrieveyoutubeurl.cpp

Last change on this file was 188, checked in by Silvan Scherrer, 8 years ago

SMPlayer: update trunk to version 17.1.0

File size: 19.9 KB
RevLine 
[121]1/* smplayer, GUI front-end for mplayer.
[188]2 Copyright (C) 2006-2017 Ricardo Villalba <rvm@users.sourceforge.net>
[121]3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17*/
18
19#include "retrieveyoutubeurl.h"
[176]20#include "loadpage.h"
21
[121]22#include <QUrl>
[176]23#include <QDebug>
[156]24#include <QRegExp>
25#include <QStringList>
[176]26#include <QSslSocket>
27
28#ifdef YT_USE_YTSIG
[156]29#include "ytsig.h"
[176]30#endif
[121]31
[165]32#if QT_VERSION >= 0x050000
33#include <QUrlQuery>
34#endif
35
36
[176]37RetrieveYoutubeUrl::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)
[121]45{
[156]46 manager = new QNetworkAccessManager(this);
[121]47
[176]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
[121]69}
70
71RetrieveYoutubeUrl::~RetrieveYoutubeUrl() {
72}
73
[176]74#ifdef YT_USE_SIG
75void RetrieveYoutubeUrl::setSettings(QSettings * settings) {
76 set = settings;
77 sig.load(set);
78}
79#endif
[165]80
[176]81bool RetrieveYoutubeUrl::isUrlSupported(const QString & url) {
82 return (!getVideoID(url).isEmpty());
83}
[156]84
[176]85QString RetrieveYoutubeUrl::fullUrl(const QString & url) {
86 QString r;
87 QString ID = getVideoID(url);
88 if (!ID.isEmpty()) {
89 QString scheme = use_https_main ? "https" : "http";
90 r = scheme + "://www.youtube.com/watch?v=" + ID;
91 }
92 return r;
[121]93}
94
[176]95QString RetrieveYoutubeUrl::getVideoID(QString video_url) {
96 //qDebug() << "RetrieveYoutubeUrl::getVideoID: video_url:" << video_url;
97
98 if (video_url.contains("m.youtube.com")) {
99 video_url.replace("m.youtube.com", "www.youtube.com");
[170]100 }
[165]101
[176]102 if (video_url.startsWith("youtube.com") || video_url.startsWith("www.youtube.com") ||
103 video_url.startsWith("youtu.be") || video_url.startsWith("www.youtu.be") ||
104 video_url.startsWith("y2u.be") || video_url.startsWith("www.y2u.be"))
105 {
106 video_url = "http://" + video_url;
107 }
[170]108
[176]109 //qDebug() << "RetrieveYoutubeUrl::getVideoID: fixed url:" << video_url;
[165]110
[176]111 QUrl url(video_url);
[165]112
[176]113 QString ID;
[156]114
[176]115 #if QT_VERSION >= 0x050000
116 QUrlQuery * q = new QUrlQuery(url);
117 #else
118 const QUrl * q = &url;
119 #endif
[170]120
[176]121 /*
122 qDebug() << "host:" << url.host();
123 qDebug() << "path:" << url.path();
124 */
[165]125
[176]126 if (url.host() == "youtu.be" || url.host() == "y2u.be") {
127 ID = url.path();
128 if (ID.startsWith("/")) ID = ID.mid(1);
129 }
130 else
131 if ((url.host().contains("youtube")) && (url.path().contains("watch_videos"))) {
132 if (q->hasQueryItem("video_ids")) {
133 int index = 0;
134 if (q->hasQueryItem("index")) index = q->queryItemValue("index").toInt();
135 QStringList list = q->queryItemValue("video_ids").split(",");
136 if (index < list.count()) ID = list[index];
[156]137 }
138 }
[176]139 else
140 if ((url.host().contains("youtube")) && (url.path().contains("watch"))) {
141 if (q->hasQueryItem("v")) {
142 ID = q->queryItemValue("v");
143 }
144 }
145
146 #if QT_VERSION >= 0x050000
147 delete q;
148 #endif
149
150 qDebug() << "RetrieveYoutubeUrl::getVideoID: ID:" << ID;
151
152 return ID;
[156]153}
154
[176]155void RetrieveYoutubeUrl::fetchPage(const QString & url) {
156 yt_url = url;
157 fetchVideoPage(url);
158 //fetchVideoInfoPage(url);
159}
160
161void 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);
171
172 emit connecting(QUrl(url).host());
173};
174
[165]175#ifdef YT_GET_VIDEOINFO
[176]176void RetrieveYoutubeUrl::fetchVideoInfoPage(const QString & url) {
177 video_id = getVideoID(url);
[165]178
[176]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);
[165]181
[176]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();
[165]187 return;
188 }
[176]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 + "&amp;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());
[165]208}
209#endif
210
[176]211#ifdef YT_USE_SIG
212void RetrieveYoutubeUrl::fetchPlayerPage(const QString & player_name) {
213 qDebug() << "RetrieveYoutubeUrl::fetchPlayerPage:" << player_name;
[121]214
[176]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
[121]230
[176]231#ifdef YT_LIVE_STREAM
232void RetrieveYoutubeUrl::fetchStreamPage(const QString & url) {
233 qDebug() << "RetrieveYoutubeUrl::fetchStreamPage:" << url;
234 dl_stream_page->fetchPage(url);
235}
236#endif
[121]237
[176]238void RetrieveYoutubeUrl::videoPageLoaded(QByteArray page) {
239 qDebug() << "RetrieveYoutubeUrl::videoPageLoaded";
240
241 QString replyString = QString::fromUtf8(page);
242
[121]243 QRegExp rx_title(".*<title>(.*)</title>.*");
244 if (rx_title.indexIn(replyString) != -1) {
245 url_title = rx_title.cap(1).simplified();
[176]246 url_title = QString(url_title).replace("&amp;","&").replace("&gt;", ">").replace("&lt;", "<").replace("&quot;","\"").replace("&#39;","'");
247 qDebug() << "RetrieveYoutubeUrl::videoPageLoaded: title:" << url_title;
[121]248 } else {
[176]249 url_title = "YouTube video";
[121]250 }
251
[176]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");
[165]257 if (rxplayer.indexIn(replyString) != -1) {
[176]258 html5_player = rxplayer.cap(1);
259 qDebug() << "RetrieveYoutubeUrl::videoPageLoaded: html5player:" << html5_player;
[165]260 }
261
[176]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
277void 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
292
[170]293 QString fmtArray;
[156]294 QRegExp regex("\\\"url_encoded_fmt_stream_map\\\"\\s*:\\s*\\\"([^\\\"]*)");
[170]295 if (regex.indexIn(replyString) != -1) {
296 fmtArray = regex.cap(1);
297 }
298
[176]299 #ifdef YT_DASH_SUPPORT
[170]300 QRegExp regex2("\\\"adaptive_fmts\\\"\\s*:\\s*\\\"([^\\\"]*)");
301 if (regex2.indexIn(replyString) != -1) {
302 if (!fmtArray.isEmpty()) fmtArray += ",";
303 fmtArray += regex2.cap(1);
304 }
[176]305 #endif
[170]306
[156]307 fmtArray = sanitizeForUnicodePoint(fmtArray);
308 fmtArray.replace(QRegExp("\\\\(.)"), "\\1");
[142]309
[176]310 //qDebug() << "RetrieveYoutubeUrl::videoPageLoaded: fmtArray:" << fmtArray;
[142]311
[176]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
331void RetrieveYoutubeUrl::videoInfoPageLoaded(QByteArray page) {
332 qDebug() << "RetrieveYoutubeUrl::videoInfoPageLoaded";
333
[165]334 #if QT_VERSION >= 0x050000
[176]335 QUrlQuery all;
336 all.setQuery(page);
337 #else
338 QUrl all;
339 all.setEncodedQuery(page);
[165]340 #endif
341
[176]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
[142]348
[176]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
[142]357
[176]358 //qDebug() <<"RetrieveYoutubeUrl::videoInfoPageLoaded: fmtArray:" << fmtArray;
[156]359
[176]360 UrlMap url_map = extractURLs(fmtArray, true, false);
[165]361
[176]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 }
[170]367
[176]368 finish(url_map);
369}
370#endif
[170]371
[176]372#ifdef YT_USE_SIG
373void RetrieveYoutubeUrl::playerPageLoaded(QByteArray page) {
374 qDebug() << "RetrieveYoutubeUrl::playerPageLoaded";
[170]375
[176]376 QString replyString = QString::fromUtf8(page);
377 QString signature_code = sig.findFunctions(replyString);
378 qDebug() << "RetrieveYoutubeUrl::playerPageLoaded: signature_code:" << signature_code;
[170]379
[176]380 if (!signature_code.isEmpty() && set) sig.save(set);
[170]381
[176]382 processVideoPage();
383}
384#endif
385
386#ifdef YT_LIVE_STREAM
387void 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;
[142]408 }
[176]409 else
410 if (!line.startsWith("#") && res_height != 0) {
411 url_map[res_height] = line;
412 res_height = 0;
413 }
[142]414 }
[176]415 } while (!line.isNull());
[142]416
[176]417 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: best_resolution:" << best_resolution;
[165]418
[176]419 // Try to find a URL with the user's preferred quality
420 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: preferred_quality:" << preferred_quality;
[142]421
[176]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;
[156]429 }
[176]430
431 if (q == WEBM_720p || q == MP4_720p) {
432 if (url_map.contains(720)) {
433 selected_quality = 720;
434 } else q = WEBM_480p;
[165]435 }
[121]436
[176]437 if (q == WEBM_480p || q == FLV_480p) {
438 if (url_map.contains(480)) {
439 selected_quality = 480;
440 } else q = MP4_360p;
441 }
[121]442
[176]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;
[121]447 }
448
[176]449 if (q == FLV_240p) {
450 if (url_map.contains(240)) {
451 selected_quality = 240;
452 }
[165]453 }
454
[176]455 qDebug() << "RetrieveYoutubeUrl::streamPageLoaded: selected_quality:" << selected_quality;
[165]456
[176]457 if (selected_quality == 0) selected_quality = best_resolution;
[165]458
[176]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}
[165]468#endif
469
[176]470void RetrieveYoutubeUrl::finish(const UrlMap & url_map) {
471 qDebug() << "RetrieveYoutubeUrl::finish";
[165]472
[176]473 int itag = findPreferredUrl(url_map, preferred_quality);
[165]474
[176]475 QString p_url;
476 if (itag != -1) p_url = url_map[itag];
477 qDebug() << "RetrieveYoutubeUrl::finish: p_url:" << p_url;
[165]478
[176]479 latest_preferred_url = p_url;
[165]480
[176]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
[165]491
[176]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();
[170]498 }
[165]499}
500
[176]501#ifdef YT_USE_SCRIPT
502QString RetrieveYoutubeUrl::aclara(const QString & text, const QString & player) {
503 QString res;
[165]504
[176]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 }
[165]511 #endif
512
[176]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
[170]527
[176]528 return res;
529}
[170]530#endif
531
[176]532UrlMap RetrieveYoutubeUrl::extractURLs(QString fmtArray, bool allow_https, bool use_player) {
533 UrlMap url_map;
[165]534
[176]535 failed_to_decrypt_signature = false;
[165]536
537 #if QT_VERSION >= 0x050000
538 QUrlQuery * q = new QUrlQuery();
539 #endif
540
[176]541 //qDebug() << "RetrieveYoutubeUrl::extractURLs: fmtArray:" << fmtArray;
542
543 QList<QByteArray> codeList = fmtArray.toLatin1().split(',');
544 //qDebug() << "RetrieveYoutubeUrl::extractURLs: codeList.count:" << codeList.count();
545
[165]546 foreach(QByteArray code, codeList) {
547 code = QUrl::fromPercentEncoding(code).toLatin1();
[176]548 //qDebug() << "RetrieveYoutubeUrl::extractURLs: code:" << code;
[165]549
550 QUrl line;
551 #if QT_VERSION >= 0x050000
552 q->setQuery(code);
553 #else
554 QUrl * q = &line;
555 q->setEncodedQuery(code);
556 #endif
557
558 if (q->hasQueryItem("url")) {
559 QUrl url( q->queryItemValue("url") );
560 line.setScheme(url.scheme());
561 line.setHost(url.host());
562 line.setPath(url.path());
563 q->removeQueryItem("url");
564 #if QT_VERSION >= 0x050000
565 q->setQuery( q->query(QUrl::FullyDecoded) + "&" + url.query(QUrl::FullyDecoded) );
566 #else
567 q->setEncodedQuery( q->encodedQuery() + "&" + url.encodedQuery() );
568 #endif
569
570 if (q->hasQueryItem("sig")) {
571 q->addQueryItem("signature", q->queryItemValue("sig"));
572 q->removeQueryItem("sig");
573 }
574 else
575 if (q->hasQueryItem("s")) {
[176]576 #ifdef YT_USE_SCRIPT
577 #ifdef YT_USE_SIG
578 QString player = sig.html5_player;
579 #else
580 QString player = html5_player;
581 #endif
582 QString signature = aclara(q->queryItemValue("s"), use_player ? player : QString::null);
[165]583 if (!signature.isEmpty()) {
584 q->addQueryItem("signature", signature);
585 } else {
586 failed_to_decrypt_signature = true;
587 }
[176]588 #else // YT_USE_SCRIPT
589 failed_to_decrypt_signature = true;
590 #endif
[165]591 q->removeQueryItem("s");
592 }
593 q->removeAllQueryItems("fallback_host");
594 q->removeAllQueryItems("type");
[170]595
596 if (!q->hasQueryItem("ratebypass")) q->addQueryItem("ratebypass", "yes");
597
[165]598 if ((q->hasQueryItem("itag")) && (q->hasQueryItem("signature"))) {
599 QString itag = q->queryItemValue("itag");
[170]600
601 // Remove duplicated queries
602 QPair <QString,QString> item;
603 QList<QPair<QString, QString> > items = q->queryItems();
604 foreach(item, items) {
605 q->removeAllQueryItems(item.first);
606 q->addQueryItem(item.first, item.second);
607 }
608
[165]609 #if QT_VERSION >= 0x050000
610 line.setQuery(q->query(QUrl::FullyDecoded));
611 #endif
[176]612
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();
617 }
618
619 //qDebug() << "RetrieveYoutubeUrl::extractURLs: itag:" << itag << "line:" << line.toString();
[165]620 }
621 }
622 }
623
624 #if QT_VERSION >= 0x050000
625 delete q;
626 #endif
627
[176]628 qDebug() << "RetrieveYoutubeUrl::extractURLs: url count:" << url_map.count();
[165]629
[176]630 return url_map;
[165]631}
632
[176]633int RetrieveYoutubeUrl::findPreferredUrl(const UrlMap & url_map, Quality q) {
[121]634 // Choose a url according to preferred quality
635 QString p_url;
636 //Quality q = preferred_quality;
637
[176]638 int chosen_quality = -1;
639
640 #define SETPURL(QUALITY) { \
641 p_url= url_map.value(QUALITY, QString()); \
642 if (!p_url.isNull()) chosen_quality = QUALITY; \
643 }
644
645
[121]646 if (q==MP4_1080p) {
[176]647 SETPURL(MP4_1080p)
648 if (p_url.isNull()) SETPURL(WEBM_1080p)
[121]649 if (p_url.isNull()) q = MP4_720p;
650 }
651
652 if (q==WEBM_1080p) {
[176]653 SETPURL(WEBM_1080p)
654 if (p_url.isNull()) SETPURL(MP4_1080p)
[121]655 if (p_url.isNull()) q = WEBM_720p;
656 }
657
658 if (q==MP4_720p) {
[176]659 SETPURL(MP4_720p)
660 if (p_url.isNull()) SETPURL(WEBM_720p)
661 if (p_url.isNull()) SETPURL(WEBM_480p)
[121]662 if (p_url.isNull()) q = MP4_360p;
663 }
664
665 if (q==WEBM_720p) {
[176]666 SETPURL(WEBM_720p)
667 if (p_url.isNull()) SETPURL(MP4_720p)
[121]668 if (p_url.isNull()) q = WEBM_480p;
669 }
670
671 if (q==WEBM_480p) {
[176]672 SETPURL(WEBM_480p)
[121]673 if (p_url.isNull()) q = WEBM_360p;
674 }
675
676 if (q==MP4_360p) {
[176]677 SETPURL(MP4_360p)
678 if (p_url.isNull()) SETPURL(WEBM_360p)
[121]679 if (p_url.isNull()) q = FLV_360p;
680 }
681
682 if (q==WEBM_360p) {
[176]683 SETPURL(WEBM_360p)
684 if (p_url.isNull()) SETPURL(MP4_360p)
[121]685 if (p_url.isNull()) q = FLV_360p;
686 }
687
688 // FLV, low priority
689 if (q==FLV_480p) {
[176]690 SETPURL(FLV_480p)
[121]691 if (p_url.isNull()) q = FLV_360p;
692 }
693
694 if (q==FLV_360p) {
[176]695 SETPURL(FLV_360p)
[121]696 if (p_url.isNull()) q = FLV_240p;
697 }
698
699 if (q==FLV_240p) {
[176]700 SETPURL(q)
[121]701 }
702
[176]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;
[121]711}
712
[156]713QString RetrieveYoutubeUrl::sanitizeForUnicodePoint(QString string) {
714 QRegExp rx("\\\\u(\\d{4})");
715 while (rx.indexIn(string) != -1) {
716 string.replace(rx.cap(0), QString(QChar(rx.cap(1).toInt(0,16))));
717 }
718 return string;
[121]719}
720
[176]721QString 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;
[121]747}
748
[176]749#ifdef YT_DASH_SUPPORT
750int 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
767
[121]768#include "moc_retrieveyoutubeurl.cpp"
Note: See TracBrowser for help on using the repository browser.