uploading to youtube, it works, but user interface is still incomplete
authorStepan777 <stepik-777@mail.ru>
Thu, 26 Jul 2012 21:58:25 +0400
changeset 7447 01111960a48d
parent 7442 9bb6abdb5675
child 7503 deaeac102355
uploading to youtube, it works, but user interface is still incomplete
QTfrontend/binds.h
QTfrontend/res/css/birthday.css
QTfrontend/res/css/christmas.css
QTfrontend/res/css/easter.css
QTfrontend/res/css/qt.css
QTfrontend/ui/dialog/ask_quit.cpp
QTfrontend/ui/dialog/upload_video.cpp
QTfrontend/ui/dialog/upload_video.h
QTfrontend/ui/page/pagevideos.cpp
QTfrontend/ui/page/pagevideos.h
hedgewars/hwengine.pas
hedgewars/uGame.pas
hedgewars/uGearsHedgehog.pas
project_files/hedgewars.pro
--- a/QTfrontend/binds.h	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/binds.h	Thu Jul 26 21:58:25 2012 +0400
@@ -21,7 +21,7 @@
 
 #include <QString>
 
-#define BINDS_NUMBER 45
+#define BINDS_NUMBER 46
 
 struct BindAction
 {
--- a/QTfrontend/res/css/birthday.css	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/res/css/birthday.css	Thu Jul 26 21:58:25 2012 +0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/christmas.css	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/res/css/christmas.css	Thu Jul 26 21:58:25 2012 +0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/easter.css	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/res/css/easter.css	Thu Jul 26 21:58:25 2012 +0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/res/css/qt.css	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/res/css/qt.css	Thu Jul 26 21:58:25 2012 +0400
@@ -33,7 +33,7 @@
 a { color:#c8c8ff; }
 
 QLineEdit, QListWidget, QTableView, QTextBrowser, QSpinBox, QComboBox,
-QComboBox QAbstractItemView, QMenu::item {
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item {
 background-color: rgba(13, 5, 68, 70%);
 }
 
@@ -42,7 +42,7 @@
 }
 
 QPushButton, QListWidget, QTableView, QLineEdit, QHeaderView,
-QTextBrowser, QSpinBox, QToolBox, QComboBox,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, GameCFGWidget, TeamSelWidget, SelWeaponWidget,
 QTabWidget::pane, QTabBar::tab {
@@ -57,14 +57,14 @@
 }
 
 QLineEdit, QListWidget,QTableView, QTextBrowser,
-QSpinBox, QToolBox {
+QSpinBox, QToolBox, QPlainTextEdit {
 border-radius: 10px;
 }
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QTableView,
 QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, GameCFGWidget, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
 font: bold 13px;
 }
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
--- a/QTfrontend/ui/dialog/ask_quit.cpp	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/ui/dialog/ask_quit.cpp	Thu Jul 26 21:58:25 2012 +0400
@@ -35,7 +35,7 @@
     QVBoxLayout * layout = new QVBoxLayout(this);
 
     QLabel * lbLabel = new QLabel(this);
-    lbLabel->setText(QLabel::tr("There are videos that are currently being encoded.\n"
+    lbLabel->setText(QLabel::tr("There are videos that are currently being processed.\n"
                                 "Exiting now will abort them.\n"
                                 "Do yot really want to quit?"));
     layout->addWidget(lbLabel);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/upload_video.cpp	Thu Jul 26 21:58:25 2012 +0400
@@ -0,0 +1,245 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QLineEdit>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QGridLayout>
+#include <QCheckBox>
+#include <QLabel>
+#include <QFrame>
+#include <QPlainTextEdit>
+#include <QSslError>
+#include <QUrl>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+
+#include "upload_video.h"
+#include "hwconsts.h"
+
+// User-agent string used in http requests.
+static const QByteArray UserAgent = ("Hedgewars-QtFrontend/" + *cVersionString).toAscii();
+
+// This is developer key obtained from http://code.google.com/apis/youtube/dashboard/
+// If you are reusing this code outside Hedgewars, don't use this developer key,
+// obtain you own at http://code.google.com/apis/youtube/dashboard/
+static const QByteArray devKey = "AI39si5pKjxR0XgNIlmrEFF-LyYD31rps4g2O5dZTxLgD0fvJ2rHxrMrNFY8FYTZrzeI3VlaFVQLKfFnSBugvdZmy8vFzRDefQ";
+
+HWUploadVideoDialog::HWUploadVideoDialog(QWidget* parent, const QString &filename, QNetworkAccessManager* netManager) : QDialog(parent)
+{
+    this->filename = filename;
+    this->netManager = netManager;
+
+    setWindowTitle(tr("Upload video"));
+
+    // Google requires us to display this, see https://developers.google.com/youtube/terms
+    QString GoogleNotice =
+        "By clicking 'upload,' you certify that you own all rights to the content or that\n"
+        "you are authorized by the owner to make the content publicly available on YouTube,\n"
+        "and that it otherwise complies with the YouTube Terms of Service located at\n"
+        "http://www.youtube.com/t/terms.";
+
+    QGridLayout * layout = new QGridLayout(this);
+
+    QLabel * lbLabel = new QLabel(this);
+    lbLabel->setText(QLabel::tr(
+                         "Please provide either the YouTube account name\n"
+                         "or the email address associated with the Google Account."));
+    layout->addWidget(lbLabel, 0, 0, 1, 2);
+
+    lbLabel = new QLabel(this);
+    lbLabel->setText(QLabel::tr("Account name (or email): "));
+    layout->addWidget(lbLabel, 1, 0);
+
+    leAccount = new QLineEdit(this);
+    layout->addWidget(leAccount, 1, 1);
+
+    lbLabel = new QLabel(this);
+    lbLabel->setText(QLabel::tr("Password: "));
+    layout->addWidget(lbLabel, 2, 0);
+
+    lePassword = new QLineEdit(this);
+    lePassword->setEchoMode(QLineEdit::Password);
+    layout->addWidget(lePassword, 2, 1);
+
+    cbSave = new QCheckBox(this);
+    cbSave->setText(QCheckBox::tr("Save account name and password"));
+    layout->addWidget(cbSave, 3, 0, 1, 2);
+
+    QFrame * hr = new QFrame(this);
+    hr->setFrameStyle(QFrame::HLine);
+    hr->setLineWidth(3);
+    hr->setFixedHeight(10);
+    layout->addWidget(hr, 4, 0, 1, 2);
+
+    lbLabel = new QLabel(this);
+    lbLabel->setText(QLabel::tr("Video title: "));
+    layout->addWidget(lbLabel, 5, 0);
+
+    leTitle = new QLineEdit(this);
+    leTitle->setText(filename);
+    layout->addWidget(leTitle, 5, 1);
+
+    lbLabel = new QLabel(this);
+    lbLabel->setText(QLabel::tr("Video description: "));
+    layout->addWidget(lbLabel, 6, 0, 1, 2);
+
+    teDescription = new QPlainTextEdit(this);
+    layout->addWidget(teDescription, 7, 0, 1, 2);
+
+    hr = new QFrame(this);
+        hr->setFrameStyle(QFrame::HLine);
+        hr->setLineWidth(3);
+        hr->setFixedHeight(10);
+        layout->addWidget(hr, 8, 0, 1, 2);
+
+    lbLabel = new QLabel(this);
+    lbLabel->setText(GoogleNotice);
+    layout->addWidget(lbLabel, 9, 0, 1, 2);
+
+    labelLog = new QLabel(this);
+    layout->addWidget(labelLog, 10, 0, 1, 2);
+
+    QDialogButtonBox* dbbButtons = new QDialogButtonBox(this);
+    btnUpload = dbbButtons->addButton(tr("Upload"), QDialogButtonBox::ActionRole);
+    QPushButton * pbCancel = dbbButtons->addButton(QDialogButtonBox::Cancel);
+    layout->addWidget(dbbButtons, 11, 0, 1, 2);
+
+    connect(btnUpload, SIGNAL(clicked()), this, SLOT(upload()));
+    connect(pbCancel, SIGNAL(clicked()), this, SLOT(reject()));
+}
+
+void HWUploadVideoDialog::log(const QString& text)
+{
+    labelLog->setText(labelLog->text() + text);
+}
+
+void HWUploadVideoDialog::setEditable(bool editable)
+{
+    leTitle->setEnabled(editable);
+    leAccount->setEnabled(editable);
+    lePassword->setEnabled(editable);
+    btnUpload->setEnabled(editable);
+}
+
+void HWUploadVideoDialog::upload()
+{
+    setEditable(false);
+
+    labelLog->clear();
+    log(tr("Authenticating at www.google.com... "));
+
+    // Documentation is at https://developers.google.com/youtube/2.0/developers_guide_protocol_clientlogin#ClientLogin_Authentication
+    QNetworkRequest request;
+    request.setUrl(QUrl("https://www.google.com/accounts/ClientLogin"));
+    request.setRawHeader("User-Agent", UserAgent);
+    request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
+
+    QString account(QUrl::toPercentEncoding(leAccount->text()));
+    QString pass(QUrl::toPercentEncoding(lePassword->text()));
+    QByteArray data = QString("Email=%1&Passwd=%2&service=youtube&source=Hedgewars").arg(account).arg(pass).toAscii();
+
+    QNetworkReply *reply = netManager->post(request, data);
+    connect(reply, SIGNAL(finished()), this, SLOT(authFinished()));
+}
+
+QString XmlEscape(const QString& str)
+{
+    QString str2 = str;
+    return "<![CDATA[" + str2.replace("]]>", "]]]]><![CDATA[>") + "]]>";
+}
+
+void HWUploadVideoDialog::authFinished()
+{
+    QNetworkReply *reply = (QNetworkReply*)sender();
+    reply->deleteLater();
+
+    QByteArray answer = reply->readAll();
+    QString authToken = "";
+    QList<QByteArray> lines = answer.split('\n');
+    foreach (const QByteArray& line, lines)
+    {
+        QString str(line);
+        if (!str.startsWith("Auth=", Qt::CaseInsensitive))
+            continue;
+        str.remove(0, 5);
+        authToken = str;
+        break;
+    }
+    if (authToken.isEmpty())
+    {
+        log(tr("failed\n"));
+        log(reply->errorString() + "\n");
+        setEditable(true);
+        return;
+    }
+    log(tr("Ok\n"));
+
+    log(tr("Sending metadata... "));
+
+    QByteArray auth = ("GoogleLogin auth=" + authToken).toAscii();
+
+    // We have authenticated, now we can send metadata and start upload
+    // Documentation is here: https://developers.google.com/youtube/2.0/developers_guide_protocol_resumable_uploads#Resumable_uploads
+    QByteArray body =
+            "<?xml version=\"1.0\"?>"
+            "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
+                "xmlns:media=\"http://search.yahoo.com/mrss/\" "
+                "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">"
+                "<media:group>"
+                    "<yt:incomplete/>"
+                    "<media:category "
+                        "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">Games"
+                    "</media:category>"
+                    "<media:title type=\"plain\">"
+                        + XmlEscape(leTitle->text()).toUtf8() +
+                    "</media:title>"
+                "</media:group>"
+            "</entry>";
+
+    QNetworkRequest request;
+    request.setUrl(QUrl("http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads"));
+    request.setRawHeader("User-Agent", UserAgent);
+    request.setRawHeader("Authorization", auth);
+    request.setRawHeader("GData-Version", "2");
+    request.setRawHeader("X-GData-Key", "key=" + devKey);
+    request.setRawHeader("Slug", filename.toUtf8());
+    request.setRawHeader("Content-Type", "application/atom+xml; charset=UTF-8");
+
+    reply = netManager->post(request, body);
+    connect(reply, SIGNAL(finished()), this, SLOT(startUpload()));
+}
+
+void HWUploadVideoDialog::startUpload()
+{
+    QNetworkReply *reply = (QNetworkReply*)sender();
+    reply->deleteLater();
+
+    location = QString::fromAscii(reply->rawHeader("Location"));
+    if (location.isEmpty())
+    {
+        log(tr("failed\n"));
+        log(reply->errorString() + "\n");
+        setEditable(true);
+        return;
+    }
+
+    log(tr("Ok\n"));
+    accept();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/dialog/upload_video.h	Thu Jul 26 21:58:25 2012 +0400
@@ -0,0 +1,62 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef UPLOAD_VIDEO_H
+#define UPLOAD_VIDEO_H
+
+#include <QDialog>
+
+class QLineEdit;
+class QCheckBox;
+class QPlainTextEdit;
+class QLabel;
+class QNetworkAccessManager;
+
+class HWUploadVideoDialog : public QDialog
+{
+        Q_OBJECT
+    public:
+    HWUploadVideoDialog(QWidget* parent, const QString& filename, QNetworkAccessManager* netManager);
+
+        QLineEdit* leAccount;
+        QLineEdit* lePassword;
+        QCheckBox* cbSave;
+
+        QLineEdit* leTitle;
+        QPlainTextEdit* teDescription;
+
+        QPushButton* btnUpload;
+
+        QLabel* labelLog;
+
+        QString location;
+
+    private:
+        QNetworkAccessManager* netManager;
+        QString filename;
+
+        void log(const QString& text);
+        void setEditable(bool editable);
+
+    private slots:
+        void upload();
+        void authFinished();
+        void startUpload();
+};
+
+#endif // UPLOAD_VIDEO_H
--- a/QTfrontend/ui/page/pagevideos.cpp	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/ui/page/pagevideos.cpp	Thu Jul 26 21:58:25 2012 +0400
@@ -39,6 +39,9 @@
 #include <QFileSystemWatcher>
 #include <QDateTime>
 #include <QRegExp>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
 
 #include "hwconsts.h"
 #include "pagevideos.h"
@@ -47,8 +50,9 @@
 #include "gameuiconfig.h"
 #include "recorder.h"
 #include "ask_quit.h"
+#include "upload_video.h"
 
-const QSize ThumbnailSize(350, 350*3/5);
+static const QSize ThumbnailSize(350, 350*3/5);
 
 // columns in table with list of video files
 enum VideosColumns
@@ -70,7 +74,9 @@
 
         QString name;
         QString desc; // description
+        QString uploadReply;
         HWRecorder * pRecorder; // non NULL if file is being encoded
+        QNetworkReply * pUploading; // non NULL if file is being uploaded
         bool seen; // used when updating directory
         float lastSizeUpdate;
         float progress;
@@ -87,6 +93,7 @@
 {
     this->name = name;
     pRecorder = NULL;
+    pUploading = NULL;
     lastSizeUpdate = 0;
     progress = 0;
 }
@@ -217,6 +224,7 @@
         filesTable->setColumnCount(vcNumColumns);
         filesTable->setHorizontalHeaderLabels(columns);
         filesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+        filesTable->setSelectionMode(QAbstractItemView::SingleSelection);
         filesTable->setEditTriggers(QAbstractItemView::SelectedClicked);
         filesTable->verticalHeader()->hide();
         filesTable->setMinimumWidth(400);
@@ -272,6 +280,9 @@
         btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
         btnDelete->setEnabled(false);
         pBottomDescLayout->addWidget(btnDelete);
+        btnToYouTube = new QPushButton(QPushButton::tr("Upload to YouTube"), pDescGroup);
+        btnToYouTube->setEnabled(false);
+        pBottomDescLayout->addWidget(btnToYouTube);
 
         pDescLayout->addStretch(1);
         pDescLayout->addLayout(pTopDescLayout, 0);
@@ -300,14 +311,16 @@
     connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged(int,int,int,int)));
     connect(btnPlay,   SIGNAL(clicked()), this, SLOT(playSelectedFile()));
     connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles()));
+    connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube()));
     connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory()));
  }
 
 PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent),
