diff --git a/tools/chatgui/src/inputbar.cpp b/tools/chatgui/src/inputbar.cpp index 35dca3d0..c75c7bab 100644 --- a/tools/chatgui/src/inputbar.cpp +++ b/tools/chatgui/src/inputbar.cpp @@ -26,6 +26,8 @@ extern "C" { #include "video/video.h" +#include "media_async/attachment_send.h" +#include "mem.h" } static bool isImageFile(const QString& suffix) { @@ -183,6 +185,7 @@ InputBar::InputBar(EmojiPanel *emojiPanel, AudioRecorder *recorder, QWidget *par "QPushButton { border: none; color: white; font-size: 16px; background: transparent; }" "QPushButton:hover { color: #fdd; }"); connect(m_cancelRecBtn, &QPushButton::clicked, this, [this]() { + m_pttActive = false; if (m_recorder && m_recorder->isRecording()) m_recorder->stopRecording(); }); roLayout->addWidget(m_cancelRecBtn); @@ -244,17 +247,25 @@ void InputBar::onSendClicked() { if (hasAttach) { if (m_pendingAttachAsImage) { GUI_INFO("send: image=%s name=%s", qPrintable(m_pendingAttachSrc), qPrintable(m_pendingAttachName)); - emit sendImageAttachment(m_pendingAttachSrc, m_pendingAttachDest, m_pendingAttachName); + emit sendImageAttachment(m_channelIdForRecord, m_pendingAttachSrc, m_pendingAttachDest, m_pendingAttachName); m_attachPreview->hide(); } else if (m_pendingAttachAsVideo) { GUI_INFO("send: video=%s name=%s", qPrintable(m_pendingAttachSrc), qPrintable(m_pendingAttachName)); - emit sendVideoAttachment(m_pendingAttachSrc, m_pendingAttachDest, m_pendingAttachName); + auto* request = (struct attachment_send_req*)u_calloc(1, sizeof(struct attachment_send_req)); + QByteArray source = m_pendingAttachSrc.toUtf8(), name = (QFileInfo(m_pendingAttachName).completeBaseName() + ".mp4").toUtf8(); + if (!request || source.size() >= (int)sizeof(request->source) || name.size() >= (int)sizeof(request->info.name)) { + DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "InputBar: video request allocation/path failed"); u_free(request); return; + } + request->info.kind = ATTACHMENT_VIDEO; request->transcode = 1; + snprintf(request->source, sizeof(request->source), "%s", source.constData()); + snprintf(request->info.name, sizeof(request->info.name), "%s", name.constData()); + emit submitAttachment(m_channelIdForRecord, request); m_attachPreviewLabel->setText(QString::fromUtf8("\xF0\x9F\x8E\xAC Перекодирование… 0%")); m_attachPreview->show(); m_videoProgressTimer->start(); } else { GUI_INFO("send: file=%s name=%s", qPrintable(m_pendingAttachSrc), qPrintable(m_pendingAttachName)); - emit sendFileAttachment(m_pendingAttachSrc, m_pendingAttachDest, m_pendingAttachName); + emit sendFileAttachment(m_channelIdForRecord, m_pendingAttachSrc, m_pendingAttachDest, m_pendingAttachName); m_attachPreview->hide(); } m_pendingAttachSrc.clear(); @@ -280,7 +291,7 @@ void InputBar::setRecordingContext(const QString& mediaDirBase, const QString& c void InputBar::setDmMode(bool enabled) { m_dmMode = enabled; - m_pttBtn->setEnabled(!enabled); + m_pttBtn->setEnabled(true); m_attachBtn->setEnabled(true); } @@ -292,7 +303,8 @@ void InputBar::setMediaEnabled(bool enabled) { void InputBar::onPttPressed() { if (!m_recorder || !m_recorder->isInitialized()) return; m_recorder->startRecording(); - m_pttActive = true; + m_pttActive = m_recorder->isRecording(); + m_recordTarget = m_channelIdForRecord; m_pttBtn->setStyleSheet(m_pttBtn->styleSheet() + "QPushButton { background: #e74c3c; border-radius: 4px; }"); } @@ -311,47 +323,11 @@ void InputBar::onPttReleased() { return; } - const int16_t* pcm = m_recorder->bufferData(); - size_t pcmSize = m_recorder->bufferSize(); - if (pcmSize == 0) return; - - QString mediaPath = m_mediaDirBase + "/" + m_channelIdForRecord; - VoiceEncoder::ensureMediaDir(mediaPath); - GUI_INFO("InputBar::onPttReleased: mediaDir=%s exists=%d channel=%s", - qPrintable(mediaPath), QDir(mediaPath).exists(), qPrintable(m_channelIdForRecord)); - - QString fileName = VoiceEncoder::generateFileName(); - QString mediaFile = mediaPath + "/" + fileName; - float durationSec = 0; - int frames = VoiceEncoder::encodeToFile(pcm, pcmSize, 48000, 1, mediaFile, durationSec); - if (frames <= 0) { - QFile::remove(mediaFile); - return; - } - - m_voiceFilePath = mediaFile; - - const auto& wf = m_recorder->waveformLevels(); - QString wfStr = "wf="; - static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int N = (int)wf.size(); - for (int i = 0; i < 100; i++) { - int s = i * N / 100, e = (i + 1) * N / 100; - if (s >= N) s = N - 1; - if (e <= s) e = s + 1; - if (e > N) e = N; - float sum = 0; int cnt = 0; - for (int j = s; j < e; j++) { sum += wf[j]; cnt++; } - float v = cnt > 0 ? sum / cnt : 0; - float db = v > 0.00001f ? 20.0f * log10f(v) : -100.0f; - int idx = (int)((db + 60.0f) / 60.0f * 64.0f); - if (idx < 0) idx = 0; if (idx > 63) idx = 63; - wfStr += b64[idx]; - } - wfStr += QString(";dur=%1;").arg(durationMs); - GUI_INFO("InputBar: voice message ready: file=%s dur=%.1fs", - qPrintable(mediaFile), durationSec); - emit sendVoiceMessage(mediaFile, mediaFile, durationMs, wfStr); + auto* request = (struct attachment_send_req*)u_calloc(1, sizeof(struct attachment_send_req)); + if (!request) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "InputBar: voice request allocation failed"); return; } + request->info.kind = ATTACHMENT_VOICE; request->preset = VoiceEncoder::preset(); + if (!m_recorder->takeRecording(request)) { u_free(request); return; } + emit submitAttachment(m_recordTarget, request); } void InputBar::onAttachClicked() { @@ -360,7 +336,7 @@ void InputBar::onAttachClicked() { if (srcPath.isEmpty()) return; QFileInfo fi(srcPath); - if (m_dmMode) { + if (m_dmMode && !QStringList{"mp4", "mov", "mkv", "avi", "webm", "m4v"}.contains(fi.suffix().toLower())) { m_pendingAttachSrc = srcPath; m_pendingAttachDest = srcPath; m_pendingAttachName = fi.fileName(); @@ -381,9 +357,9 @@ void InputBar::onAttachClicked() { QString ts = now.toString("yyyyMMdd-HHmmss"); int rnd = rand() & 0xFFFF; - bool asImage = isImageFile(fi.suffix()); + bool asImage = !m_dmMode && isImageFile(fi.suffix()); bool asVideo = false; - if (!asImage && video_probe(srcPath.toUtf8().constData())) { + if (!asImage && QStringList{"mp4", "mov", "mkv", "avi", "webm", "m4v"}.contains(fi.suffix().toLower())) { QMessageBox box(this); box.setWindowTitle(QString::fromUtf8("Отправить видео")); box.setText(QString::fromUtf8("Файл содержит видео. Как отправить?")); diff --git a/tools/chatgui/src/inputbar.h b/tools/chatgui/src/inputbar.h index 3032cf97..0d119d69 100644 --- a/tools/chatgui/src/inputbar.h +++ b/tools/chatgui/src/inputbar.h @@ -9,6 +9,7 @@ class QProgressBar; class QTimer; class EmojiPanel; class AudioRecorder; +struct attachment_send_req; class InputBar : public QWidget { Q_OBJECT @@ -18,19 +19,16 @@ public: void setRecordingContext(const QString& mediaDirBase, const QString& channelId); - /* DM использует прикрепление оригинального файла без канальной подготовки. */ + /* Общий composer для канала и PM. */ void setDmMode(bool enabled); void setMediaEnabled(bool enabled); signals: void sendMessage(const QString &text); - void sendVoiceMessage(const QString& mediaSrc, const QString& mediaDest, - int durationMs, const QString& waveformStr); - void sendFileAttachment(const QString& mediaSrc, const QString& mediaDest, + void submitAttachment(const QString& target, struct attachment_send_req* request); + void sendFileAttachment(const QString& target, const QString& mediaSrc, const QString& mediaDest, const QString& displayName); - void sendVideoAttachment(const QString& mediaSrc, const QString& mediaDest, - const QString& displayName); - void sendImageAttachment(const QString& mediaSrc, const QString& mediaDest, + void sendImageAttachment(const QString& target, const QString& mediaSrc, const QString& mediaDest, const QString& displayName); protected: @@ -70,6 +68,7 @@ private: QString m_voiceFilePath; QString m_mediaDirBase; QString m_channelIdForRecord; + QString m_recordTarget; QString m_pendingAttachSrc; QString m_pendingAttachDest; QString m_pendingAttachName;