QTfrontend/ui/page/pagevideos.cpp
changeset 13232 2a3c52c49e8e
parent 12897 fc47fc4af6bd
child 13286 1fbf2ac1f13e
equal deleted inserted replaced
13231:6193503c4727 13232:2a3c52c49e8e
    37 #include <QVBoxLayout>
    37 #include <QVBoxLayout>
    38 #include <QHBoxLayout>
    38 #include <QHBoxLayout>
    39 #include <QFileSystemWatcher>
    39 #include <QFileSystemWatcher>
    40 #include <QDateTime>
    40 #include <QDateTime>
    41 #include <QRegExp>
    41 #include <QRegExp>
    42 #include <QNetworkAccessManager>
       
    43 #include <QNetworkRequest>
       
    44 #include <QNetworkReply>
       
    45 #include <QXmlStreamReader>
    42 #include <QXmlStreamReader>
    46 
    43 
    47 #include "hwconsts.h"
    44 #include "hwconsts.h"
    48 #include "pagevideos.h"
    45 #include "pagevideos.h"
    49 #include "igbox.h"
    46 #include "igbox.h"
    50 #include "LibavInteraction.h"
    47 #include "LibavInteraction.h"
    51 #include "gameuiconfig.h"
    48 #include "gameuiconfig.h"
    52 #include "recorder.h"
    49 #include "recorder.h"
    53 #include "ask_quit.h"
    50 #include "ask_quit.h"
    54 #include "upload_video.h"
       
    55 
    51 
    56 static const QSize ThumbnailSize(350, 350*3/5);
    52 static const QSize ThumbnailSize(350, 350*3/5);
    57 
    53 
    58 // columns in table with list of video files
    54 // columns in table with list of video files
    59 enum VideosColumns
    55 enum VideosColumns
    60 {
    56 {
    61     vcName,
    57     vcName,
    62     vcSize,
    58     vcSize,
    63     vcProgress, // either encoding or uploading
    59     vcProgress, // for encoding
    64 
    60 
    65     vcNumColumns,
    61     vcNumColumns,
    66 };
    62 };
    67 
    63 
    68 // this class is used for items in first column in file-table
    64 // this class is used for items in first column in file-table
    75         ~VideoItem();
    71         ~VideoItem();
    76 
    72 
    77         QString name;
    73         QString name;
    78         QString prefix; // original filename without extension
    74         QString prefix; // original filename without extension
    79         QString desc;   // description (duration, resolution, etc...)
    75         QString desc;   // description (duration, resolution, etc...)
    80         QString uploadUrl; // https://youtu.be/???????
       
    81         HWRecorder    * pRecorder; // non NULL if file is being encoded
    76         HWRecorder    * pRecorder; // non NULL if file is being encoded
    82         QNetworkReply * pUploading; // non NULL if file is being uploaded
       
    83         bool seen; // used when updating directory
    77         bool seen; // used when updating directory
    84         float lastSizeUpdate;
    78         float lastSizeUpdate;
    85         float progress;
    79         float progress;
    86 
    80 
    87         bool ready()
    81         bool ready()
    94 VideoItem::VideoItem(const QString& name)
    88 VideoItem::VideoItem(const QString& name)
    95 : QTableWidgetItem(name, UserType)
    89 : QTableWidgetItem(name, UserType)
    96 {
    90 {
    97     this->name = name;
    91     this->name = name;
    98     pRecorder = NULL;
    92     pRecorder = NULL;
    99     pUploading = NULL;
       
   100     lastSizeUpdate = 0;
    93     lastSizeUpdate = 0;
   101     progress = 0;
    94     progress = 0;
   102     seen = false;
    95     seen = false;
   103 }
    96 }
   104 
    97 
   178                                            Qt::LinksAccessibleByMouse |
   171                                            Qt::LinksAccessibleByMouse |
   179                                            Qt::LinksAccessibleByKeyboard);
   172                                            Qt::LinksAccessibleByKeyboard);
   180         labelDesc->setTextFormat(Qt::RichText);
   173         labelDesc->setTextFormat(Qt::RichText);
   181         labelDesc->setWordWrap(true);
   174         labelDesc->setWordWrap(true);
   182         labelDesc->setOpenExternalLinks(true);
   175         labelDesc->setOpenExternalLinks(true);
   183         //labelDesc->setMinimumSize(ThumbnailSize);
       
   184         //pTopDescLayout->addWidget(labelDesc, 1);
       
   185 
   176 
   186         // buttons: play and delete
   177         // buttons: play and delete
   187         btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
   178         btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
   188         btnPlay->setEnabled(false);
   179         btnPlay->setEnabled(false);
   189         btnPlay->setWhatsThis(QPushButton::tr("Play this video"));
   180         btnPlay->setWhatsThis(QPushButton::tr("Play this video"));
   190         pBottomDescLayout->addWidget(btnPlay);
   181         pBottomDescLayout->addWidget(btnPlay);
   191         btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
   182         btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
   192         btnDelete->setEnabled(false);
   183         btnDelete->setEnabled(false);
   193         btnDelete->setWhatsThis(QPushButton::tr("Delete this video"));
   184         btnDelete->setWhatsThis(QPushButton::tr("Delete this video"));
   194         pBottomDescLayout->addWidget(btnDelete);
   185         pBottomDescLayout->addWidget(btnDelete);
   195         btnToYouTube = new QPushButton(QPushButton::tr("Upload to YouTube"), pDescGroup);
       
   196         btnToYouTube->setEnabled(false);
       
   197         btnToYouTube->setWhatsThis(QPushButton::tr("Upload this video to your YouTube account"));
       
   198         pBottomDescLayout->addWidget(btnToYouTube);
       
   199 
   186 
   200         pDescLayout->addWidget(labelThumbnail, 0);
   187         pDescLayout->addWidget(labelThumbnail, 0);
   201         pDescLayout->addWidget(labelDesc, 0);
   188         pDescLayout->addWidget(labelDesc, 0);
   202         pDescLayout->addLayout(pBottomDescLayout, 0);
   189         pDescLayout->addLayout(pBottomDescLayout, 0);
   203         pDescLayout->addStretch(1);
   190         pDescLayout->addStretch(1);
   218     connect(filesTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellDoubleClicked(int, int)));
   205     connect(filesTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellDoubleClicked(int, int)));
   219     connect(filesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int, int)));
   206     connect(filesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int, int)));
   220     connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged()));
   207     connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged()));
   221     connect(btnPlay,   SIGNAL(clicked()), this, SLOT(playSelectedFile()));
   208     connect(btnPlay,   SIGNAL(clicked()), this, SLOT(playSelectedFile()));
   222     connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles()));
   209     connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles()));
   223     connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube()));
       
   224     connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory()));
   210     connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory()));
   225 }
   211 }
   226 
   212 
   227 PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent),
   213 PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent),
   228     config(0), netManager(0)
   214     config(0)
   229 {
   215 {
   230     nameChangedFromCode = false;
   216     nameChangedFromCode = false;
   231     numRecorders = 0;
   217     numRecorders = 0;
   232     numUploads = 0;
       
   233     initPage();
   218     initPage();
   234 }
   219 }
   235 
   220 
   236 void PageVideos::init(GameUIConfig * config)
   221 void PageVideos::init(GameUIConfig * config)
   237 {
   222 {
   489         // nothing is selected => clear description and return
   474         // nothing is selected => clear description and return
   490         labelDesc->clear();
   475         labelDesc->clear();
   491         clearThumbnail();
   476         clearThumbnail();
   492         btnPlay->setEnabled(false);
   477         btnPlay->setEnabled(false);
   493         btnDelete->setEnabled(false);
   478         btnDelete->setEnabled(false);
   494         btnToYouTube->setEnabled(false);
       
   495         return;
   479         return;
   496     }
   480     }
   497 
   481 
   498     btnPlay->setEnabled(item->ready());
   482     btnPlay->setEnabled(item->ready());
   499     btnToYouTube->setEnabled(item->ready());
       
   500     btnDelete->setEnabled(true);
   483     btnDelete->setEnabled(true);
   501     btnDelete->setText(item->ready()? QPushButton::tr("Delete") :  QPushButton::tr("Cancel"));
   484     btnDelete->setText(item->ready()? QPushButton::tr("Delete") :  QPushButton::tr("Cancel"));
   502     btnToYouTube->setText(item->pUploading? QPushButton::tr("Cancel uploading") :  QPushButton::tr("Upload to YouTube"));
       
   503 
   485 
   504     // construct string with desctiption of this file to display it
   486     // construct string with desctiption of this file to display it
   505     QString desc = item->name + "\n\n";
   487     QString desc = item->name + "\n\n";
   506 
   488 
   507     if (!item->ready())
   489     if (!item->ready())
   541         int pt = item->prefix.lastIndexOf('.');
   523         int pt = item->prefix.lastIndexOf('.');
   542         if (pt != -1)
   524         if (pt != -1)
   543             item->prefix.truncate(pt);
   525             item->prefix.truncate(pt);
   544     }
   526     }
   545 
   527 
   546     if (item->ready() && item->uploadUrl.isEmpty())
       
   547     {
       
   548         // try to load url from file
       
   549         QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
       
   550         if (!file->open(QIODevice::ReadOnly))
       
   551             item->uploadUrl = "no";
       
   552         else
       
   553         {
       
   554             QByteArray data = file->readAll();
       
   555             file->close();
       
   556             item->uploadUrl = QString::fromUtf8(data.data());
       
   557         }
       
   558     }
       
   559     if (item->uploadUrl != "no")
       
   560         desc += QString("<a href=\"%1\" style=\"color: white;\">%1</a>").arg(item->uploadUrl);
       
   561     desc.replace("\n", "<br/>");
   528     desc.replace("\n", "<br/>");
   562 
   529 
   563     labelDesc->setText(desc);
   530     labelDesc->setText(desc);
   564 
   531 
   565     if (!item->prefix.isEmpty())
   532     if (!item->prefix.isEmpty())
   686 {
   653 {
   687     QString path = QDir::toNativeSeparators(cfgdir->absolutePath() + "/Videos");
   654     QString path = QDir::toNativeSeparators(cfgdir->absolutePath() + "/Videos");
   688     QDesktopServices::openUrl(QUrl("file:///" + path));
   655     QDesktopServices::openUrl(QUrl("file:///" + path));
   689 }
   656 }
   690 
   657 
   691 // clear VideoTemp directory (except for thumbnails and upload links)
   658 // clear VideoTemp directory (except for thumbnails)
   692 void PageVideos::clearTemp()
   659 void PageVideos::clearTemp()
   693 {
   660 {
   694     QDir temp(cfgdir->absolutePath() + "/VideoTemp");
   661     QDir temp(cfgdir->absolutePath() + "/VideoTemp");
   695     QStringList files = temp.entryList(QDir::Files);
   662     QStringList files = temp.entryList(QDir::Files);
   696     foreach (const QString& file, files)
   663     foreach (const QString& file, files)
   697     {
   664     {
   698         if (!file.endsWith(".bmp") && !file.endsWith(".png") && !file.endsWith("-url.txt"))
   665         if (!file.endsWith(".bmp") && !file.endsWith(".png"))
   699             temp.remove(file);
   666             temp.remove(file);
   700     }
   667     }
   701 }
   668 }
   702 
   669 
   703 bool PageVideos::tryQuit(HWForm * form)
   670 bool PageVideos::tryQuit(HWForm * form)
   704 {
   671 {
   705     bool quit = true;
   672     bool quit = true;
   706     if (numRecorders != 0 || numUploads != 0)
   673     if (numRecorders != 0)
   707     {
   674     {
   708         // ask user what to do - abort or wait
   675         // ask user what to do - abort or wait
   709         HWAskQuitDialog * askd = new HWAskQuitDialog(this, form);
   676         HWAskQuitDialog * askd = new HWAskQuitDialog(this, form);
   710         askd->deleteLater();
   677         askd->deleteLater();
   711         quit = askd->exec();
   678         quit = askd->exec();
   716 }
   683 }
   717 
   684 
   718 // returns multi-line string with list of videos in progress
   685 // returns multi-line string with list of videos in progress
   719 /* it will look like this:
   686 /* it will look like this:
   720 foo.avi (15.21% - encoding)
   687 foo.avi (15.21% - encoding)
   721 bar.avi (18.21% - uploading)
       
   722 */
   688 */
   723 QString PageVideos::getVideosInProgress()
   689 QString PageVideos::getVideosInProgress()
   724 {
   690 {
   725     QString list = "";
   691     QString list = "";
   726     int count = filesTable->rowCount();
   692     int count = filesTable->rowCount();
   728     {
   694     {
   729         VideoItem * item = nameItem(i);
   695         VideoItem * item = nameItem(i);
   730         QString process;
   696         QString process;
   731         if (!item->ready())
   697         if (!item->ready())
   732             process = tr("encoding");
   698             process = tr("encoding");
   733         else if (item->pUploading)
       
   734             process = tr("uploading");
       
   735         else
   699         else
   736             continue;
   700             continue;
   737         float progress = 100*item->progress;
   701         float progress = 100*item->progress;
   738         if (progress > 99.99)
   702         if (progress > 99.99)
   739             progress = 99.99; // displaying 100% may be confusing
   703             progress = 99.99; // displaying 100% may be confusing
   769         }
   733         }
   770         addRecorder(pRecorder);
   734         addRecorder(pRecorder);
   771     }
   735     }
   772 }
   736 }
   773 
   737 
   774 VideoItem * PageVideos::itemFromReply(QNetworkReply* reply, int & row)
       
   775 {
       
   776     VideoItem * item = NULL;
       
   777     int count = filesTable->rowCount();
       
   778     // find corresponding item (maybe there is a better way to implement this?)
       
   779     for (int i = 0; i < count; i++)
       
   780     {
       
   781         item = nameItem(i);
       
   782         if (item->pUploading == reply)
       
   783         {
       
   784             row = i;
       
   785             break;
       
   786         }
       
   787     }
       
   788     return item;
       
   789 }
       
   790 
       
   791 void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
       
   792 {
       
   793     QNetworkReply* reply = (QNetworkReply*)sender();
       
   794     int row;
       
   795     VideoItem * item = itemFromReply(reply, row);
       
   796     setProgress(row, item, bytesSent*1.0/bytesTotal);
       
   797 }
       
   798 
       
   799 void PageVideos::uploadFinished()
       
   800 {
       
   801     QNetworkReply* reply = (QNetworkReply*)sender();
       
   802     reply->deleteLater();
       
   803 
       
   804     int row;
       
   805     VideoItem * item = itemFromReply(reply, row);
       
   806     if (!item)
       
   807         return;
       
   808 
       
   809     item->pUploading = NULL;
       
   810 
       
   811     // extract video id from reply
       
   812     QString videoid;
       
   813     QXmlStreamReader xml(reply);
       
   814     while (!xml.atEnd())
       
   815     {
       
   816         xml.readNext();
       
   817         if (xml.qualifiedName() == "yt:videoid")
       
   818         {
       
   819             videoid = xml.readElementText();
       
   820             break;
       
   821         }
       
   822     }
       
   823 
       
   824     if (!videoid.isEmpty())
       
   825     {
       
   826         item->uploadUrl = "https://youtu.be/" + videoid;
       
   827         updateDescription();
       
   828 
       
   829         // save url in file
       
   830         QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
       
   831         if (file->open(QIODevice::WriteOnly))
       
   832         {
       
   833             file->write(item->uploadUrl.toUtf8());
       
   834             file->close();
       
   835         }
       
   836     }
       
   837 
       
   838     filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
       
   839     numUploads--;
       
   840 }
       
   841 
       
   842 // this will protect saved youtube password from those who cannot read source code
       
   843 static QString protectPass(QString str)
       
   844 {
       
   845     QByteArray array = str.toUtf8();
       
   846     for (int i = 0; i < array.size(); i++)
       
   847         array[i] = array[i] ^ 0xC4 ^ i;
       
   848     array = array.toBase64();
       
   849     return QString::fromLatin1(array.data());
       
   850 }
       
   851 
       
   852 static QString unprotectPass(QString str)
       
   853 {
       
   854     QByteArray array = QByteArray::fromBase64(str.toLatin1());
       
   855     for (int i = 0; i < array.size(); i++)
       
   856         array[i] = array[i] ^ 0xC4 ^ i;
       
   857     return QString::fromUtf8(array);
       
   858 }
       
   859 
       
   860 void PageVideos::uploadToYouTube()
       
   861 {
       
   862     int row = filesTable->currentRow();
       
   863     VideoItem * item = nameItem(row);
       
   864 
       
   865     if (item->pUploading) //Act as 'cancel uploading' button
       
   866     {
       
   867         // ask user if (s)he is serious
       
   868         QMessageBox reallyStopMsg(this);
       
   869         reallyStopMsg.setIcon(QMessageBox::Question);
       
   870         reallyStopMsg.setWindowTitle(QMessageBox::tr("Videos - Are you sure?"));
       
   871         reallyStopMsg.setText(QMessageBox::tr("Do you really want to cancel uploading %1?").arg(item->name));
       
   872         reallyStopMsg.setWindowModality(Qt::WindowModal);
       
   873         reallyStopMsg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
       
   874 
       
   875         if (reallyStopMsg.exec() != QMessageBox::Ok)
       
   876             return;
       
   877         item->pUploading->abort();
       
   878         btnToYouTube->setText(QPushButton::tr("Upload to YouTube"));
       
   879         filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
       
   880         //numUploads--;
       
   881         return;
       
   882     }
       
   883 
       
   884     if (!netManager)
       
   885         netManager = new QNetworkAccessManager(this);
       
   886 
       
   887     HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager);
       
   888     dlg->deleteLater();
       
   889     if (config->value("youtube/save").toBool())
       
   890     {
       
   891         dlg->cbSave->setChecked(true);
       
   892         dlg->leAccount->setText(config->value("youtube/name").toString());
       
   893         dlg->lePassword->setText(unprotectPass(config->value("youtube/pswd").toString()));
       
   894     }
       
   895 
       
   896     bool result = dlg->exec();
       
   897 
       
   898     if (dlg->cbSave->isChecked())
       
   899     {
       
   900         config->setValue("youtube/save", true);
       
   901         config->setValue("youtube/name", dlg->leAccount->text());
       
   902         config->setValue("youtube/pswd", protectPass(dlg->lePassword->text()));
       
   903     }
       
   904     else
       
   905     {
       
   906         config->setValue("youtube/save", false);
       
   907         config->setValue("youtube/name", "");
       
   908         config->setValue("youtube/pswd", "");
       
   909     }
       
   910 
       
   911     if (!result)
       
   912         return;
       
   913 
       
   914     QNetworkRequest request(QUrl(dlg->location));
       
   915     request.setRawHeader("Content-Type", "application/octet-stream");
       
   916 
       
   917     QFile * file = new QFile(item->path(), this);
       
   918     if (!file->open(QIODevice::ReadOnly))
       
   919         return;
       
   920 
       
   921     // add progress bar
       
   922     QProgressBar * progressBar = new QProgressBar(filesTable);
       
   923     progressBar->setMinimum(0);
       
   924     progressBar->setMaximum(10000);
       
   925     progressBar->setValue(0);
       
   926     // make it different from progress-bar used during encoding (use blue color)
       
   927     progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" );
       
   928     filesTable->setCellWidget(row, vcProgress, progressBar);
       
   929 
       
   930     QNetworkReply* reply = netManager->put(request, file);
       
   931     file->setParent(reply); // automatically close file when needed
       
   932     item->pUploading = reply;
       
   933     connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
       
   934     connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
       
   935     numUploads++;
       
   936 
       
   937     updateDescription();
       
   938 }