changeset 7507 | 3032a5739fe1 |
parent 7447 | 01111960a48d |
child 7528 | f8cf277dca36 |
7503:deaeac102355 | 7507:3032a5739fe1 |
---|---|
27 #include <QSslError> |
27 #include <QSslError> |
28 #include <QUrl> |
28 #include <QUrl> |
29 #include <QNetworkAccessManager> |
29 #include <QNetworkAccessManager> |
30 #include <QNetworkRequest> |
30 #include <QNetworkRequest> |
31 #include <QNetworkReply> |
31 #include <QNetworkReply> |
32 #include <QMessageBox> |
|
33 #include <QRegExp> |
|
34 #include <QRegExpValidator> |
|
35 #include <QMessageBox> |
|
32 |
36 |
33 #include "upload_video.h" |
37 #include "upload_video.h" |
34 #include "hwconsts.h" |
38 #include "hwconsts.h" |
35 |
39 |
36 // User-agent string used in http requests. |
40 // User-agent string used in http requests. |
48 |
52 |
49 setWindowTitle(tr("Upload video")); |
53 setWindowTitle(tr("Upload video")); |
50 |
54 |
51 // Google requires us to display this, see https://developers.google.com/youtube/terms |
55 // Google requires us to display this, see https://developers.google.com/youtube/terms |
52 QString GoogleNotice = |
56 QString GoogleNotice = |
53 "By clicking 'upload,' you certify that you own all rights to the content or that\n" |
57 "By clicking 'upload,' you certify that you own all rights to the content or that " |
54 "you are authorized by the owner to make the content publicly available on YouTube,\n" |
58 "you are authorized by the owner to make the content publicly available on YouTube, " |
55 "and that it otherwise complies with the YouTube Terms of Service located at\n" |
59 "and that it otherwise complies with the YouTube Terms of Service located at " |
56 "http://www.youtube.com/t/terms."; |
60 "http://www.youtube.com/t/terms."; |
57 |
61 |
62 // youtube doesn't understand this characters, even when they are properly escaped |
|
63 // (either with CDATA or with < or >) |
|
64 QRegExp rx("[^<>]*"); |
|
65 |
|
66 int row = 0; |
|
67 |
|
58 QGridLayout * layout = new QGridLayout(this); |
68 QGridLayout * layout = new QGridLayout(this); |
59 |
69 |
60 QLabel * lbLabel = new QLabel(this); |
70 QLabel * lbLabel = new QLabel(this); |
71 lbLabel->setWordWrap(true); |
|
61 lbLabel->setText(QLabel::tr( |
72 lbLabel->setText(QLabel::tr( |
62 "Please provide either the YouTube account name\n" |
73 "Please provide either the YouTube account name " |
63 "or the email address associated with the Google Account.")); |
74 "or the email address associated with the Google Account.")); |
64 layout->addWidget(lbLabel, 0, 0, 1, 2); |
75 layout->addWidget(lbLabel, row++, 0, 1, 2); |
65 |
76 |
66 lbLabel = new QLabel(this); |
77 lbLabel = new QLabel(this); |
67 lbLabel->setText(QLabel::tr("Account name (or email): ")); |
78 lbLabel->setText(QLabel::tr("Account name (or email): ")); |
68 layout->addWidget(lbLabel, 1, 0); |
79 layout->addWidget(lbLabel, row, 0); |
69 |
80 |
70 leAccount = new QLineEdit(this); |
81 leAccount = new QLineEdit(this); |
71 layout->addWidget(leAccount, 1, 1); |
82 layout->addWidget(leAccount, row++, 1); |
72 |
83 |
73 lbLabel = new QLabel(this); |
84 lbLabel = new QLabel(this); |
74 lbLabel->setText(QLabel::tr("Password: ")); |
85 lbLabel->setText(QLabel::tr("Password: ")); |
75 layout->addWidget(lbLabel, 2, 0); |
86 layout->addWidget(lbLabel, row, 0); |
76 |
87 |
77 lePassword = new QLineEdit(this); |
88 lePassword = new QLineEdit(this); |
78 lePassword->setEchoMode(QLineEdit::Password); |
89 lePassword->setEchoMode(QLineEdit::Password); |
79 layout->addWidget(lePassword, 2, 1); |
90 layout->addWidget(lePassword, row++, 1); |
80 |
91 |
81 cbSave = new QCheckBox(this); |
92 cbSave = new QCheckBox(this); |
82 cbSave->setText(QCheckBox::tr("Save account name and password")); |
93 cbSave->setText(QCheckBox::tr("Save account name and password")); |
83 layout->addWidget(cbSave, 3, 0, 1, 2); |
94 layout->addWidget(cbSave, row++, 0, 1, 2); |
84 |
95 |
85 QFrame * hr = new QFrame(this); |
96 QFrame * hr = new QFrame(this); |
86 hr->setFrameStyle(QFrame::HLine); |
97 hr->setFrameStyle(QFrame::HLine); |
87 hr->setLineWidth(3); |
98 hr->setLineWidth(3); |
88 hr->setFixedHeight(10); |
99 hr->setFixedHeight(10); |
89 layout->addWidget(hr, 4, 0, 1, 2); |
100 layout->addWidget(hr, row++, 0, 1, 2); |
90 |
101 |
91 lbLabel = new QLabel(this); |
102 lbLabel = new QLabel(this); |
92 lbLabel->setText(QLabel::tr("Video title: ")); |
103 lbLabel->setText(QLabel::tr("Video title: ")); |
93 layout->addWidget(lbLabel, 5, 0); |
104 layout->addWidget(lbLabel, row, 0); |
94 |
105 |
95 leTitle = new QLineEdit(this); |
106 leTitle = new QLineEdit(this); |
96 leTitle->setText(filename); |
107 leTitle->setText(filename); |
97 layout->addWidget(leTitle, 5, 1); |
108 leTitle->setValidator(new QRegExpValidator(rx)); |
109 layout->addWidget(leTitle, row++, 1); |
|
98 |
110 |
99 lbLabel = new QLabel(this); |
111 lbLabel = new QLabel(this); |
100 lbLabel->setText(QLabel::tr("Video description: ")); |
112 lbLabel->setText(QLabel::tr("Video description: ")); |
101 layout->addWidget(lbLabel, 6, 0, 1, 2); |
113 layout->addWidget(lbLabel, row++, 0, 1, 2); |
102 |
114 |
103 teDescription = new QPlainTextEdit(this); |
115 teDescription = new QPlainTextEdit(this); |
104 layout->addWidget(teDescription, 7, 0, 1, 2); |
116 layout->addWidget(teDescription, row++, 0, 1, 2); |
117 |
|
118 lbLabel = new QLabel(this); |
|
119 lbLabel->setText(QLabel::tr("Tags (comma separated): ")); |
|
120 layout->addWidget(lbLabel, row, 0); |
|
121 |
|
122 leTags = new QLineEdit(this); |
|
123 leTags->setText("hedgewars"); |
|
124 leTags->setMaxLength(500); |
|
125 leTags->setValidator(new QRegExpValidator(rx)); |
|
126 layout->addWidget(leTags, row++, 1); |
|
127 |
|
128 cbPrivate = new QCheckBox(this); |
|
129 cbPrivate->setText(QCheckBox::tr("Video is private")); |
|
130 layout->addWidget(cbPrivate, row++, 0, 1, 2); |
|
105 |
131 |
106 hr = new QFrame(this); |
132 hr = new QFrame(this); |
107 hr->setFrameStyle(QFrame::HLine); |
133 hr->setFrameStyle(QFrame::HLine); |
108 hr->setLineWidth(3); |
134 hr->setLineWidth(3); |
109 hr->setFixedHeight(10); |
135 hr->setFixedHeight(10); |
110 layout->addWidget(hr, 8, 0, 1, 2); |
136 layout->addWidget(hr, row++, 0, 1, 2); |
111 |
137 |
112 lbLabel = new QLabel(this); |
138 lbLabel = new QLabel(this); |
139 lbLabel->setWordWrap(true); |
|
113 lbLabel->setText(GoogleNotice); |
140 lbLabel->setText(GoogleNotice); |
114 layout->addWidget(lbLabel, 9, 0, 1, 2); |
141 layout->addWidget(lbLabel, row++, 0, 1, 2); |
115 |
|
116 labelLog = new QLabel(this); |
|
117 layout->addWidget(labelLog, 10, 0, 1, 2); |
|
118 |
142 |
119 QDialogButtonBox* dbbButtons = new QDialogButtonBox(this); |
143 QDialogButtonBox* dbbButtons = new QDialogButtonBox(this); |
120 btnUpload = dbbButtons->addButton(tr("Upload"), QDialogButtonBox::ActionRole); |
144 btnUpload = dbbButtons->addButton(tr("Upload"), QDialogButtonBox::ActionRole); |
121 QPushButton * pbCancel = dbbButtons->addButton(QDialogButtonBox::Cancel); |
145 QPushButton * pbCancel = dbbButtons->addButton(QDialogButtonBox::Cancel); |
122 layout->addWidget(dbbButtons, 11, 0, 1, 2); |
146 layout->addWidget(dbbButtons, row++, 0, 1, 2); |
147 |
|
148 /* hr = new QFrame(this); |
|
149 hr->setFrameStyle(QFrame::HLine); |
|
150 hr->setLineWidth(3); |
|
151 hr->setFixedHeight(10); |
|
152 layout->addWidget(hr, row++, 0, 1, 2);*/ |
|
123 |
153 |
124 connect(btnUpload, SIGNAL(clicked()), this, SLOT(upload())); |
154 connect(btnUpload, SIGNAL(clicked()), this, SLOT(upload())); |
125 connect(pbCancel, SIGNAL(clicked()), this, SLOT(reject())); |
155 connect(pbCancel, SIGNAL(clicked()), this, SLOT(reject())); |
126 } |
|
127 |
|
128 void HWUploadVideoDialog::log(const QString& text) |
|
129 { |
|
130 labelLog->setText(labelLog->text() + text); |
|
131 } |
156 } |
132 |
157 |
133 void HWUploadVideoDialog::setEditable(bool editable) |
158 void HWUploadVideoDialog::setEditable(bool editable) |
134 { |
159 { |
135 leTitle->setEnabled(editable); |
160 leTitle->setEnabled(editable); |
139 } |
164 } |
140 |
165 |
141 void HWUploadVideoDialog::upload() |
166 void HWUploadVideoDialog::upload() |
142 { |
167 { |
143 setEditable(false); |
168 setEditable(false); |
144 |
|
145 labelLog->clear(); |
|
146 log(tr("Authenticating at www.google.com... ")); |
|
147 |
169 |
148 // Documentation is at https://developers.google.com/youtube/2.0/developers_guide_protocol_clientlogin#ClientLogin_Authentication |
170 // Documentation is at https://developers.google.com/youtube/2.0/developers_guide_protocol_clientlogin#ClientLogin_Authentication |
149 QNetworkRequest request; |
171 QNetworkRequest request; |
150 request.setUrl(QUrl("https://www.google.com/accounts/ClientLogin")); |
172 request.setUrl(QUrl("https://www.google.com/accounts/ClientLogin")); |
151 request.setRawHeader("User-Agent", UserAgent); |
173 request.setRawHeader("User-Agent", UserAgent); |
157 |
179 |
158 QNetworkReply *reply = netManager->post(request, data); |
180 QNetworkReply *reply = netManager->post(request, data); |
159 connect(reply, SIGNAL(finished()), this, SLOT(authFinished())); |
181 connect(reply, SIGNAL(finished()), this, SLOT(authFinished())); |
160 } |
182 } |
161 |
183 |
162 QString XmlEscape(const QString& str) |
184 static QString XmlEscape(const QString& str) |
163 { |
185 { |
164 QString str2 = str; |
186 QString str2 = str; |
187 // youtube doesn't understand this characters, even when they are properly escaped |
|
188 // (either with CDATA or with < >) |
|
189 str2.replace('<', ' ').replace('>', ' '); |
|
165 return "<![CDATA[" + str2.replace("]]>", "]]]]><![CDATA[>") + "]]>"; |
190 return "<![CDATA[" + str2.replace("]]>", "]]]]><![CDATA[>") + "]]>"; |
166 } |
191 } |
167 |
192 |
168 void HWUploadVideoDialog::authFinished() |
193 void HWUploadVideoDialog::authFinished() |
169 { |
194 { |
170 QNetworkReply *reply = (QNetworkReply*)sender(); |
195 QNetworkReply *reply = (QNetworkReply*)sender(); |
171 reply->deleteLater(); |
196 reply->deleteLater(); |
197 |
|
198 int HttpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
|
172 |
199 |
173 QByteArray answer = reply->readAll(); |
200 QByteArray answer = reply->readAll(); |
174 QString authToken = ""; |
201 QString authToken = ""; |
175 QList<QByteArray> lines = answer.split('\n'); |
202 QList<QByteArray> lines = answer.split('\n'); |
176 foreach (const QByteArray& line, lines) |
203 foreach (const QByteArray& line, lines) |
182 authToken = str; |
209 authToken = str; |
183 break; |
210 break; |
184 } |
211 } |
185 if (authToken.isEmpty()) |
212 if (authToken.isEmpty()) |
186 { |
213 { |
187 log(tr("failed\n")); |
214 QString errorStr = QMessageBox::tr("Error while authenticating at google.com:\n"); |
188 log(reply->errorString() + "\n"); |
215 if (HttpCode == 403) |
216 errorStr += QMessageBox::tr("Login or password is incorrect"); |
|
217 else |
|
218 errorStr += reply->errorString(); |
|
219 QMessageBox::warning(this, QMessageBox::tr("Error"), errorStr); |
|
189 setEditable(true); |
220 setEditable(true); |
190 return; |
221 return; |
191 } |
222 } |
192 log(tr("Ok\n")); |
|
193 |
|
194 log(tr("Sending metadata... ")); |
|
195 |
223 |
196 QByteArray auth = ("GoogleLogin auth=" + authToken).toAscii(); |
224 QByteArray auth = ("GoogleLogin auth=" + authToken).toAscii(); |
197 |
225 |
198 // We have authenticated, now we can send metadata and start upload |
226 // We have authenticated, now we can send metadata and start upload |
199 // Documentation is here: https://developers.google.com/youtube/2.0/developers_guide_protocol_resumable_uploads#Resumable_uploads |
227 // Documentation is here: https://developers.google.com/youtube/2.0/developers_guide_protocol_resumable_uploads#Resumable_uploads |
201 "<?xml version=\"1.0\"?>" |
229 "<?xml version=\"1.0\"?>" |
202 "<entry xmlns=\"http://www.w3.org/2005/Atom\" " |
230 "<entry xmlns=\"http://www.w3.org/2005/Atom\" " |
203 "xmlns:media=\"http://search.yahoo.com/mrss/\" " |
231 "xmlns:media=\"http://search.yahoo.com/mrss/\" " |
204 "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">" |
232 "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">" |
205 "<media:group>" |
233 "<media:group>" |
206 "<yt:incomplete/>" |
234 // "<yt:incomplete/>" |
207 "<media:category " |
235 "<media:category " |
208 "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">Games" |
236 "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">Games" |
209 "</media:category>" |
237 "</media:category>" |
210 "<media:title type=\"plain\">" |
238 "<media:title type=\"plain\">" |
211 + XmlEscape(leTitle->text()).toUtf8() + |
239 + XmlEscape(leTitle->text()).toUtf8() + |
212 "</media:title>" |
240 "</media:title>" |
241 "<media:description type=\"plain\">" |
|
242 + XmlEscape(teDescription->toPlainText()).toUtf8() + |
|
243 "</media:description>" |
|
244 "<media:keywords type=\"plain\">" |
|
245 + XmlEscape(leTags->text()).toUtf8() + |
|
246 "</media:keywords>" |
|
247 + (cbPrivate->isChecked()? "<yt:private/>" : "") + |
|
213 "</media:group>" |
248 "</media:group>" |
214 "</entry>"; |
249 "</entry>"; |
215 |
250 |
216 QNetworkRequest request; |
251 QNetworkRequest request; |
217 request.setUrl(QUrl("http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads")); |
252 request.setUrl(QUrl("http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads")); |
232 reply->deleteLater(); |
267 reply->deleteLater(); |
233 |
268 |
234 location = QString::fromAscii(reply->rawHeader("Location")); |
269 location = QString::fromAscii(reply->rawHeader("Location")); |
235 if (location.isEmpty()) |
270 if (location.isEmpty()) |
236 { |
271 { |
237 log(tr("failed\n")); |
272 QString errorStr = QMessageBox::tr("Error while sending metadata to youtube.com:\n"); |
238 log(reply->errorString() + "\n"); |
273 errorStr += reply->errorString(); |
274 QMessageBox::warning(this, QMessageBox::tr("Error"), errorStr); |
|
239 setEditable(true); |
275 setEditable(true); |
240 return; |
276 return; |
241 } |
277 } |
242 |
278 |
243 log(tr("Ok\n")); |
|
244 accept(); |
279 accept(); |
245 } |
280 } |