|
|
|
|
@ -965,23 +965,27 @@ void MessageList::onAttachmentDownloaded(const QString& chId, int64_t msgId) {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (m.contentType == "video/mp4") { |
|
|
|
|
QString fp = item->data(MsgVoiceFileRole).toString(); |
|
|
|
|
QString thumbPath = fp + ".jpg"; |
|
|
|
|
if (!fp.isEmpty() && QFile::exists(fp) && !QFile::exists(thumbPath)) { |
|
|
|
|
QPointer<MessageList> self(this); |
|
|
|
|
std::thread([self, fp, thumbPath]() { |
|
|
|
|
int rc = video_extract_thumbnail(fp.toUtf8().constData(), |
|
|
|
|
thumbPath.toUtf8().constData(), 320); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "MessageList: thumbnail %s rc=%d", |
|
|
|
|
qPrintable(thumbPath), rc); |
|
|
|
|
if (self) { |
|
|
|
|
QMetaObject::invokeMethod(self, [self]() { self->m_view->viewport()->update(); }, |
|
|
|
|
Qt::QueuedConnection); |
|
|
|
|
} |
|
|
|
|
}).detach(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (m.contentType == "video/mp4") prepareVideoThumbnail(item->data(MsgVoiceFileRole).toString()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Общие превью для каналов/PM; не более двух decoder workers, без запуска из paint. */ |
|
|
|
|
void MessageList::prepareVideoThumbnail(const QString& path) { |
|
|
|
|
if (path.isEmpty() || m_thumbnailJobs.contains(path) || !QFile::exists(path) || QFile::exists(path + ".jpg")) return; |
|
|
|
|
m_thumbnailPool.setMaxThreadCount(2); |
|
|
|
|
m_thumbnailJobs.insert(path); |
|
|
|
|
QPointer<MessageList> self(this); |
|
|
|
|
QObject* dispatcher = QCoreApplication::instance(); |
|
|
|
|
m_thumbnailPool.start([self, dispatcher, path]() { |
|
|
|
|
QString output = path + ".jpg"; |
|
|
|
|
int rc = video_extract_thumbnail(path.toUtf8().constData(), output.toUtf8().constData(), 320); |
|
|
|
|
if (rc) DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "thumbnail failed path=%s error=%d", qPrintable(path), rc); |
|
|
|
|
else DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "thumbnail ready path=%s", qPrintable(output)); |
|
|
|
|
QMetaObject::invokeMethod(dispatcher, [self, path]() { |
|
|
|
|
if (!self) return; |
|
|
|
|
self->m_thumbnailJobs.remove(path); |
|
|
|
|
self->m_view->viewport()->update(); |
|
|
|
|
}, Qt::QueuedConnection); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void MessageList::onMessageContextMenu(const QPoint& pos) { |
|
|
|
|
@ -992,6 +996,7 @@ void MessageList::onMessageContextMenu(const QPoint& pos) {
|
|
|
|
|
bool isVideo = (ct == "video/mp4"); |
|
|
|
|
bool isImage = ct.startsWith("image/"); |
|
|
|
|
bool isDmFile = isDmChannel(idx.data(MsgChannelIdRole).toString()) && ct == "application/octet-stream"; |
|
|
|
|
bool isDm = isDmChannel(idx.data(MsgChannelIdRole).toString()); |
|
|
|
|
if (!isVideo && !isImage && !isDmFile) return; |
|
|
|
|
|
|
|
|
|
QString filePath = idx.data(MsgVoiceFileRole).toString(); |
|
|
|
|
@ -1002,7 +1007,7 @@ void MessageList::onMessageContextMenu(const QPoint& pos) {
|
|
|
|
|
QAction* saveAct = menu.addAction(QString::fromUtf8("Сохранить в файл")); |
|
|
|
|
saveAct->setEnabled(downloaded); |
|
|
|
|
QAction* dlAct = nullptr; |
|
|
|
|
if (!downloaded && !isDmFile) dlAct = menu.addAction(QString::fromUtf8("Скачать")); |
|
|
|
|
if (!downloaded && !isDm) dlAct = menu.addAction(QString::fromUtf8("Скачать")); |
|
|
|
|
|
|
|
|
|
QAction* chosen = menu.exec(m_view->viewport()->mapToGlobal(pos)); |
|
|
|
|
|
|
|
|
|
@ -1137,6 +1142,7 @@ void MessageList::onDmMessages(const uint8_t* data, int len) {
|
|
|
|
|
item->setData(duration, MsgVideoDurationMsRole); |
|
|
|
|
item->setData(message.value("width").toInt(), MsgVideoWidthRole); |
|
|
|
|
item->setData(message.value("height").toInt(), MsgVideoHeightRole); |
|
|
|
|
prepareVideoThumbnail(path); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
m_model->appendRow(item); |
|
|
|
|
|