-    config(0)
+    config(0), netManager(0)
 {
     nameChangedFromCode = false;
     numRecorders = 0;
+    numUploads = 0;
     initPage();
 }
 
@@ -521,6 +534,14 @@
     numRecorders++;
 }
 
+void PageVideos::setProgress(int row, VideoItem* item, float value)
+{
+    QProgressBar * progressBar = (QProgressBar*)filesTable->cellWidget(row, vcProgress);
+    progressBar->setValue(value*10000);
+    progressBar->setFormat(QString("%1%").arg(value*100, 0, 'f', 2));
+    item->progress = value;
+}
+
 void PageVideos::updateProgress(float value)
 {
     HWRecorder * pRecorder = (HWRecorder*)sender();
@@ -534,11 +555,7 @@
         item->lastSizeUpdate = value;
     }
 
-    // update progress bar
-    QProgressBar * progressBar = (QProgressBar*)filesTable->cellWidget(row, vcProgress);
-    progressBar->setValue(value*10000);
-    progressBar->setFormat(QString("%1%").arg(value*100, 0, 'f', 2));
-    item->progress = value;
+    setProgress(row, item, value);
 }
 
 void PageVideos::encodingFinished(bool success)
@@ -674,11 +691,13 @@
         clearThumbnail();
         btnPlay->setEnabled(false);
         btnDelete->setEnabled(false);
