From 579b20fff737d8fc26b17514eabf9fb1684a2151 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 23 Oct 2022 22:02:19 +0300 Subject: [PATCH] Added initial ability to transcribe video messages. --- Telegram/SourceFiles/api/api_transcribes.cpp | 43 +++++++++++++------ Telegram/SourceFiles/api/api_transcribes.h | 1 + Telegram/SourceFiles/data/data_document.cpp | 16 ++++++- Telegram/SourceFiles/data/data_document.h | 4 ++ .../SourceFiles/data/data_media_types.cpp | 17 ++++++++ .../SourceFiles/data/data_replies_list.cpp | 2 +- .../view/media/history_view_document.cpp | 27 +++++++++--- .../view/media/history_view_document.h | 2 + .../history/view/media/history_view_gif.cpp | 35 +++++++++++++++ .../history/view/media/history_view_gif.h | 2 + Telegram/SourceFiles/ui/chat/chat.style | 1 + Telegram/SourceFiles/ui/chat/chat_style.cpp | 1 + Telegram/SourceFiles/ui/chat/chat_style.h | 4 ++ 13 files changed, 134 insertions(+), 21 deletions(-) diff --git a/Telegram/SourceFiles/api/api_transcribes.cpp b/Telegram/SourceFiles/api/api_transcribes.cpp index 30c7fdee5..6a69b53d6 100644 --- a/Telegram/SourceFiles/api/api_transcribes.cpp +++ b/Telegram/SourceFiles/api/api_transcribes.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/history.h" #include "main/main_session.h" +#include "data/data_document.h" #include "data/data_session.h" #include "data/data_peer.h" #include "apiwrap.h" @@ -30,6 +31,9 @@ void Transcribes::toggle(not_null item) { _session->data().requestItemResize(item); } else if (!i->second.requestId) { i->second.shown = !i->second.shown; + if (i->second.roundview) { + _session->data().requestItemViewRefresh(item); + } _session->data().requestItemResize(item); } } @@ -55,6 +59,9 @@ void Transcribes::apply(const MTPDupdateTranscribedAudio &update) { j->second.result = text; j->second.pending = update.is_pending(); if (const auto item = _session->data().message(i->second)) { + if (j->second.roundview) { + _session->data().requestItemViewRefresh(item); + } _session->data().requestItemResize(item); } } @@ -63,29 +70,41 @@ void Transcribes::load(not_null item) { if (!item->isHistoryEntry() || item->isLocal()) { return; } + const auto toggleRound = [](not_null item, Entry &entry) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { + if (document->isVideoMessage()) { + entry.roundview = true; + document->owner().requestItemViewRefresh(item); + } + } + } + }; const auto id = item->fullId(); const auto requestId = _api.request(MTPmessages_TranscribeAudio( item->history()->peer->input, MTP_int(item->id) )).done([=](const MTPmessages_TranscribedAudio &result) { - result.match([&](const MTPDmessages_transcribedAudio &data) { - auto &entry = _map[id]; - entry.requestId = 0; - entry.pending = data.is_pending(); - entry.result = qs(data.vtext()); - _ids.emplace(data.vtranscription_id().v, id); - if (const auto item = _session->data().message(id)) { - _session->data().requestItemResize(item); - } - }); + const auto &data = result.data(); + auto &entry = _map[id]; + entry.requestId = 0; + entry.pending = data.is_pending(); + entry.result = qs(data.vtext()); + _ids.emplace(data.vtranscription_id().v, id); + if (const auto item = _session->data().message(id)) { + toggleRound(item, entry); + _session->data().requestItemResize(item); + } }).fail([=](const MTP::Error &error) { auto &entry = _map[id]; entry.requestId = 0; entry.pending = false; entry.failed = true; - if (error.type() == qstr("MSG_VOICE_TOO_LONG")) { + if (error.type() == u"MSG_VOICE_TOO_LONG"_q) { entry.toolong = true; - } else if (const auto item = _session->data().message(id)) { + } + if (const auto item = _session->data().message(id)) { + toggleRound(item, entry); _session->data().requestItemResize(item); } }).send(); diff --git a/Telegram/SourceFiles/api/api_transcribes.h b/Telegram/SourceFiles/api/api_transcribes.h index a63fb97af..2f8bb2234 100644 --- a/Telegram/SourceFiles/api/api_transcribes.h +++ b/Telegram/SourceFiles/api/api_transcribes.h @@ -27,6 +27,7 @@ public: bool failed = false; bool toolong = false; bool pending = false; + bool roundview = false; mtpRequestId requestId = 0; }; diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 468464065..1bf78e3fa 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -381,6 +381,10 @@ void DocumentData::setattributes( type = data.is_round_message() ? RoundVideoDocument : VideoDocument; + if (data.is_round_message()) { + _additional = std::make_unique(); + round()->duration = data.vduration().v; + } } else if (const auto info = sticker()) { info->type = StickerType::Webm; } @@ -397,7 +401,7 @@ void DocumentData::setattributes( _additional = std::make_unique(); } } - if (const auto voiceData = voice()) { + if (const auto voiceData = voice() ? voice() : round()) { voiceData->duration = data.vduration().v; voiceData->waveform = documentWaveformDecode( data.vwaveform().value_or_empty()); @@ -1252,6 +1256,16 @@ const VoiceData *DocumentData::voice() const { return const_cast(this)->voice(); } +RoundData *DocumentData::round() { + return isVideoMessage() + ? static_cast(_additional.get()) + : nullptr; +} + +const RoundData *DocumentData::round() const { + return const_cast(this)->round(); +} + bool DocumentData::hasRemoteLocation() const { return (_dc != 0 && _access != 0); } diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 0b11ed013..11f858251 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -90,6 +90,8 @@ struct VoiceData : public DocumentAdditionalData { char wavemax = 0; }; +using RoundData = VoiceData; + namespace Serialize { class Document; } // namespace Serialize; @@ -152,6 +154,8 @@ public: [[nodiscard]] const SongData *song() const; [[nodiscard]] VoiceData *voice(); [[nodiscard]] const VoiceData *voice() const; + [[nodiscard]] RoundData *round(); + [[nodiscard]] const RoundData *round() const; void forceIsStreamedAnimation(); [[nodiscard]] bool isVoiceMessage() const; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 8a46bd48c..25fed8947 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -1048,6 +1048,23 @@ std::unique_ptr MediaFile::createView( _document, _skipPremiumEffect, replacing)); + } else if (_document->isVideoMessage()) { + const auto &entry = _document->session().api().transcribes().entry( + parent()); + if (!entry.requestId + && entry.shown + && entry.roundview + && !entry.pending) { + return std::make_unique( + message, + realParent, + _document); + } else { + return std::make_unique( + message, + realParent, + _document); + } } else if (_document->isAnimation() || _document->isVideoFile()) { return std::make_unique( message, diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index 41c698eb1..9c5fee598 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -839,7 +839,7 @@ std::optional RepliesList::computeUnreadCountLocally( end(_list), wasReadTillId, std::greater<>()); - return std::max(*wasUnreadCountAfter - (till - from), 0); + return std::max(int(*wasUnreadCountAfter - (till - from)), 0); } return std::nullopt; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 51f7802c7..132e13be7 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -155,7 +155,7 @@ void PaintWaveform( if (const auto song = document->song()) { add(FormatPlayedText(duration, duration)); add(FormatDurationAndSizeText(duration, document->size)); - } else if (const auto voice = document->voice()) { + } else if (const auto voice = document->voice() ? document->voice() : document->round()) { add(FormatPlayedText(duration, duration)); add(FormatDurationAndSizeText(duration, document->size)); } else if (document->isVideoFile()) { @@ -174,6 +174,12 @@ Document::Document( not_null document) : File(parent, realParent) , _data(document) { + if (_data->isVideoMessage()) { + const auto &entry = _data->session().api().transcribes().entry( + realParent); + _transcribedRound = entry.shown; + } + auto caption = createCaption(); createComponents(!caption.isEmpty()); @@ -215,7 +221,7 @@ bool Document::dataLoaded() const { void Document::createComponents(bool caption) { uint64 mask = 0; - if (_data->isVoiceMessage()) { + if (_data->isVoiceMessage() || _transcribedRound) { mask |= HistoryDocumentVoice::Bit(); } else { mask |= HistoryDocumentNamed::Bit(); @@ -344,7 +350,9 @@ QSize Document::countOptimalSize() { if (thumbed) { accumulate_max(maxWidth, tleft + MaxStatusWidth(_data) + tright); } else { - auto unread = _data->isVoiceMessage() ? (st::mediaUnreadSkip + st::mediaUnreadSize) : 0; + auto unread = (_data->isVoiceMessage() || _transcribedRound) + ? (st::mediaUnreadSkip + st::mediaUnreadSize) + : 0; accumulate_max(maxWidth, tleft + MaxStatusWidth(_data) + unread + _parent->skipBlockWidth() + st::msgPadding.right()); } @@ -604,8 +612,11 @@ void Document::draw( if (voice) { ensureDataMediaCreated(); - if (const auto voiceData = _data->voice()) { - if (voiceData->waveform.isEmpty()) { + { + const auto voiceData = _data->isVideoMessage() + ? _data->round() + : _data->voice(); + if (voiceData && voiceData->waveform.isEmpty()) { if (loaded) { Local::countVoiceWaveform(_dataMedia.get()); } @@ -642,7 +653,7 @@ void Document::draw( PaintWaveform(p, context, - _data->voice(), + _transcribedRound ? _data->round() : _data->voice(), namewidth + st::msgWaveformSkip, progress); p.restore(); @@ -1156,6 +1167,8 @@ void Document::setStatusSize(int64 newSize, TimeId realDuration) const { ? _data->song()->duration : (_data->isVoiceMessage() ? _data->voice()->duration + : _transcribedRound + ? _data->round()->duration : -1); File::setStatusSize(newSize, _data->size, duration, realDuration); if (auto thumbed = Get()) { @@ -1190,7 +1203,7 @@ bool Document::updateStatusText() const { statusSize = Ui::FileStatusSizeReady; } - if (_data->isVoiceMessage()) { + if (_data->isVoiceMessage() || _transcribedRound) { const auto state = ::Media::Player::instance()->getState(AudioMsgId::Type::Voice); if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId()) && !::Media::Player::IsStoppedOrStopping(state.state)) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h index fc9856a6d..01bdabe30 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_document.h @@ -174,6 +174,8 @@ private: mutable TooltipFilename _tooltipFilename; + bool _transcribedRound = false; + }; bool DrawThumbnailAsSongCover( diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 18c7d9df9..1f5605a0e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/view/media/history_view_gif.h" +#include "apiwrap.h" +#include "api/api_transcribes.h" #include "lang/lang_keys.h" #include "mainwindow.h" #include "main/main_session.h" @@ -93,6 +95,13 @@ Gif::Gif( _data->loadVideoThumbnail(realParent->fullId()); } } + + _transcribe = std::make_shared([=, + id = realParent->fullId()] { + if (const auto item = _data->session().data().message(id)) { + _data->session().api().transcribes().toggle(item); + } + }); } Gif::~Gif() { @@ -673,6 +682,21 @@ void Gif::draw(Painter &p, const PaintContext &context) const { fastShareTop -= (st::msgDateImgDelta + st::msgDateImgPadding.y() + st::msgDateFont->height + st::msgDateImgPadding.y()); } _parent->drawRightAction(p, context, fastShareLeft, fastShareTop, 2 * paintx + paintw); + if (_data->session().premium()) { + const auto stm = context.messageStyle(); + PainterHighQualityEnabler hq(p); + p.setPen(Qt::NoPen); + p.setBrush(context.st->msgServiceBg()); + + const auto s = st::historyFastShareSize; + const auto r = QRect( + fastShareLeft, + fastShareTop - s - st::msgDateImgDelta, + s, + s); + p.drawEllipse(r); + context.st->historyFastTranscribeIcon().paintInCenter(p, r); + } } } } @@ -976,6 +1000,17 @@ TextState Gif::textState(QPoint point, StateRequest request) const { if (QRect(fastShareLeft, fastShareTop, size->width(), size->height()).contains(point)) { result.link = _parent->rightActionLink(); } + if (_data->session().premium()) { + const auto s = st::historyFastShareSize; + const auto r = QRect( + fastShareLeft, + fastShareTop - s - st::msgDateImgDelta, + s, + s); + if (r.contains(point)) { + result.link = _transcribe; + } + } } } return result; diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index e5331b0c4..540144b2c 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -201,6 +201,8 @@ private: mutable bool _thumbCacheBlurred = false; mutable bool _thumbIsEllipse = false; + ClickHandlerPtr _transcribe; + }; } // namespace HistoryView diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 994b1b38b..a2a43c011 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -876,6 +876,7 @@ historyFastShareBottom: 5px; historyFastShareIcon: icon {{ "fast_share", msgServiceFg }}; historyGoToOriginalIcon: icon {{ "fast_to_original", msgServiceFg }}; historyFastCommentsIcon: icon {{ "fast_comments", msgServiceFg }}; +historyFastTranscribeIcon: icon {{ "chat/voice_to_text", msgServiceFg }}; historySavedFont: font(semibold 14px); diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index d165023d2..bf37a73ee 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -85,6 +85,7 @@ ChatStyle::ChatStyle() { make(_msgBotKbWebviewIcon, st::msgBotKbWebviewIcon); make(_historyFastCommentsIcon, st::historyFastCommentsIcon); make(_historyFastShareIcon, st::historyFastShareIcon); + make(_historyFastTranscribeIcon, st::historyFastTranscribeIcon); make(_historyGoToOriginalIcon, st::historyGoToOriginalIcon); make(_historyMapPoint, st::historyMapPoint); make(_historyMapPointInner, st::historyMapPointInner); diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index a95f5d929..415a8b896 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -252,6 +252,9 @@ public: [[nodiscard]] const style::icon &historyFastShareIcon() const { return _historyFastShareIcon; } + [[nodiscard]] const style::icon &historyFastTranscribeIcon() const { + return _historyFastTranscribeIcon; + } [[nodiscard]] const style::icon &historyGoToOriginalIcon() const { return _historyGoToOriginalIcon; } @@ -344,6 +347,7 @@ private: style::icon _msgBotKbWebviewIcon = { Qt::Uninitialized }; style::icon _historyFastCommentsIcon = { Qt::Uninitialized }; style::icon _historyFastShareIcon = { Qt::Uninitialized }; + style::icon _historyFastTranscribeIcon = { Qt::Uninitialized }; style::icon _historyGoToOriginalIcon = { Qt::Uninitialized }; style::icon _historyMapPoint = { Qt::Uninitialized }; style::icon _historyMapPointInner = { Qt::Uninitialized };