QTfrontend/ui/dialog/upload_video.cpp
branchios-develop
changeset 13413 ba39a1d396c0
parent 13411 6e8b807bda4b
parent 13412 236cc4cf2448
equal deleted inserted replaced
13411:6e8b807bda4b 13413:ba39a1d396c0
     1 /*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
       
     4  *
       
     5  * This program is free software; you can redistribute it and/or modify
       
     6  * it under the terms of the GNU General Public License as published by
       
     7  * the Free Software Foundation; version 2 of the License
       
     8  *
       
     9  * This program is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12  * GNU General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU General Public License
       
    15  * along with this program; if not, write to the Free Software
       
    16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    17  */
       
    18 
       
    19 #include <QLineEdit>
       
    20 #include <QDialogButtonBox>
       
    21 #include <QPushButton>
       
    22 #include <QGridLayout>
       
    23 #include <QCheckBox>
       
    24 #include <QLabel>
       
    25 #include <QFrame>
       
    26 #include <QPlainTextEdit>
       
    27 #include <QSslError>
       
    28 #include <QUrl>
       
    29 #include <QNetworkAccessManager>
       
    30 #include <QNetworkRequest>
       
    31 #include <QNetworkReply>
       
    32 #include <QMessageBox>
       
    33 #include <QRegExp>
       
    34 #include <QRegExpValidator>
       
    35 
       
    36 #include "upload_video.h"
       
    37 #include "hwconsts.h"
       
    38 
       
    39 // User-agent string used in http requests.
       
    40 // Don't make it a global varibale - crash on linux because of cVersionString
       
    41 #define USER_AGENT ("Hedgewars-QtFrontend/" + *cVersionString).toAscii()
       
    42 
       
    43 // This is developer key obtained from http://code.google.com/apis/youtube/dashboard/
       
    44 // If you are reusing this code outside Hedgewars, don't use this developer key,
       
    45 // obtain you own at http://code.google.com/apis/youtube/dashboard/
       
    46 static const QByteArray devKey = "AI39si5pKjxR0XgNIlmrEFF-LyYD31rps4g2O5dZTxLgD0fvJ2rHxrMrNFY8FYTZrzeI3VlaFVQLKfFnSBugvdZmy8vFzRDefQ";
       
    47 
       
    48 HWUploadVideoDialog::HWUploadVideoDialog(QWidget* parent, const QString &filename, QNetworkAccessManager* netManager) : QDialog(parent)
       
    49 {
       
    50     this->filename = filename;
       
    51     this->netManager = netManager;
       
    52 
       
    53     setWindowTitle(tr("Upload video"));
       
    54 
       
    55     // Google requires us to display this, see https://developers.google.com/youtube/terms
       
    56     QString GoogleNotice =
       
    57         "<p>By clicking 'upload,' you certify that you own all rights to the content or that "
       
    58         "you are authorized by the owner to make the content publicly available on YouTube, "
       
    59         "and that it otherwise complies with the YouTube Terms of Service located at "
       
    60         "<a href=\"https://www.youtube.com/t/terms\" style=\"color: white;\">https://www.youtube.com/t/terms</a>.</p>";
       
    61 
       
    62     // youtube doesn't understand this characters, even when they are properly escaped
       
    63     // (either with CDATA or with &lt or &gt)
       
    64     QRegExp rx("[^<>]*");
       
    65 
       
    66     int row = 0;
       
    67 
       
    68     QGridLayout * layout = new QGridLayout(this);
       
    69     layout->setColumnStretch(0, 1);
       
    70     layout->setColumnStretch(1, 2);
       
    71 
       
    72     QLabel * lbLabel = new QLabel(this);
       
    73     lbLabel->setWordWrap(true);
       
    74     lbLabel->setText(QLabel::tr(
       
    75                          "Please provide either the YouTube account name "
       
    76                          "or the email address associated with the Google Account."));
       
    77     layout->addWidget(lbLabel, row++, 0, 1, 2);
       
    78 
       
    79     lbLabel = new QLabel(this);
       
    80     lbLabel->setText(QLabel::tr("Account name (or email): "));
       
    81     layout->addWidget(lbLabel, row, 0);
       
    82 
       
    83     leAccount = new QLineEdit(this);
       
    84     layout->addWidget(leAccount, row++, 1);
       
    85 
       
    86     lbLabel = new QLabel(this);
       
    87     lbLabel->setText(QLabel::tr("Password: "));
       
    88     layout->addWidget(lbLabel, row, 0);
       
    89 
       
    90     lePassword = new QLineEdit(this);
       
    91     lePassword->setEchoMode(QLineEdit::Password);
       
    92     layout->addWidget(lePassword, row++, 1);
       
    93 
       
    94     cbSave = new QCheckBox(this);
       
    95     cbSave->setText(QCheckBox::tr("Save account name and password"));
       
    96     layout->addWidget(cbSave, row++, 0, 1, 2);
       
    97 
       
    98     QFrame * hr = new QFrame(this);
       
    99     hr->setFrameStyle(QFrame::HLine);
       
   100     hr->setLineWidth(3);
       
   101     hr->setFixedHeight(10);
       
   102     layout->addWidget(hr, row++, 0, 1, 2);
       
   103 
       
   104     lbLabel = new QLabel(this);
       
   105     lbLabel->setText(QLabel::tr("Video title: "));
       
   106     layout->addWidget(lbLabel, row, 0);
       
   107 
       
   108     leTitle = new QLineEdit(this);
       
   109     leTitle->setText(filename);
       
   110     leTitle->setValidator(new QRegExpValidator(rx, leTitle));
       
   111     layout->addWidget(leTitle, row++, 1);
       
   112 
       
   113     lbLabel = new QLabel(this);
       
   114     lbLabel->setText(QLabel::tr("Video description: "));
       
   115     layout->addWidget(lbLabel, row++, 0, 1, 2);
       
   116 
       
   117     teDescription = new QPlainTextEdit(this);
       
   118     layout->addWidget(teDescription, row++, 0, 1, 2);
       
   119 
       
   120     lbLabel = new QLabel(this);
       
   121     lbLabel->setText(QLabel::tr("Tags (comma separated): "));
       
   122     layout->addWidget(lbLabel, row, 0);
       
   123 
       
   124     leTags = new QLineEdit(this);
       
   125     leTags->setText("hedgewars");
       
   126     leTags->setMaxLength(500);
       
   127     leTags->setValidator(new QRegExpValidator(rx, leTags));
       
   128     layout->addWidget(leTags, row++, 1);
       
   129 
       
   130     cbPrivate = new QCheckBox(this);
       
   131     cbPrivate->setText(QCheckBox::tr("Video is private"));
       
   132     layout->addWidget(cbPrivate, row++, 0, 1, 2);
       
   133 
       
   134     hr = new QFrame(this);
       
   135         hr->setFrameStyle(QFrame::HLine);
       
   136         hr->setLineWidth(3);
       
   137         hr->setFixedHeight(10);
       
   138         layout->addWidget(hr, row++, 0, 1, 2);
       
   139 
       
   140     lbLabel = new QLabel(this);
       
   141     lbLabel->setWordWrap(true);
       
   142     lbLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
       
   143     lbLabel->setTextFormat(Qt::RichText);
       
   144     lbLabel->setOpenExternalLinks(true);
       
   145     lbLabel->setText(GoogleNotice);
       
   146     layout->addWidget(lbLabel, row++, 0, 1, 2);
       
   147 
       
   148     QDialogButtonBox* dbbButtons = new QDialogButtonBox(this);
       
   149     btnUpload = dbbButtons->addButton(tr("Upload"), QDialogButtonBox::ActionRole);
       
   150     QPushButton * pbCancel = dbbButtons->addButton(QDialogButtonBox::Cancel);
       
   151     layout->addWidget(dbbButtons, row++, 0, 1, 2);
       
   152 
       
   153    /* hr = new QFrame(this);
       
   154         hr->setFrameStyle(QFrame::HLine);
       
   155         hr->setLineWidth(3);
       
   156         hr->setFixedHeight(10);
       
   157         layout->addWidget(hr, row++, 0, 1, 2);*/
       
   158 
       
   159     connect(btnUpload, SIGNAL(clicked()), this, SLOT(upload()));
       
   160     connect(pbCancel, SIGNAL(clicked()), this, SLOT(reject()));
       
   161 
       
   162     this->setWindowModality(Qt::WindowModal);
       
   163 }
       
   164 
       
   165 void HWUploadVideoDialog::showEvent(QShowEvent * event)
       
   166 {
       
   167     QDialog::showEvent(event);
       
   168 
       
   169     // set width to the same value as height (otherwise dialog has too small width)
       
   170     QSize s = size();
       
   171     QPoint p = pos();
       
   172     resize(s.height(), s.height());
       
   173     move(p.x() - (s.height() - s.width())/2, p.y());
       
   174 }
       
   175 
       
   176 void HWUploadVideoDialog::setEditable(bool editable)
       
   177 {
       
   178     leTitle->setEnabled(editable);
       
   179     leAccount->setEnabled(editable);
       
   180     lePassword->setEnabled(editable);
       
   181     btnUpload->setEnabled(editable);
       
   182 }
       
   183 
       
   184 void HWUploadVideoDialog::upload()
       
   185 {
       
   186     setEditable(false);
       
   187 
       
   188     // Documentation is at https://developers.google.com/youtube/2.0/developers_guide_protocol_clientlogin#ClientLogin_Authentication
       
   189     QNetworkRequest request;
       
   190     request.setUrl(QUrl("https://www.google.com/accounts/ClientLogin"));
       
   191     request.setRawHeader("User-Agent", USER_AGENT);
       
   192     request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
       
   193 
       
   194     QString account(QUrl::toPercentEncoding(leAccount->text()));
       
   195     QString pass(QUrl::toPercentEncoding(lePassword->text()));
       
   196     QByteArray data = QString("Email=%1&Passwd=%2&service=youtube&source=Hedgewars").arg(account).arg(pass).toAscii();
       
   197 
       
   198     QNetworkReply *reply = netManager->post(request, data);
       
   199     connect(reply, SIGNAL(finished()), this, SLOT(authFinished()));
       
   200 }
       
   201 
       
   202 static QString XmlEscape(const QString& str)
       
   203 {
       
   204     QString str2 = str;
       
   205     // youtube doesn't understand this characters, even when they are properly escaped
       
   206     // (either with CDATA or with &lt; &gt;)
       
   207     str2.replace('<', ' ').replace('>', ' ');
       
   208     return "<![CDATA[" + str2.replace("]]>", "]]]]><![CDATA[>") + "]]>";
       
   209 }
       
   210 
       
   211 void HWUploadVideoDialog::authFinished()
       
   212 {
       
   213     QNetworkReply *reply = (QNetworkReply*)sender();
       
   214     reply->deleteLater();
       
   215 
       
   216     int HttpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
       
   217 
       
   218     QByteArray answer = reply->readAll();
       
   219     QString authToken = "";
       
   220     QList<QByteArray> lines = answer.split('\n');
       
   221     foreach (const QByteArray& line, lines)
       
   222     {
       
   223         QString str(line);
       
   224         if (!str.startsWith("Auth=", Qt::CaseInsensitive))
       
   225             continue;
       
   226         str.remove(0, 5);
       
   227         authToken = str;
       
   228         break;
       
   229     }
       
   230     if (authToken.isEmpty())
       
   231     {
       
   232         QString errorStr = QMessageBox::tr("Error while authenticating at google.com:\n");
       
   233         if (HttpCode == 403)
       
   234             errorStr += QMessageBox::tr("Login or password is incorrect");
       
   235         else
       
   236             errorStr += reply->errorString();
       
   237 
       
   238         QMessageBox deniedMsg(this);
       
   239         deniedMsg.setIcon(QMessageBox::Warning);
       
   240         deniedMsg.setWindowTitle(QMessageBox::tr("Video upload - Error"));
       
   241         deniedMsg.setText(errorStr);
       
   242         deniedMsg.setWindowModality(Qt::WindowModal);
       
   243         deniedMsg.exec();
       
   244 
       
   245         setEditable(true);
       
   246         return;
       
   247     }
       
   248 
       
   249     QByteArray auth = ("GoogleLogin auth=" + authToken).toAscii();
       
   250 
       
   251     // We have authenticated, now we can send metadata and start upload
       
   252     // Documentation is here: https://developers.google.com/youtube/2.0/developers_guide_protocol_resumable_uploads#Resumable_uploads
       
   253     QByteArray body =
       
   254             "<?xml version=\"1.0\"?>"
       
   255             "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
       
   256                 "xmlns:media=\"http://search.yahoo.com/mrss/\" "
       
   257                 "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">"
       
   258                 "<media:group>"
       
   259                   //  "<yt:incomplete/>"
       
   260                     "<media:category "
       
   261                         "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">Games"
       
   262                     "</media:category>"
       
   263                     "<media:title type=\"plain\">"
       
   264                         + XmlEscape(leTitle->text()).toUtf8() +
       
   265                     "</media:title>"
       
   266                     "<media:description type=\"plain\">"
       
   267                         + XmlEscape(teDescription->toPlainText()).toUtf8() +
       
   268                     "</media:description>"
       
   269                     "<media:keywords type=\"plain\">"
       
   270                         + XmlEscape(leTags->text()).toUtf8() +
       
   271                     "</media:keywords>"
       
   272                     + (cbPrivate->isChecked()? "<yt:private/>" : "") +
       
   273                 "</media:group>"
       
   274             "</entry>";
       
   275 
       
   276     QNetworkRequest request;
       
   277     request.setUrl(QUrl("http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads"));
       
   278     request.setRawHeader("User-Agent", USER_AGENT);
       
   279     request.setRawHeader("Authorization", auth);
       
   280     request.setRawHeader("GData-Version", "2");
       
   281     request.setRawHeader("X-GData-Key", "key=" + devKey);
       
   282     request.setRawHeader("Slug", filename.toUtf8());
       
   283     request.setRawHeader("Content-Type", "application/atom+xml; charset=UTF-8");
       
   284 
       
   285     reply = netManager->post(request, body);
       
   286     connect(reply, SIGNAL(finished()), this, SLOT(startUpload()));
       
   287 }
       
   288 
       
   289 void HWUploadVideoDialog::startUpload()
       
   290 {
       
   291     QNetworkReply *reply = (QNetworkReply*)sender();
       
   292     reply->deleteLater();
       
   293 
       
   294     location = QString::fromLatin1(reply->rawHeader("Location"));
       
   295     if (location.isEmpty())
       
   296     {
       
   297         QString errorStr = QMessageBox::tr("Error while sending metadata to youtube.com:\n");
       
   298         errorStr += reply->errorString();
       
   299 
       
   300         QMessageBox deniedMsg(this);
       
   301         deniedMsg.setIcon(QMessageBox::Warning);
       
   302         deniedMsg.setWindowTitle(QMessageBox::tr("Video upload - Error"));
       
   303         deniedMsg.setText(errorStr);
       
   304         deniedMsg.setWindowModality(Qt::WindowModal);
       
   305         deniedMsg.exec();
       
   306 
       
   307         setEditable(true);
       
   308         return;
       
   309     }
       
   310 
       
   311     accept();
       
   312 }