+        btnToYouTube->setEnabled(false);
         return;
     }
 
     btnPlay->setEnabled(item->ready());
     btnDelete->setEnabled(true);
+    btnToYouTube->setEnabled(item->ready());
 
     QString desc = item->name + "\n\n";
     QString thumbName = "";
@@ -701,6 +720,8 @@
             desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin);
             thumbName = prefix;
         }
+
+        desc += item->uploadReply;
     }
     else
         desc += tr("(in progress...)");
@@ -823,12 +844,12 @@
 bool PageVideos::tryQuit(HWForm * form)
 {
     bool quit = true;
-    if (numRecorders != 0)
+    if (numRecorders != 0 || numUploads != 0)
     {
         // ask user what to do - abort or wait
         HWAskQuitDialog * askd = new HWAskQuitDialog(this, form);
+        askd->deleteLater();
         quit = askd->exec();
-        delete askd;
     }
     if (quit)
         clearTemp();
@@ -836,6 +857,10 @@
 }
 
 // returns multi-line string with list of videos in progress
+/* it will look like this:
+foo.avi (15.21% - encoding)
+bar.avi (18.21% - uploading)
+*/
 QString PageVideos::getVideosInProgress()
 {
     QString list = "";
@@ -843,11 +868,17 @@
     for (int i = 0; i < count; i++)
     {
         VideoItem * item = nameItem(i);
+        QString process;
+        if (!item->ready())
+            process = tr("encoding");
+        else if (item->pUploading)
+            process = tr("uploading");
+        else
+            continue;
         float progress = 100*item->progress;
         if (progress > 99.99)
             progress = 99.99; // displaying 100% may be confusing
-        if (!item->ready())
-            list += item->name + " (" + QString::number(progress, 'f', 2) + "%)\n";
+        list += item->name + " (" + QString::number(progress, 'f', 2) + "% - " + process + ")\n";
     }
     return list;
 }
