diff --git a/tools/chatgui/src/mainwindow.h b/tools/chatgui/src/mainwindow.h index 29f364f4..e8f6a778 100644 --- a/tools/chatgui/src/mainwindow.h +++ b/tools/chatgui/src/mainwindow.h @@ -30,12 +30,12 @@ class CallAudioEngine; class MemberFileBrowser; class MessageNotification; class QMessageBox; +struct MessageRow; struct AutoPlayItem { - QString channelId; - qint64 msgId = 0; - bool downloadStarted = false; - float durationSec = 0.0f; + QString channelId; // Канал снимка; msgId уникален только внутри него. + qint64 msgId = 0; // Локальный rowid сообщения. + bool downloadStarted = false; // Команда загрузки принята очередью; false допускает первую попытку. }; class MainWindow : public QMainWindow { @@ -108,8 +108,9 @@ private: void setupBridgeCallbacks(); void setupConnects(); void onShutdownCheck(); - void autoPlayCheckNewMessage(const QString& channelId); + void autoPlayCheckNewMessage(const QString& channelId, qint64 msgId = 0); QString autoPlayMediaDir() const; + QString autoPlayFilePath(const QString& channelId, const MessageRow& message) const; void showMemberFiles(const QString& channel, quint64 owner = 0); void showStreamPublisher(quint64 group, StreamGridWindow* returnTo = nullptr); // Управление; закрытие возвращает фокус сетке. @@ -145,7 +146,8 @@ private: QTimer* m_cpuLoadTimer = nullptr; bool m_cpuLoadPending = false; QTimer* m_shutdownTimer = nullptr; - QSet m_autoPlayDownloading; /* msgIds with download in progress */ + QSet> m_autoPlayDownloading; // (channel,rowid) ожидающей загрузки. + QHash m_lastAutoPlayId; // Подавляет повторное добавление последнего сообщения канала. QHash m_activeCalls; /* call_id → окно звонка */ QPointer m_radioCallWindow; diff --git a/tools/chatgui/transport/gui_bridge.h b/tools/chatgui/transport/gui_bridge.h index 5f2a27ec..9720eb00 100644 --- a/tools/chatgui/transport/gui_bridge.h +++ b/tools/chatgui/transport/gui_bridge.h @@ -98,6 +98,9 @@ int gui_bridge_post_uasync_owned_fn(void (*fn)(void*), void* arg); * 0 передаёт владение: fn освобождает arg после исполнения, мост — при отмене. * -1 оставляет блок вызывающему для u_free; вложенные ресурсы этим API не поддерживаются. */ int gui_bridge_post_bound_fn(void (*fn)(void*), void* arg, struct UTUN_INSTANCE** binding); +/* Скачать вложение канала: десятичный непустой channel (до 63 байт), msg_id>0. + * Копия/привязка/владение принадлежат мосту. 0 — запрос принят, -1 — отказ с логом без heap-обязательств GUI. */ +int gui_bridge_download_attachment(const char* channel, int64_t msg_id); /* Команды обычного звонка: GUI не передаёт instance или heap-аргументы. * Под mutex моста копируют данные, привязываются к живому running-ядру и передают владение очереди. diff --git a/tools/chatgui/transport/gui_bridge_impl.cpp b/tools/chatgui/transport/gui_bridge_impl.cpp index 0b64da72..e5aac215 100644 --- a/tools/chatgui/transport/gui_bridge_impl.cpp +++ b/tools/chatgui/transport/gui_bridge_impl.cpp @@ -789,6 +789,29 @@ int gui_bridge_post_bound_fn(void (*fn)(void*), void* arg, struct UTUN_INSTANCE* return 0; } +/* Общий путь загрузки для delegate, контекстного меню и auto-play. */ +int gui_bridge_download_attachment(const char* channel, int64_t msg_id) { + const size_t length = channel ? strnlen(channel, sizeof(attachment_dl_req::channel_id)) : 0; + if (!length || length == sizeof(attachment_dl_req::channel_id) || strspn(channel, "0123456789") != length || msg_id <= 0) { + DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment download rejected: invalid channel/id length=%zu id=%lld", length, (long long)msg_id); + return -1; + } + std::lock_guard guard(bridge_mutex); + if (!g_inst || !g_ua || !uasync_is_running(g_ua)) { + DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment download rejected: core unavailable channel=%s id=%lld", channel, (long long)msg_id); + return -1; + } + auto* request = static_cast(u_calloc(1, sizeof(attachment_dl_req))); + if (!request) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment download allocation failed"); return -1; } + request->inst = g_inst; request->msg_id = msg_id; memcpy(request->channel_id, channel, length + 1); + if (post_command(chat_core_attachment_download_trampoline, request, true) < 0) { + u_free(request); DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment download queue failed channel=%s id=%lld", channel, (long long)msg_id); + return -1; + } + DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "attachment download queued channel=%s id=%lld", channel, (long long)msg_id); + return 0; +} + /* Создание payload и enqueue атомарны относительно detach; GUI не читает заимствованный instance. */ int gui_bridge_call_start(const char* channel, uint64_t peer, uint64_t call_id) { const size_t length = channel ? strnlen(channel, sizeof(call_start_arg::ch_id)) : 0;