Ignore:
Timestamp:
May 3, 2016, 2:14:41 PM (10 years ago)
Author:
Silvan Scherrer
Message:

smplayer: update vendor to version 16.4

File:
1 edited

Legend:

Unmodified
Added
Removed
  • smplayer/vendor/current/src/youtube/retrieveyoutubeurl.cpp

    r168 r175  
    11/*  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]>
    43
    54    This program is free software; you can redistribute it and/or modify
     
    1918
    2019#include "retrieveyoutubeurl.h"
     20
     21
    2122#include <QUrl>
     23
    2224#include <QRegExp>
    2325#include <QStringList>
    24 #include <QFile>
     26#include <QSslSocket>
     27
     28#ifdef YT_USE_YTSIG
    2529#include "ytsig.h"
     30
    2631
    2732#if QT_VERSION >= 0x050000
     
    2934#endif
    3035
    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
     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)
    3845{
    39         reply = 0;
    4046        manager = new QNetworkAccessManager(this);
    4147
    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
    4369}
    4470
     
    4672}
    4773
     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
    48155void 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
     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);
    58171
    59172        emit connecting(QUrl(url).host());
     173
    60174
    61175#ifdef YT_GET_VIDEOINFO
     176
    62177        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();
    111187                return;
    112188        }
    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 + "&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());
     208}
     209#endif
     210
     211#ifdef YT_USE_SIG
     212void 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
     232void RetrieveYoutubeUrl::fetchStreamPage(const QString & url) {
     233        qDebug() << "RetrieveYoutubeUrl::fetchStreamPage:" << url;
     234        dl_stream_page->fetchPage(url);
     235}
     236#endif
     237
     238void RetrieveYoutubeUrl::videoPageLoaded(QByteArray page) {
     239        qDebug() << "RetrieveYoutubeUrl::videoPageLoaded";
     240
     241        QString replyString = QString::fromUtf8(page);
    149242
    150243        QRegExp rx_title(".*<title>(.*)</title>.*");
    151244        if (rx_title.indexIn(replyString) != -1) {
    152245                url_title = rx_title.cap(1).simplified();
    153                 url_title = QString(url_title).replace("&amp;","&").replace("&gt;", ">").replace("&lt;", "<").replace("&quot;","\"").replace("&#39;","'")/*.replace(" - YouTube", "")*/;
    154                 qDebug("RetrieveYoutubeUrl::parse: title '%s'", url_title.toUtf8().constData());
     246                url_title = QString(url_title).replace("&amp;","&").replace("&gt;", ">").replace("&lt;", "<").replace("&quot;","\"").replace("&#39;","'");
     247                qDebug(;
    155248        } 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");
    163257        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
     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
    167292
    168293        QString fmtArray;
     
    172297        }
    173298
    174 #ifdef YT_DASH_SUPPORT
     299#ifdef YT_DASH_SUPPORT
    175300        QRegExp regex2("\\\"adaptive_fmts\\\"\\s*:\\s*\\\"([^\\\"]*)");
    176301        if (regex2.indexIn(replyString) != -1) {
     
    178303                fmtArray += regex2.cap(1);
    179304        }
    180 #endif
     305#endif
    181306
    182307        fmtArray = sanitizeForUnicodePoint(fmtArray);
    183308        fmtArray.replace(QRegExp("\\\\(.)"), "\\1");
    184309
    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
     331void 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
     373void 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
     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;
     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
     470void 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
     502QString 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
     532UrlMap RetrieveYoutubeUrl::extractURLs(QString fmtArray, bool allow_https, bool use_player) {
     533        UrlMap url_map;
     534
     535        failed_to_decrypt_signature = false;
    188536
    189537        #if QT_VERSION >= 0x050000
     
    191539        #endif
    192540
     541
     542
    193543        QList<QByteArray> codeList = fmtArray.toLatin1().split(',');
     544
     545
    194546        foreach(QByteArray code, codeList) {
    195547                code = QUrl::fromPercentEncoding(code).toLatin1();
    196                 //qDebug("code: %s", code.constData());
     548                //qDebug(;
    197549
    198550                QUrl line;
     
    212564                        #if QT_VERSION >= 0x050000
    213565                        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                         */
    220566                        #else
    221567                        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                         */
    228568                        #endif
    229569
     
    234574                        else
    235575                        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;
    238579                                #else
    239                                 QString signature = YTSig::aclara(q->queryItemValue("s"));
     580                                QString ;
    240581                                #endif
     582
    241583                                if (!signature.isEmpty()) {
    242584                                        q->addQueryItem("signature", signature);
    243585                                } else {
    244                                 #ifndef YT_GET_VIDEOINFO
    245586                                        failed_to_decrypt_signature = true;
     587
     588
     589
    246590                                #endif
    247                                 }
    248591                                q->removeQueryItem("s");
    249592                        }
     
    268611                                #endif
    269612
    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();
    273617                                }
    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();
    279620                        }
    280621                }
     
    285626        #endif
    286627
    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
     633int RetrieveYoutubeUrl::findPreferredUrl(const UrlMap & url_map, Quality q) {
    522634        // Choose a url according to preferred quality
    523635        QString p_url;
    524636        //Quality q = preferred_quality;
    525637
     638
     639
     640
     641
     642
     643
     644
     645
    526646        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())
    529649                if (p_url.isNull()) q = MP4_720p;
    530650        }
    531651
    532652        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())
    535655                if (p_url.isNull()) q = WEBM_720p;
    536656        }
    537657
    538658        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())
    542662                if (p_url.isNull()) q = MP4_360p;
    543663        }
    544664
    545665        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())
    548668                if (p_url.isNull()) q = WEBM_480p;
    549669        }
    550670
    551671        if (q==WEBM_480p) {
    552                 p_url = urlMap.value(WEBM_480p, QString());
     672               
    553673                if (p_url.isNull()) q = WEBM_360p;
    554674        }
    555675
    556676        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())
    559679                if (p_url.isNull()) q = FLV_360p;
    560680        }
    561681
    562682        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())
    565685                if (p_url.isNull()) q = FLV_360p;
    566686        }
     
    568688        // FLV, low priority
    569689        if (q==FLV_480p) {
    570                 p_url = urlMap.value(FLV_480p, QString());
     690               
    571691                if (p_url.isNull()) q = FLV_360p;
    572692        }
    573693
    574694        if (q==FLV_360p) {
    575                 p_url = urlMap.value(FLV_360p, QString());
     695               
    576696                if (p_url.isNull()) q = FLV_240p;
    577697        }
    578698
    579699        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}
    608712
    609713QString RetrieveYoutubeUrl::sanitizeForUnicodePoint(QString string) {
     
    615719}
    616720
    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 }
     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;
     747}
     748
     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
    626767
    627768#include "moc_retrieveyoutubeurl.cpp"
Note: See TracChangeset for help on using the changeset viewer.