diff -r deaeac102355 -r 3032a5739fe1 QTfrontend/ui/page/pagevideos.cpp --- a/QTfrontend/ui/page/pagevideos.cpp Mon Aug 06 00:40:26 2012 +0400 +++ b/QTfrontend/ui/page/pagevideos.cpp Mon Aug 06 00:44:32 2012 +0400 @@ -42,6 +42,7 @@ #include #include #include +#include #include "hwconsts.h" #include "pagevideos.h" @@ -59,11 +60,12 @@ { vcName, vcSize, - vcProgress, + vcProgress, // either encoding or uploading vcNumColumns, }; +// this class is used for items in first column in file-table class VideoItem : public QTableWidgetItem { // note: QTableWidgetItem is not Q_OBJECT @@ -73,9 +75,10 @@ ~VideoItem(); QString name; - QString desc; // description - QString uploadReply; - HWRecorder * pRecorder; // non NULL if file is being encoded + QString prefix; // original filename without extension + QString desc; // description (duration, resolution, etc...) + QString uploadUrl; // http://youtu.be/??????? + 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; @@ -112,7 +115,7 @@ // options { IconedGroupBox* pOptionsGroup = new IconedGroupBox(this); - pOptionsGroup->setIcon(QIcon(":/res/Settings.png")); + pOptionsGroup->setIcon(QIcon(":/res/Settings.png")); // FIXME pOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); pOptionsGroup->setTitle(QGroupBox::tr("Video recording options")); QGridLayout * pOptLayout = new QGridLayout(pOptionsGroup); @@ -211,7 +214,7 @@ // list of videos { IconedGroupBox* pTableGroup = new IconedGroupBox(this); - pTableGroup->setIcon(QIcon(":/res/graphicsicon.png")); + pTableGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME pTableGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); pTableGroup->setTitle(QGroupBox::tr("Videos")); @@ -247,7 +250,7 @@ // description { IconedGroupBox* pDescGroup = new IconedGroupBox(this); - pDescGroup->setIcon(QIcon(":/res/graphicsicon.png")); + pDescGroup->setIcon(QIcon(":/res/graphicsicon.png")); // FIXME pDescGroup->setTitle(QGroupBox::tr("Description")); QVBoxLayout* pDescLayout = new QVBoxLayout(pDescGroup); @@ -271,6 +274,11 @@ // label with file description labelDesc = new QLabel(pDescGroup); labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop); + labelDesc->setTextInteractionFlags(Qt::TextSelectableByMouse | + Qt::TextSelectableByKeyboard | + Qt::LinksAccessibleByMouse | + Qt::LinksAccessibleByKeyboard); + labelDesc->setTextFormat(Qt::RichText); pTopDescLayout->addWidget(labelDesc, 1); // buttons: play and delete @@ -313,6 +321,7 @@ connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles())); connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube())); connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory())); + connect(labelDesc, SIGNAL(linkActivated(const QString&)), this, SLOT(linkActivated(const QString&))); } PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent), @@ -435,6 +444,7 @@ if (iFormat == -1) return false; comboAVFormats->setCurrentIndex(iFormat); + // format was changed, so lists of codecs were automatically updated to codecs supported by this format // try to find video codec int iVCodec = comboVideoCodecs->findData(vcodec); @@ -453,7 +463,7 @@ } // get file size as string -QString FileSizeStr(const QString & path) +static QString FileSizeStr(const QString & path) { quint64 size = QFileInfo(path).size(); @@ -478,6 +488,8 @@ filesTable->item(row, vcSize)->setText(FileSizeStr(path)); } +// There is a button 'Open videos dir', so it is possible that user will open +// this dir and rename/delete some files there, so we should handle this. void PageVideos::updateFileList(const QString & path) { // mark all files as non seen @@ -545,7 +557,7 @@ void PageVideos::updateProgress(float value) { HWRecorder * pRecorder = (HWRecorder*)sender(); - VideoItem * item = (VideoItem*)pRecorder->item; + VideoItem * item = pRecorder->item; int row = filesTable->row(item); // update file size every percent @@ -609,6 +621,7 @@ QString newName = item->text(); if (!newName.contains('.')) // user forgot an extension { + // restore old extension int pt = oldName.lastIndexOf('.'); if (pt != -1) { @@ -676,7 +689,7 @@ void PageVideos::clearThumbnail() { - // add empty image for proper sizing + // add empty (transparent) image for proper sizing QPixmap pic(ThumbnailSize); pic.fill(QColor(0,0,0,0)); labelThumbnail->setPixmap(pic); @@ -687,6 +700,7 @@ VideoItem * item = nameItem(filesTable->currentRow()); if (!item) { + // nothing is selected => clear description and return labelDesc->clear(); clearThumbnail(); btnPlay->setEnabled(false); @@ -696,51 +710,75 @@ } btnPlay->setEnabled(item->ready()); + btnToYouTube->setEnabled(item->ready()); btnDelete->setEnabled(true); - btnToYouTube->setEnabled(item->ready()); + btnDelete->setText(item->ready()? QPushButton::tr("Delete") : QPushButton::tr("Cancel")); + btnToYouTube->setText(item->pUploading? QPushButton::tr("Cancel uploading") : QPushButton::tr("Upload to YouTube")); + // construct string with desctiption of this file to display it QString desc = item->name + "\n\n"; - QString thumbName = ""; - if (item->ready()) + if (!item->ready()) + desc += tr("(in progress...)"); + else { QString path = item->path(); - desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + "\n"; - desc += tr("Size: ") + FileSizeStr(path) + "\n"; - if (item->desc == "") + desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + '\n'; + desc += tr("Size: ") + FileSizeStr(path) + '\n'; + if (item->desc.isEmpty()) + { + // Extract description from file; + // It will contain duration, resolution, etc and also comment added by hwengine. item->desc = LibavIteraction::instance().getFileInfo(path); - desc += item->desc; - - // extract thumbnail name fron description - int prefixBegin = desc.indexOf("prefix["); - int prefixEnd = desc.indexOf("]prefix"); - if (prefixBegin != -1 && prefixEnd != -1) - { - QString prefix = desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7)); - desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin); - thumbName = prefix; - } - desc += item->uploadReply; - } - else - desc += tr("(in progress...)"); - - if (thumbName.isEmpty()) - { - if (item->ready()) - thumbName = item->name; - else - thumbName = item->pRecorder->name; - // remove extension - int pt = thumbName.lastIndexOf('.'); - if (pt != -1) - thumbName.truncate(pt); + // extract prefix (original name) from description (it is enclosed in prefix[???]prefix) + int prefixBegin = item->desc.indexOf("prefix["); + int prefixEnd = item->desc.indexOf("]prefix"); + if (prefixBegin != -1 && prefixEnd != -1) + { + item->prefix = desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7)); + item->desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin); + } + } + desc += item->desc + '\n'; } - if (!thumbName.isEmpty()) + if (item->prefix.isEmpty()) + { + // try to extract prefix from file name instead + if (item->ready()) + item->prefix = item->name; + else + item->prefix = item->pRecorder->name; + + // remove extension + int pt = item->prefix.lastIndexOf('.'); + if (pt != -1) + item->prefix.truncate(pt); + } + + if (item->ready() && item->uploadUrl.isEmpty()) { - thumbName = cfgdir->absoluteFilePath("VideoTemp/" + thumbName); + // try to load url from file + QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this); + if (!file->open(QIODevice::ReadOnly)) + item->uploadUrl = "no"; + else + { + QByteArray data = file->readAll(); + file->close(); + item->uploadUrl = QString::fromUtf8(data.data()); + } + } + if (item->uploadUrl != "no") + desc += QString("%1").arg(item->uploadUrl); + desc.replace("\n", "
"); + + labelDesc->setText(desc); + + if (!item->prefix.isEmpty()) + { + QString thumbName = cfgdir->absoluteFilePath("VideoTemp/" + item->prefix); QPixmap pic; if (pic.load(thumbName + ".png") || pic.load(thumbName + ".bmp")) { @@ -753,7 +791,6 @@ else clearThumbnail(); } - labelDesc->setText(desc); } // user selected another cell, so we should change description @@ -766,10 +803,15 @@ void PageVideos::play(int row) { VideoItem * item = nameItem(row); - if (item->ready()) + if (item && item->ready()) QDesktopServices::openUrl(QUrl("file:///" + QDir::toNativeSeparators(item->path()))); } +void PageVideos::linkActivated(const QString & link) +{ + QDesktopServices::openUrl(QUrl(link)); +} + void PageVideos::playSelectedFile() { int index = filesTable->currentRow(); @@ -779,6 +821,30 @@ void PageVideos::deleteSelectedFiles() { + int index = filesTable->currentRow(); + if (index == -1) + return; + + VideoItem * item = nameItem(index); + if (!item) + return; + + // ask user if (s)he is serious + if (QMessageBox::question(this, + tr("Are you sure?"), + tr("Do you really want do remove %1?").arg(item->name), + QMessageBox::Yes | QMessageBox::No) + != QMessageBox::Yes) + return; + + // remove + if (!item->ready()) + item->pRecorder->deleteLater(); + else + cfgdir->remove("Videos/" + item->name); + +// this code is for removing several files when multiple selection is enabled +#if 0 QList items = filesTable->selectedItems(); int num = items.size() / vcNumColumns; if (num == 0) @@ -803,6 +869,7 @@ else cfgdir->remove("Videos/" + item->name); } +#endif } void PageVideos::keyPressEvent(QKeyEvent * pEvent) @@ -829,14 +896,14 @@ QDesktopServices::openUrl(QUrl("file:///" + path)); } -// clear VideoTemp directory (except for thumbnails) +// clear VideoTemp directory (except for thumbnails and upload links) void PageVideos::clearTemp() { QDir temp(cfgdir->absolutePath() + "/VideoTemp"); QStringList files = temp.entryList(QDir::Files); foreach (const QString& file, files) { - if (!file.endsWith(".bmp") && !file.endsWith(".png")) + if (!file.endsWith(".bmp") && !file.endsWith(".png") && !file.endsWith("-url.txt")) temp.remove(file); } } @@ -912,14 +979,11 @@ } } -void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal) +VideoItem * PageVideos::itemFromReply(QNetworkReply* reply, int & row) { - 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?) + // find corresponding item (maybe there is a better way to implement this?) for (int i = 0; i < count; i++) { item = nameItem(i); @@ -929,48 +993,125 @@ break; } } - Q_ASSERT(item); + return item; +} +void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal) +{ + QNetworkReply* reply = (QNetworkReply*)sender(); + int row; + VideoItem * item = itemFromReply(reply, row); setProgress(row, item, bytesSent*1.0/bytesTotal); } void PageVideos::uploadFinished() { QNetworkReply* reply = (QNetworkReply*)sender(); + reply->deleteLater(); - VideoItem * item = NULL; int row; - int count = filesTable->rowCount(); - for (int i = 0; i < count; i++) + VideoItem * item = itemFromReply(reply, row); + if (!item) + return; + + item->pUploading = NULL; + + // extract video id from reply + QString videoid; + QXmlStreamReader xml(reply); + while (!xml.atEnd()) { - item = nameItem(i); - if (item->pUploading == reply) + xml.readNext(); + if (xml.qualifiedName() == "yt:videoid") { - row = i; + videoid = xml.readElementText(); break; } } - Q_ASSERT(item); + + if (!videoid.isEmpty()) + { + item->uploadUrl = "http://youtu.be/" + videoid; + updateDescription(); - item->pUploading = NULL; - QByteArray answer = reply->readAll(); - item->uploadReply = QString::fromUtf8(answer.data()); - // QMessageBox::information(this,"",item->uploadReply,0); + // save url in file + QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this); + if (file->open(QIODevice::WriteOnly)) + { + file->write(item->uploadUrl.toUtf8()); + file->close(); + } + } + filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar numUploads--; } +// this will protect saved youtube password from those who cannot read source code +static QString protectPass(QString str) +{ + QByteArray array = str.toUtf8(); + for (int i = 0; i < array.size(); i++) + array[i] = array[i] ^ 0xC4 ^ i; + array = array.toBase64(); + return QString::fromAscii(array.data()); +} + +static QString unprotectPass(QString str) +{ + QByteArray array = QByteArray::fromBase64(str.toAscii()); + for (int i = 0; i < array.size(); i++) + array[i] = array[i] ^ 0xC4 ^ i; + return QString::fromUtf8(array); +} + void PageVideos::uploadToYouTube() { int row = filesTable->currentRow(); VideoItem * item = nameItem(row); + if (item->pUploading) + { + if (QMessageBox::question(this, + tr("Are you sure?"), + tr("Do you really want do cancel uploading %1?").arg(item->name), + QMessageBox::Yes | QMessageBox::No) + != QMessageBox::Yes) + return; + item->pUploading->deleteLater(); + filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar + numUploads--; + return; + } + if (!netManager) netManager = new QNetworkAccessManager(this); HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager); dlg->deleteLater(); - if (!dlg->exec()) + if (config->value("youtube/save").toBool()) + { + dlg->cbSave->setChecked(true); + dlg->leAccount->setText(config->value("youtube/name").toString()); + dlg->lePassword->setText(unprotectPass(config->value("youtube/pswd").toString())); + } + + bool result = dlg->exec(); + + if (dlg->cbSave->isChecked()) + { + config->setValue("youtube/save", true); + config->setValue("youtube/name", dlg->leAccount->text()); + config->setValue("youtube/pswd", protectPass(dlg->lePassword->text())); + } + else + { + config->setValue("youtube/save", false); + config->setValue("youtube/name", ""); + config->setValue("youtube/pswd", ""); + } + + if (!result) return; QNetworkRequest request(QUrl(dlg->location)); @@ -985,7 +1126,7 @@ progressBar->setMinimum(0); progressBar->setMaximum(10000); progressBar->setValue(0); - // make it different from encoding progress-bar + // make it different from progress-bar used during encoding (use blue color) progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" ); filesTable->setCellWidget(row, vcProgress, progressBar); @@ -994,4 +1135,6 @@ connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64))); connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished())); numUploads++; + + updateDescription(); }