]> git.sur5r.net Git - minitube/blob - src/video.cpp
Demo stuff
[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 void Video::loadStreamUrl() {
31
32     // https://develop.participatoryculture.org/trac/democracy/browser/trunk/tv/portable/flashscraper.py
33
34     // Get Video ID
35     // youtube-dl line 428
36     // QRegExp re("^((?:http://)?(?:\\w+\\.)?youtube\\.com/(?:(?:v/)|(?:(?:watch(?:\\.php)?)?\\?(?:.+&)?v=)))?([0-9A-Za-z_-]+)(?(1).+)?$");
37     QRegExp re("^http://www\\.youtube\\.com/watch\\?v=([0-9A-Za-z_-]+).*");
38     bool match = re.exactMatch(m_webpage.toString());
39     if (!match || re.numCaptures() < 1) {
40         emit errorStreamUrl(QString("Cannot get video id for %1").arg(m_webpage.toString()));
41         return;
42     }
43     videoId = re.cap(1);
44
45     getVideoInfo();
46
47 }
48
49 void  Video::getVideoInfo() {
50     static const QStringList elTypes = QStringList() << "&el=embedded" << "&el=vevo" << "&el=detailpage" << "";
51
52     if (elIndex > elTypes.size() - 1) {
53         // Don't panic! We have a plan B.
54         // get the youtube video webpage
55         qDebug() << "Scraping" << webpage().toString();
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%2&ps=default&eurl=&gl=US&hl=en"
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     // get video token
80     QRegExp re = QRegExp("^.*&token=([^&]+).*$");
81     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
85         elIndex++;
86         getVideoInfo();
87         return;
88     }
89     QString videoToken = re.cap(1);
90     while (videoToken.contains('%'))
91         videoToken = QByteArray::fromPercentEncoding(videoToken.toAscii());
92     qDebug() << "videoToken" << videoToken;
93     this->videoToken = videoToken;
94
95     /*
96     // get fmt_url_map
97     re = QRegExp("^.*&fmt_url_map=([^&]+).*$");
98     match = re.exactMatch(videoInfo);
99     // handle regexp failure
100     if (!match || re.numCaptures() < 1) {
101         // Don't panic! We're gonna try another magic "el" param
102         elIndex++;
103         getVideoInfo();
104         return;
105     }
106     QString fmtUrlMap = re.cap(1);
107
108     while (fmtUrlMap.contains('%'))
109         fmtUrlMap = QByteArray::fromPercentEncoding(fmtUrlMap.toAscii());
110
111     qDebug() << "fmtUrlMap" << fmtUrlMap;
112     QStringList formatUrls = fmtUrlMap.split(",", QString::SkipEmptyParts);
113     foreach(QString formatUrl, formatUrls) {
114         int separator = formatUrl.indexOf("|");
115         if (separator == -1) continue;
116         int format = formatUrl.left(separator).toInt();
117         QString url = formatUrl.mid(separator + 1);
118         qDebug() << format << url;
119     }
120     */
121
122     QSettings settings;
123     QString definitionName = settings.value("definition").toString();
124     int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
125     if (definitionCode == 18) {
126         // This is assumed always available
127         foundVideoUrl(videoToken, 18);
128     } else {
129         findVideoUrl(definitionCode);
130     }
131
132 }
133
134 void Video::foundVideoUrl(QString videoToken, int definitionCode) {
135
136     QUrl videoUrl = QUrl(QString(
137             "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=&ps=&asv=&fmt=%3"
138             ).arg(videoId, videoToken, QString::number(definitionCode)));
139
140     m_streamUrl = videoUrl;
141     emit gotStreamUrl(videoUrl);
142 }
143
144 void Video::errorVideoInfo(QNetworkReply *reply) {
145     emit errorStreamUrl(tr("Network error: %1 for %2").arg(reply->errorString(), reply->url().toString()));
146 }
147
148 void Video::scrapeWebPage(QByteArray data) {
149
150     QString videoHTML = QString::fromUtf8(data);
151     QRegExp re(".*, \"t\": \"([^\"]+)\".*");
152     bool match = re.exactMatch(videoHTML);
153
154     // on regexp failure, stop and report error
155     if (!match || re.numCaptures() < 1) {
156         emit errorStreamUrl("Error parsing video page");
157         return;
158     }
159
160     QString videoToken = re.cap(1);
161     // FIXME proper decode
162     videoToken = videoToken.replace("%3D", "=");
163
164     // we'll need this in gotHeadHeaders()
165     this->videoToken = videoToken;
166
167     // qDebug() << "token" << videoToken;
168
169     QSettings settings;
170     QString definitionName = settings.value("definition").toString();
171     int definitionCode = VideoDefinition::getDefinitionCode(definitionName);
172     if (definitionCode == 18) {
173         // This is assumed always available
174         foundVideoUrl(videoToken, 18);
175     } else {
176         findVideoUrl(definitionCode);
177     }
178
179 }
180
181 void Video::gotHeadHeaders(QNetworkReply* reply) {
182     int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
183     // qDebug() << "gotHeaders" << statusCode;
184     if (statusCode == 200) {
185         foundVideoUrl(videoToken, definitionCode);
186     } else {
187
188         // try next (lower quality) definition
189         /*
190         QStringList definitionNames = VideoDefinition::getDefinitionNames();
191         int currentIndex = definitionNames.indexOf(currentDefinition);
192         int previousIndex = 0;
193         if (currentIndex > 0) {
194             previousIndex = currentIndex - 1;
195         }
196         if (previousIndex > 0) {
197             QString nextDefinitionName = definitionNames.at(previousIndex);
198             findVideoUrl(nextDefinitionName);
199         } else {
200             foundVideoUrl(videoToken, 18);
201         }*/
202
203
204         QList<int> definitionCodes = VideoDefinition::getDefinitionCodes();
205         int currentIndex = definitionCodes.indexOf(definitionCode);
206         int previousIndex = 0;
207         if (currentIndex > 0) {
208             previousIndex = currentIndex - 1;
209             int definitionCode = definitionCodes.at(previousIndex);
210             if (definitionCode == 18) {
211                 // This is assumed always available
212                 foundVideoUrl(videoToken, 18);
213             } else {
214                 findVideoUrl(definitionCode);
215             }
216
217         } else {
218             foundVideoUrl(videoToken, 18);
219         }
220
221     }
222 }
223
224 void Video::findVideoUrl(int definitionCode) {
225     this->definitionCode = definitionCode;
226
227     QUrl videoUrl = QUrl(QString(
228             "http://www.youtube.com/get_video?video_id=%1&t=%2&eurl=&el=&ps=&asv=&fmt=%3"
229             ).arg(videoId, videoToken, QString::number(definitionCode)));
230
231     QObject *reply = The::http()->head(videoUrl);
232     connect(reply, SIGNAL(finished(QNetworkReply*)), SLOT(gotHeadHeaders(QNetworkReply*)));
233     // connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(errorVideoInfo(QNetworkReply*)));
234
235     // see you in gotHeadHeaders()
236
237 }