@@ -880,3 +911,87 @@
         addRecorder(pRecorder);
     }
 }
+
+void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
+{
+    QNetworkReply* reply = (QNetworkReply*)sender();
+
+    VideoItem * item = NULL;
+    int row;
+    int count = filesTable->rowCount();
+    // find corresponding item (maybe there is a better wat to implement this?)
+    for (int i = 0; i < count; i++)
+    {
+        item = nameItem(i);
+        if (item->pUploading == reply)
+        {
+            row = i;
+            break;
+        }
+    }
+    Q_ASSERT(item);
+
+    setProgress(row, item, bytesSent*1.0/bytesTotal);
+}
+
+void PageVideos::uploadFinished()
+{
+    QNetworkReply* reply = (QNetworkReply*)sender();
+
+    VideoItem * item = NULL;
+    int row;
+    int count = filesTable->rowCount();
+    for (int i = 0; i < count; i++)
+    {
+        item = nameItem(i);
+        if (item->pUploading == reply)
+        {
+            row = i;
+            break;
+        }
+    }
+    Q_ASSERT(item);
+
+    item->pUploading = NULL;
+    QByteArray answer = reply->readAll();
+    item->uploadReply = QString::fromUtf8(answer.data());
+   // QMessageBox::information(this,"",item->uploadReply,0);
+    filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
+    numUploads--;
+}
+
+void PageVideos::uploadToYouTube()
+{
+    int row = filesTable->currentRow();
+    VideoItem * item = nameItem(row);
+
+    if (!netManager)
+        netManager = new QNetworkAccessManager(this);
+
+    HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager);
+    dlg->deleteLater();
+    if (!dlg->exec())
+        return;
+
+    QNetworkRequest request(QUrl(dlg->location));
+    request.setRawHeader("Content-Type", "application/octet-stream");
+
+    QFile * file = new QFile(item->path(), this);
+    if (!file->open(QIODevice::ReadOnly))
+        return;
+
+    // add progress bar
+    QProgressBar * progressBar = new QProgressBar(filesTable);
+    progressBar->setMinimum(0);
+    progressBar->setMaximum(10000);
+    progressBar->setValue(0);
+    // make it different from encoding progress-bar
+    progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" );
+    filesTable->setCellWidget(row, vcProgress, progressBar);
+
+    QNetworkReply* reply = netManager->put(request, file);
+    item->pUploading = reply;
+    connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
+    connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
+    numUploads++;
+}
--- a/QTfrontend/ui/page/pagevideos.h	Thu Jul 26 21:56:47 2012 +0400
+++ b/QTfrontend/ui/page/pagevideos.h	Thu Jul 26 21:58:25 2012 +0400
@@ -20,9 +20,9 @@
 #ifndef PAGE_VIDEOS_H
 #define PAGE_VIDEOS_H
 
