]> git.sur5r.net Git - minitube/blob - src/video.cpp
635c95c07cb85d5119ba06047acf45b22f1b2303
[minitube] / src / video.cpp
1 #include "video.h"
2 #include "networkaccess.h"
3 #include <QtNetwork>
4 #include "videodefinition.h"
5
6 namespace The {
7     NetworkAccess* http();
8 }
9
10 Video::Video() : m_duration(0),
11 m_viewCount(-1),
12 definitionCode(0),
13 elIndex(0) { }
14
15 void Video::preloadThumbnail() {
16     if (m_thumbnailUrls.isEmpty()) return;
17     QObject *reply = The::http()->get(m_thumbnailUrls.first());
18     connect(reply, SIGNAL(data(QByteArray)), SLOT(setThumbnail(QByteArray)));
19 }
20
21 void Video::setThumbnail(QByteArray bytes) {
22     m_thumbnail = QImage::fromData(bytes);
23     emit gotThumbnail();
24 }
25
26 const QImage Video::thumbnail() const {
27     return m_thumbnail;
28 }
29
30 static const QStringList elTypes = QStringList() << "embedded" << "vevo" << "detailpage";
31
32 void Video::loadStreamUrl() {
33
34     // https://develop.participatoryculture.org/trac/democracy/browser/trunk/tv/portable/flashscraper.py
35
36     // Get Video ID
37     // youtube-dl line 428
38     // QRegExp re("^((?:http://)?(?:\\w+\\.)?youtube\\.com/(?:(?:v/)|(?:(?:watch(?:\\.php)?)?\\?(?:.+&)?v=)))?([0-9A-Za-z_-]+)(?(1).+)?$");
39     QRegExp re("^http://www\\.youtube\\.com/watch\\?v=([0-9A-Za-z_-]+).*");
40     bool match = re.exactMatch(m_webpage.toString());
41     if (!match || re.numCaptures() < 1) {
42         emit errorStreamUrl(QString("Cannot get video id for %1").arg(m_webpage.toString()));
43         return;
44     }
45     videoId = re.cap(1);
46
47     getVideoInfo();
48
49 }
50
51 void  Video::getVideoInfo() {
52
53     if (elIndex > elTypes.size() - 1) {
54         // Don't panic! We have a plan B.
55         // get the youtube video webpage
56         QObject *reply = The::http()->get(webpage().toString());
57         connect(reply, SIGNAL(data(QByteArray)), SLOT(scrapeWebPage(QByteArray)));
58         connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
59         // see you in scrapWebPage(QByteArray)
60         return;
61     }
62
63     // Get Video Token
64     QUrl videoInfoUrl = QUrl(QString(
65             "http://www.youtube.com/get_video_info?video_id=%1&el=%2&ps=default&eurl="
66             ).arg(videoId, elTypes.at(elIndex)));
67
68     QObject *reply = The::http()->get(videoInfoUrl);
69     connect(reply, SIGNAL(data(QByteArray)), SLOT(gotVideoInfo(QByteArray)));
70     connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
71
72     // see you in gotVideoInfo...
73
74 }
75
76 void  Video::gotVideoInfo(QByteArray data) {
77     QString videoInfo = QString::fromUtf8(data);
78
79     QRegExp re = QRegExp("^.*&token=([^&]+).*$");
80     bool match = re.exactMatch(videoInfo);
81
82     // handle regexp failure
83     if (!match || re.numCaptures() < 1) {
84         // Don't panic! We're gonna try another magic "el" param
85         elIndex++;
86         getVideoInfo();
87         return;
88     }
89
90     QString videoToken = re.cap(1);
91     // FIXME proper decode
92     videoToken = videoToken.replace("%3D", "=");
93     // we'll need this in gotHeadHeaders()
94     this->videoToken = videoToken;
95
96     // qDebug() << "token" << videoToken;
97
98     QSettings settings;
99     QString definitionName = settings.value("definition").toString();
100     int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
101     if (definitionCode == 18) {
102         // This is assumed always available
103         foundVideoUrl(videoToken, 18);
104     } else {
105         findVideoUrl(definitionCode);
106     }
107
108 }
109
110 void Video::foundVideoUrl(QString videoToken, int definitionCode) {
111
112     QUrl videoUrl = QUrl(QString(
113             "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=embedded&ps=default&fmt=%3"
114             ).arg(videoId, videoToken, QString::number(definitionCode)));
115
116     m_streamUrl = videoUrl;
117     emit gotStreamUrl(videoUrl);
118 }
119
120 void Video::errorVideoInfo(QNetworkReply *reply) {
121     emit errorStreamUrl(tr("Network error: %1 for %2").arg(reply->errorString(), reply->url().toString()));
122 }
123
124 void Video::scrapeWebPage(QByteArray data) {
125
126     QString videoHTML = QString::fromUtf8(data);
127     QRegExp re(".*, \"t\": \"([^\"]+)\".*");
128     bool match = re.exactMatch(videoHTML);
129
130     // on regexp failure, stop and report error
131     if (!match || re.numCaptures() < 1) {
132         emit errorStreamUrl("Error parsing video page");
133         return;
134     }
135
136     QString videoToken = re.cap(1);
137     // FIXME proper decode
138     videoToken = videoToken.replace("%3D", "=");
139
140     // we'll need this in gotHeadHeaders()
141     this->videoToken = videoToken;
142
143     // qDebug() << "token" << videoToken;
144
145     QSettings settings;
146     QString definitionName = settings.value("definition").toString();
147     int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
148     if (definitionCode == 18) {
149         // This is assumed always available
150         foundVideoUrl(videoToken, 18);
151     } else {
152         findVideoUrl(definitionCode);
153     }
154
155 }
156
157 void Video::gotHeadHeaders(QNetworkReply* reply) {
158     int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
159     // qDebug() << "gotHeaders" << statusCode;
160     if (statusCode == 200) {
161         foundVideoUrl(videoToken, definitionCode);
162     } else {
163
164         // try next (lower quality) definition
165         /*
166         QStringList definitionNames = VideoDefinition::getDefinitionNames();
167         int currentIndex = definitionNames.indexOf(currentDefinition);
168         int previousIndex = 0;
169         if (currentIndex > 0) {
170             previousIndex = currentIndex - 1;
171         }
172         if (previousIndex > 0) {
173             QString nextDefinitionName = definitionNames.at(previousIndex);
174             findVideoUrl(nextDefinitionName);
175         } else {
176             foundVideoUrl(videoToken, 18);
177         }*/
178
179
180         QList<int> definitionCodes = VideoDefinition::getDefinitionCodes();
181         int currentIndex = definitionCodes.indexOf(definitionCode);
182         int previousIndex = 0;
183         if (currentIndex > 0) {
184             previousIndex = currentIndex - 1;
185             int definitionCode = definitionCodes.at(previousIndex);
186             if (definitionCode == 18) {
187                 // This is assumed always available
188                 foundVideoUrl(videoToken, 18);
189             } else {
190                 findVideoUrl(definitionCode);
191             }
192
193         } else {
194             foundVideoUrl(videoToken, 18);
195         }
196
197     }
198 }
199
200 void Video::findVideoUrl(int definitionCode) {
201     this->definitionCode = definitionCode;
202
203     QUrl videoUrl = QUrl(QString(
204             "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=embedded&ps=default&fmt=%3"
205             ).arg(videoId, videoToken, QString::number(definitionCode)));
206
207     QObject *reply = The::http()->head(videoUrl);
208     connect(reply, SIGNAL(finished(QNetworkReply*)), SLOT(gotHeadHeaders(QNetworkReply*)));
209     // connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
210
211     // see you in gotHeadHeaders()
212
213 }