QTfrontend/ui/dialog/upload_video.cpp
changeset 7507 3032a5739fe1
parent 7447 01111960a48d
child 7528 f8cf277dca36
equal deleted inserted replaced
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 &lt or &gt)
       
    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 &lt; &gt;)
       
   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 }