-#include <QPixmap>
 #include "AbstractPage.h"
 
+class QNetworkAccessManager;
 class GameUIConfig;
 class HWRecorder;
 class VideoItem;
@@ -41,7 +41,6 @@
         QCheckBox *checkUseGameRes;
         QCheckBox *checkRecordAudio;
 
-
         QString format()
         { return comboAVFormats->itemData(comboAVFormats->currentIndex()).toString(); }
 
@@ -76,8 +75,10 @@
         void updateDescription();
         void clearTemp();
         void clearThumbnail();
+        void setProgress(int row, VideoItem* item, float value);
 
         GameUIConfig * config;
+        QNetworkAccessManager* netManager;
 
         // options group
         QComboBox *comboAVFormats;
@@ -90,7 +91,7 @@
         QPushButton *btnOpenDir;
 
         // description group
-        QPushButton *btnPlay, *btnDelete;
+        QPushButton *btnPlay, *btnDelete, *btnToYouTube;
         QLabel *labelDesc;
         QLabel *labelThumbnail;
 
@@ -98,7 +99,7 @@
         // (in signal cellChanged)
         bool nameChangedFromCode;
 
-        int numRecorders;
+        int numRecorders, numUploads;
 
     private slots:
         void changeAVFormat(int index);
@@ -114,6 +115,9 @@
         void deleteSelectedFiles();
         void openVideosDirectory();
         void updateFileList(const QString & path);
