2 #include "networkaccess.h"
4 #include "videodefinition.h"
10 Video::Video() : m_duration(0),
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)));
21 void Video::setThumbnail(QByteArray bytes) {
22 m_thumbnail = QImage::fromData(bytes);
26 const QImage Video::thumbnail() const {
30 static const QStringList elTypes = QStringList() << "embedded" << "vevo" << "detailpage";
32 void Video::loadStreamUrl() {
34 // https://develop.participatoryculture.org/trac/democracy/browser/trunk/tv/portable/flashscraper.py
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()));
51 void Video::getVideoInfo() {
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)
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)));
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*)));
72 // see you in gotVideoInfo...
76 void Video::gotVideoInfo(QByteArray data) {
77 QString videoInfo = QString::fromUtf8(data);
79 QRegExp re = QRegExp("^.*&token=([^&]+).*$");
80 bool match = re.exactMatch(videoInfo);
82 // handle regexp failure
83 if (!match || re.numCaptures() < 1) {
84 // Don't panic! We're gonna try another magic "el" param
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;
96 // qDebug() << "token" << videoToken;
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);
105 findVideoUrl(definitionCode);
110 void Video::foundVideoUrl(QString videoToken, int definitionCode) {
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)));
116 m_streamUrl = videoUrl;
117 emit gotStreamUrl(videoUrl);
120 void Video::errorVideoInfo(QNetworkReply *reply) {
121 emit errorStreamUrl(tr("Network error: %1 for %2").arg(reply->errorString(), reply->url().toString()));
124 void Video::scrapeWebPage(QByteArray data) {
126 QString videoHTML = QString::fromUtf8(data);
127 QRegExp re(".*, \"t\": \"([^\"]+)\".*");
128 bool match = re.exactMatch(videoHTML);
130 // on regexp failure, stop and report error
131 if (!match || re.numCaptures() < 1) {
132 emit errorStreamUrl("Error parsing video page");
136 QString videoToken = re.cap(1);
137 // FIXME proper decode
138 videoToken = videoToken.replace("%3D", "=");
140 // we'll need this in gotHeadHeaders()
141 this->videoToken = videoToken;
143 // qDebug() << "token" << videoToken;
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);
152 findVideoUrl(definitionCode);
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);
164 // try next (lower quality) definition
166 QStringList definitionNames = VideoDefinition::getDefinitionNames();
167 int currentIndex = definitionNames.indexOf(currentDefinition);
168 int previousIndex = 0;
169 if (currentIndex > 0) {
170 previousIndex = currentIndex - 1;
172 if (previousIndex > 0) {
173 QString nextDefinitionName = definitionNames.at(previousIndex);
174 findVideoUrl(nextDefinitionName);
176 foundVideoUrl(videoToken, 18);
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);
190 findVideoUrl(definitionCode);
194 foundVideoUrl(videoToken, 18);
200 void Video::findVideoUrl(int definitionCode) {
201 this->definitionCode = definitionCode;
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)));
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*)));
211 // see you in gotHeadHeaders()