57 // columns in table with list of video files |
58 // columns in table with list of video files |
58 enum VideosColumns |
59 enum VideosColumns |
59 { |
60 { |
60 vcName, |
61 vcName, |
61 vcSize, |
62 vcSize, |
62 vcProgress, |
63 vcProgress, // either encoding or uploading |
63 |
64 |
64 vcNumColumns, |
65 vcNumColumns, |
65 }; |
66 }; |
66 |
67 |
|
68 // this class is used for items in first column in file-table |
67 class VideoItem : public QTableWidgetItem |
69 class VideoItem : public QTableWidgetItem |
68 { |
70 { |
69 // note: QTableWidgetItem is not Q_OBJECT |
71 // note: QTableWidgetItem is not Q_OBJECT |
70 |
72 |
71 public: |
73 public: |
72 VideoItem(const QString& name); |
74 VideoItem(const QString& name); |
73 ~VideoItem(); |
75 ~VideoItem(); |
74 |
76 |
75 QString name; |
77 QString name; |
76 QString desc; // description |
78 QString prefix; // original filename without extension |
77 QString uploadReply; |
79 QString desc; // description (duration, resolution, etc...) |
78 HWRecorder * pRecorder; // non NULL if file is being encoded |
80 QString uploadUrl; // http://youtu.be/??????? |
|
81 HWRecorder * pRecorder; // non NULL if file is being encoded |
79 QNetworkReply * pUploading; // non NULL if file is being uploaded |
82 QNetworkReply * pUploading; // non NULL if file is being uploaded |
80 bool seen; // used when updating directory |
83 bool seen; // used when updating directory |
81 float lastSizeUpdate; |
84 float lastSizeUpdate; |
82 float progress; |
85 float progress; |
83 |
86 |
269 pTopDescLayout->addWidget(labelThumbnail, 2); |
272 pTopDescLayout->addWidget(labelThumbnail, 2); |
270 |
273 |
271 // label with file description |
274 // label with file description |
272 labelDesc = new QLabel(pDescGroup); |
275 labelDesc = new QLabel(pDescGroup); |
273 labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop); |
276 labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop); |
|
277 labelDesc->setTextInteractionFlags(Qt::TextSelectableByMouse | |
|
278 Qt::TextSelectableByKeyboard | |
|
279 Qt::LinksAccessibleByMouse | |
|
280 Qt::LinksAccessibleByKeyboard); |
|
281 labelDesc->setTextFormat(Qt::RichText); |
274 pTopDescLayout->addWidget(labelDesc, 1); |
282 pTopDescLayout->addWidget(labelDesc, 1); |
275 |
283 |
276 // buttons: play and delete |
284 // buttons: play and delete |
277 btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup); |
285 btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup); |
278 btnPlay->setEnabled(false); |
286 btnPlay->setEnabled(false); |
311 connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged(int,int,int,int))); |
319 connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged(int,int,int,int))); |
312 connect(btnPlay, SIGNAL(clicked()), this, SLOT(playSelectedFile())); |
320 connect(btnPlay, SIGNAL(clicked()), this, SLOT(playSelectedFile())); |
313 connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles())); |
321 connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles())); |
314 connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube())); |
322 connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube())); |
315 connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory())); |
323 connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory())); |
|
324 connect(labelDesc, SIGNAL(linkActivated(const QString&)), this, SLOT(linkActivated(const QString&))); |
316 } |
325 } |
317 |
326 |
318 PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent), |
327 PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent), |
319 config(0), netManager(0) |
328 config(0), netManager(0) |
320 { |
329 { |
674 return (VideoItem*)filesTable->item(row, vcName); |
687 return (VideoItem*)filesTable->item(row, vcName); |
675 } |
688 } |
676 |
689 |
677 void PageVideos::clearThumbnail() |
690 void PageVideos::clearThumbnail() |
678 { |
691 { |
679 // add empty image for proper sizing |
692 // add empty (transparent) image for proper sizing |
680 QPixmap pic(ThumbnailSize); |
693 QPixmap pic(ThumbnailSize); |
681 pic.fill(QColor(0,0,0,0)); |
694 pic.fill(QColor(0,0,0,0)); |
682 labelThumbnail->setPixmap(pic); |
695 labelThumbnail->setPixmap(pic); |
683 } |
696 } |
684 |
697 |
685 void PageVideos::updateDescription() |
698 void PageVideos::updateDescription() |
686 { |
699 { |
687 VideoItem * item = nameItem(filesTable->currentRow()); |
700 VideoItem * item = nameItem(filesTable->currentRow()); |
688 if (!item) |
701 if (!item) |
689 { |
702 { |
|
703 // nothing is selected => clear description and return |
690 labelDesc->clear(); |
704 labelDesc->clear(); |
691 clearThumbnail(); |
705 clearThumbnail(); |
692 btnPlay->setEnabled(false); |
706 btnPlay->setEnabled(false); |
693 btnDelete->setEnabled(false); |
707 btnDelete->setEnabled(false); |
694 btnToYouTube->setEnabled(false); |
708 btnToYouTube->setEnabled(false); |
695 return; |
709 return; |
696 } |
710 } |
697 |
711 |
698 btnPlay->setEnabled(item->ready()); |
712 btnPlay->setEnabled(item->ready()); |
|
713 btnToYouTube->setEnabled(item->ready()); |
699 btnDelete->setEnabled(true); |
714 btnDelete->setEnabled(true); |
700 btnToYouTube->setEnabled(item->ready()); |
715 btnDelete->setText(item->ready()? QPushButton::tr("Delete") : QPushButton::tr("Cancel")); |
701 |
716 btnToYouTube->setText(item->pUploading? QPushButton::tr("Cancel uploading") : QPushButton::tr("Upload to YouTube")); |
|
717 |
|
718 // construct string with desctiption of this file to display it |
702 QString desc = item->name + "\n\n"; |
719 QString desc = item->name + "\n\n"; |
703 QString thumbName = ""; |
720 |
704 |
721 if (!item->ready()) |
705 if (item->ready()) |
722 desc += tr("(in progress...)"); |
|
723 else |
706 { |
724 { |
707 QString path = item->path(); |
725 QString path = item->path(); |
708 desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + "\n"; |
726 desc += tr("Date: ") + QFileInfo(path).created().toString(Qt::DefaultLocaleLongDate) + '\n'; |
709 desc += tr("Size: ") + FileSizeStr(path) + "\n"; |
727 desc += tr("Size: ") + FileSizeStr(path) + '\n'; |
710 if (item->desc == "") |
728 if (item->desc.isEmpty()) |
|
729 { |
|
730 // Extract description from file; |
|
731 // It will contain duration, resolution, etc and also comment added by hwengine. |
711 item->desc = LibavIteraction::instance().getFileInfo(path); |
732 item->desc = LibavIteraction::instance().getFileInfo(path); |
712 desc += item->desc; |
733 |
713 |
734 // extract prefix (original name) from description (it is enclosed in prefix[???]prefix) |
714 // extract thumbnail name fron description |
735 int prefixBegin = item->desc.indexOf("prefix["); |
715 int prefixBegin = desc.indexOf("prefix["); |
736 int prefixEnd = item->desc.indexOf("]prefix"); |
716 int prefixEnd = desc.indexOf("]prefix"); |
737 if (prefixBegin != -1 && prefixEnd != -1) |
717 if (prefixBegin != -1 && prefixEnd != -1) |
738 { |
|
739 item->prefix = desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7)); |
|
740 item->desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin); |
|
741 } |
|
742 } |
|
743 desc += item->desc + '\n'; |
|
744 } |
|
745 |
|
746 if (item->prefix.isEmpty()) |
|
747 { |
|
748 // try to extract prefix from file name instead |
|
749 if (item->ready()) |
|
750 item->prefix = item->name; |
|
751 else |
|
752 item->prefix = item->pRecorder->name; |
|
753 |
|
754 // remove extension |
|
755 int pt = item->prefix.lastIndexOf('.'); |
|
756 if (pt != -1) |
|
757 item->prefix.truncate(pt); |
|
758 } |
|
759 |
|
760 if (item->ready() && item->uploadUrl.isEmpty()) |
|
761 { |
|
762 // try to load url from file |
|
763 QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this); |
|
764 if (!file->open(QIODevice::ReadOnly)) |
|
765 item->uploadUrl = "no"; |
|
766 else |
718 { |
767 { |
719 QString prefix = desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7)); |
768 QByteArray data = file->readAll(); |
720 desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin); |
769 file->close(); |
721 thumbName = prefix; |
770 item->uploadUrl = QString::fromUtf8(data.data()); |
722 } |
771 } |
723 |
772 } |
724 desc += item->uploadReply; |
773 if (item->uploadUrl != "no") |
725 } |
774 desc += QString("<a href=\"%1\">%1</a>").arg(item->uploadUrl); |
726 else |
775 desc.replace("\n", "<br/>"); |
727 desc += tr("(in progress...)"); |
776 |
728 |
777 labelDesc->setText(desc); |
729 if (thumbName.isEmpty()) |
778 |
730 { |
779 if (!item->prefix.isEmpty()) |
731 if (item->ready()) |
780 { |
732 thumbName = item->name; |
781 QString thumbName = cfgdir->absoluteFilePath("VideoTemp/" + item->prefix); |
733 else |
|
734 thumbName = item->pRecorder->name; |
|
735 // remove extension |
|
736 int pt = thumbName.lastIndexOf('.'); |
|
737 if (pt != -1) |
|
738 thumbName.truncate(pt); |
|
739 } |
|
740 |
|
741 if (!thumbName.isEmpty()) |
|
742 { |
|
743 thumbName = cfgdir->absoluteFilePath("VideoTemp/" + thumbName); |
|
744 QPixmap pic; |
782 QPixmap pic; |
745 if (pic.load(thumbName + ".png") || pic.load(thumbName + ".bmp")) |
783 if (pic.load(thumbName + ".png") || pic.load(thumbName + ".bmp")) |
746 { |
784 { |
747 if (pic.height()*ThumbnailSize.width() > pic.width()*ThumbnailSize.height()) |
785 if (pic.height()*ThumbnailSize.width() > pic.width()*ThumbnailSize.height()) |
748 pic = pic.scaledToWidth(ThumbnailSize.width()); |
786 pic = pic.scaledToWidth(ThumbnailSize.width()); |
910 } |
977 } |
911 addRecorder(pRecorder); |
978 addRecorder(pRecorder); |
912 } |
979 } |
913 } |
980 } |
914 |
981 |
915 void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal) |
982 VideoItem * PageVideos::itemFromReply(QNetworkReply* reply, int & row) |
916 { |
983 { |
917 QNetworkReply* reply = (QNetworkReply*)sender(); |
|
918 |
|
919 VideoItem * item = NULL; |
984 VideoItem * item = NULL; |
920 int row; |
|
921 int count = filesTable->rowCount(); |
985 int count = filesTable->rowCount(); |
922 // find corresponding item (maybe there is a better wat to implement this?) |
986 // find corresponding item (maybe there is a better way to implement this?) |
923 for (int i = 0; i < count; i++) |
987 for (int i = 0; i < count; i++) |
924 { |
988 { |
925 item = nameItem(i); |
989 item = nameItem(i); |
926 if (item->pUploading == reply) |
990 if (item->pUploading == reply) |
927 { |
991 { |
928 row = i; |
992 row = i; |
929 break; |
993 break; |
930 } |
994 } |
931 } |
995 } |
932 Q_ASSERT(item); |
996 return item; |
933 |
997 } |
|
998 |
|
999 void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal) |
|
1000 { |
|
1001 QNetworkReply* reply = (QNetworkReply*)sender(); |
|
1002 int row; |
|
1003 VideoItem * item = itemFromReply(reply, row); |
934 setProgress(row, item, bytesSent*1.0/bytesTotal); |
1004 setProgress(row, item, bytesSent*1.0/bytesTotal); |
935 } |
1005 } |
936 |
1006 |
937 void PageVideos::uploadFinished() |
1007 void PageVideos::uploadFinished() |
938 { |
1008 { |
939 QNetworkReply* reply = (QNetworkReply*)sender(); |
1009 QNetworkReply* reply = (QNetworkReply*)sender(); |
940 |
1010 reply->deleteLater(); |
941 VideoItem * item = NULL; |
1011 |
942 int row; |
1012 int row; |
943 int count = filesTable->rowCount(); |
1013 VideoItem * item = itemFromReply(reply, row); |
944 for (int i = 0; i < count; i++) |
1014 if (!item) |
945 { |
1015 return; |
946 item = nameItem(i); |
1016 |
947 if (item->pUploading == reply) |
1017 item->pUploading = NULL; |
|
1018 |
|
1019 // extract video id from reply |
|
1020 QString videoid; |
|
1021 QXmlStreamReader xml(reply); |
|
1022 while (!xml.atEnd()) |
|
1023 { |
|
1024 xml.readNext(); |
|
1025 if (xml.qualifiedName() == "yt:videoid") |
948 { |
1026 { |
949 row = i; |
1027 videoid = xml.readElementText(); |
950 break; |
1028 break; |
951 } |
1029 } |
952 } |
1030 } |
953 Q_ASSERT(item); |
1031 |
954 |
1032 if (!videoid.isEmpty()) |
955 item->pUploading = NULL; |
1033 { |
956 QByteArray answer = reply->readAll(); |
1034 item->uploadUrl = "http://youtu.be/" + videoid; |
957 item->uploadReply = QString::fromUtf8(answer.data()); |
1035 updateDescription(); |
958 // QMessageBox::information(this,"",item->uploadReply,0); |
1036 |
|
1037 // save url in file |
|
1038 QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this); |
|
1039 if (file->open(QIODevice::WriteOnly)) |
|
1040 { |
|
1041 file->write(item->uploadUrl.toUtf8()); |
|
1042 file->close(); |
|
1043 } |
|
1044 } |
|
1045 |
959 filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar |
1046 filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar |
960 numUploads--; |
1047 numUploads--; |
961 } |
1048 } |
962 |
1049 |
|
1050 // this will protect saved youtube password from those who cannot read source code |
|
1051 static QString protectPass(QString str) |
|
1052 { |
|
1053 QByteArray array = str.toUtf8(); |
|
1054 for (int i = 0; i < array.size(); i++) |
|
1055 array[i] = array[i] ^ 0xC4 ^ i; |
|
1056 array = array.toBase64(); |
|
1057 return QString::fromAscii(array.data()); |
|
1058 } |
|
1059 |
|
1060 static QString unprotectPass(QString str) |
|
1061 { |
|
1062 QByteArray array = QByteArray::fromBase64(str.toAscii()); |
|
1063 for (int i = 0; i < array.size(); i++) |
|
1064 array[i] = array[i] ^ 0xC4 ^ i; |
|
1065 return QString::fromUtf8(array); |
|
1066 } |
|
1067 |
963 void PageVideos::uploadToYouTube() |
1068 void PageVideos::uploadToYouTube() |
964 { |
1069 { |
965 int row = filesTable->currentRow(); |
1070 int row = filesTable->currentRow(); |
966 VideoItem * item = nameItem(row); |
1071 VideoItem * item = nameItem(row); |
967 |
1072 |
|
1073 if (item->pUploading) |
|
1074 { |
|
1075 if (QMessageBox::question(this, |
|
1076 tr("Are you sure?"), |
|
1077 tr("Do you really want do cancel uploading %1?").arg(item->name), |
|
1078 QMessageBox::Yes | QMessageBox::No) |
|
1079 != QMessageBox::Yes) |
|
1080 return; |
|
1081 item->pUploading->deleteLater(); |
|
1082 filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar |
|
1083 numUploads--; |
|
1084 return; |
|
1085 } |
|
1086 |
968 if (!netManager) |
1087 if (!netManager) |
969 netManager = new QNetworkAccessManager(this); |
1088 netManager = new QNetworkAccessManager(this); |
970 |
1089 |
971 HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager); |
1090 HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager); |
972 dlg->deleteLater(); |
1091 dlg->deleteLater(); |
973 if (!dlg->exec()) |
1092 if (config->value("youtube/save").toBool()) |
|
1093 { |
|
1094 dlg->cbSave->setChecked(true); |
|
1095 dlg->leAccount->setText(config->value("youtube/name").toString()); |
|
1096 dlg->lePassword->setText(unprotectPass(config->value("youtube/pswd").toString())); |
|
1097 } |
|
1098 |
|
1099 bool result = dlg->exec(); |
|
1100 |
|
1101 if (dlg->cbSave->isChecked()) |
|
1102 { |
|
1103 config->setValue("youtube/save", true); |
|
1104 config->setValue("youtube/name", dlg->leAccount->text()); |
|
1105 config->setValue("youtube/pswd", protectPass(dlg->lePassword->text())); |
|
1106 } |
|
1107 else |
|
1108 { |
|
1109 config->setValue("youtube/save", false); |
|
1110 config->setValue("youtube/name", ""); |
|
1111 config->setValue("youtube/pswd", ""); |
|
1112 } |
|
1113 |
|
1114 if (!result) |
974 return; |
1115 return; |
975 |
1116 |
976 QNetworkRequest request(QUrl(dlg->location)); |
1117 QNetworkRequest request(QUrl(dlg->location)); |
977 request.setRawHeader("Content-Type", "application/octet-stream"); |
1118 request.setRawHeader("Content-Type", "application/octet-stream"); |
978 |
1119 |
983 // add progress bar |
1124 // add progress bar |
984 QProgressBar * progressBar = new QProgressBar(filesTable); |
1125 QProgressBar * progressBar = new QProgressBar(filesTable); |
985 progressBar->setMinimum(0); |
1126 progressBar->setMinimum(0); |
986 progressBar->setMaximum(10000); |
1127 progressBar->setMaximum(10000); |
987 progressBar->setValue(0); |
1128 progressBar->setValue(0); |
988 // make it different from encoding progress-bar |
1129 // make it different from progress-bar used during encoding (use blue color) |
989 progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" ); |
1130 progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" ); |
990 filesTable->setCellWidget(row, vcProgress, progressBar); |
1131 filesTable->setCellWidget(row, vcProgress, progressBar); |
991 |
1132 |
992 QNetworkReply* reply = netManager->put(request, file); |
1133 QNetworkReply* reply = netManager->put(request, file); |
993 item->pUploading = reply; |
1134 item->pUploading = reply; |
994 connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64))); |
1135 connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64))); |
995 connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished())); |
1136 connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished())); |
996 numUploads++; |
1137 numUploads++; |
997 } |
1138 |
|
1139 updateDescription(); |
|
1140 } |