+        void uploadToYouTube();
+        void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
+        void uploadFinished();
 };
 
 #endif // PAGE_VIDEOS_H
--- a/hedgewars/hwengine.pas	Thu Jul 26 21:56:47 2012 +0400
+++ b/hedgewars/hwengine.pas	Thu Jul 26 21:58:25 2012 +0400
@@ -32,6 +32,9 @@
 uses SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler, uSound,
      uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uRandom, uLandTexture, uCollisions,
      SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted
+     {$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF}
+     {$IFDEF SDL13}, uTouch{$ENDIF}{$IFDEF ANDROID}, GLUnit{$ENDIF}, uAILandMarks;
+
 
 {$IFDEF HWLIBRARY}
 procedure initEverything(complete:boolean);
--- a/hedgewars/uGame.pas	Thu Jul 26 21:56:47 2012 +0400
+++ b/hedgewars/uGame.pas	Thu Jul 26 21:58:25 2012 +0400
@@ -48,7 +48,7 @@
 
     if (GameType = gmtDemo) then 
         if isSpeed then
-        Lag:= Lag * 10
+        begin
             i:= RealTicks-SpeedStart;
             if i < 2000 then Lag:= Lag*5
             else if i < 4000 then Lag:= Lag*10
--- a/hedgewars/uGearsHedgehog.pas	Thu Jul 26 21:56:47 2012 +0400
+++ b/hedgewars/uGearsHedgehog.pas	Thu Jul 26 21:58:25 2012 +0400
@@ -617,7 +617,6 @@
                         AddPickup(HH^.Hedgehog^, Gear^.AmmoType, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y));
                         end
                     else
-                    or (GameType = gmtDemo)  then
                         begin
 // Add spawning here...
                         AddRandomness(GameTicks);
--- a/project_files/hedgewars.pro	Thu Jul 26 21:56:47 2012 +0400
+++ b/project_files/hedgewars.pro	Thu Jul 26 21:58:25 2012 +0400
@@ -108,7 +108,8 @@
     ../QTfrontend/util/libav_iteraction.h \
     ../QTfrontend/ui/page/pagevideos.h \
     ../QTfrontend/net/recorder.h \
-    ../QTfrontend/ui/dialog/ask_quit.h
+    ../QTfrontend/ui/dialog/ask_quit.h \
+    ../QTfrontend/ui/dialog/upload_video.h
 
 SOURCES += ../QTfrontend/model/ammoSchemeModel.cpp \
     ../QTfrontend/model/MapModel.cpp \
@@ -194,7 +195,8 @@
     ../QTfrontend/util/libav_iteraction.cpp \
     ../QTfrontend/ui/page/pagevideos.cpp \
     ../QTfrontend/net/recorder.cpp \
-    ../QTfrontend/ui/dialog/ask_quit.cpp
+    ../QTfrontend/ui/dialog/ask_quit.cpp \
+    ../QTfrontend/ui/dialog/upload_video.cpp
 
 win32 {
     SOURCES += ../QTfrontend/xfire.cpp