]> git.sur5r.net Git - minitube/blob - src/ytjs/ytjsvideo.cpp
New upstream version 3.6.1
[minitube] / src / ytjs / ytjsvideo.cpp
1 #include "ytjsvideo.h"
2
3 #include "videodefinition.h"
4 #include "yt3.h"
5 #include "ytjs.h"
6
7 YTJSVideo::YTJSVideo(const QString &videoId, QObject *parent)
8     : QObject(parent), videoId(videoId), definitionCode(0) {}
9
10 void YTJSVideo::loadStreamUrl() {
11     auto &ytjs = YTJS::instance();
12     if (!ytjs.isInitialized()) {
13         QTimer::singleShot(500, this, [this] { loadStreamUrl(); });
14         return;
15     }
16     auto &engine = ytjs.getEngine();
17
18     if (loadingStreamUrl) return;
19     loadingStreamUrl = true;
20
21     auto function = engine.evaluate("videoInfo");
22     if (!function.isCallable()) {
23         qWarning() << function.toString() << " is not callable";
24         loadingStreamUrl = false;
25         emit errorStreamUrl(function.toString());
26         return;
27     }
28
29     auto handler = new ResultHandler;
30     connect(handler, &ResultHandler::error, this, &YTJSVideo::errorStreamUrl);
31     connect(handler, &ResultHandler::data, this, [this](const QJsonDocument &doc) {
32         auto obj = doc.object();
33
34         QMap<int, QString> urlMap;
35         const auto formats = obj["formats"].toArray();
36         for (const auto &format : formats) {
37             bool isDashMpd = format["isDashMPD"].toBool();
38             if (isDashMpd) continue;
39             int itag = format["itag"].toInt();
40             QString url = format["url"].toString();
41             // qDebug() << itag << url;
42             urlMap.insert(itag, url);
43         }
44         if (urlMap.isEmpty()) {
45             loadingStreamUrl = false;
46             emit errorStreamUrl("No formats");
47             return;
48         }
49
50         qDebug() << "available formats" << urlMap.keys();
51         const VideoDefinition &definition = YT3::instance().maxVideoDefinition();
52         const QVector<VideoDefinition> &definitions = VideoDefinition::getDefinitions();
53         int previousIndex = std::max(definitions.indexOf(definition), 0);
54         for (; previousIndex >= 0; previousIndex--) {
55             const VideoDefinition &previousDefinition = definitions.at(previousIndex);
56             qDebug() << "Testing format" << previousDefinition.getCode();
57             if (urlMap.contains(previousDefinition.getCode())) {
58                 qDebug() << "Found format" << previousDefinition.getCode();
59
60                 QString url = urlMap.value(previousDefinition.getCode());
61                 definitionCode = previousDefinition.getCode();
62
63                 QString audioUrl;
64                 if (!previousDefinition.hasAudio()) {
65                     qDebug() << "Finding audio format";
66                     static const QVector<int> audioFormats({251, 171, 140});
67                     for (int audioFormat : audioFormats) {
68                         qDebug() << "Trying audio format" << audioFormat;
69                         auto i = urlMap.constFind(audioFormat);
70                         if (i != urlMap.constEnd()) {
71                             qDebug() << "Found audio format" << i.value();
72                             audioUrl = i.value();
73                             break;
74                         }
75                     }
76                 }
77
78                 loadingStreamUrl = false;
79                 emit gotStreamUrl(url, audioUrl);
80                 return;
81             }
82         }
83
84         loadingStreamUrl = false;
85         emit errorStreamUrl(tr("Cannot get video stream for %1").arg(videoId));
86     });
87     QJSValue h = engine.newQObject(handler);
88     auto value = function.call({h, videoId});
89     ytjs.checkError(value);
90 }