Use video covers in messages.

This commit is contained in:
John Preston 2025-01-28 17:01:18 +04:00
parent 107f329b4f
commit e8034189df
7 changed files with 84 additions and 21 deletions

View file

@ -1334,6 +1334,7 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
message, message,
realParent, realParent,
_document, _document,
_videoCover,
_spoiler); _spoiler);
} }
} else if (_document->isAnimation() || _document->isVideoFile()) { } else if (_document->isAnimation() || _document->isVideoFile()) {
@ -1341,6 +1342,7 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
message, message,
realParent, realParent,
_document, _document,
_videoCover,
_spoiler); _spoiler);
} else if (_document->isTheme() && _document->hasThumbnail()) { } else if (_document->isTheme() && _document->hasThumbnail()) {
return std::make_unique<HistoryView::ThemeDocument>( return std::make_unique<HistoryView::ThemeDocument>(
@ -2609,6 +2611,7 @@ std::unique_ptr<HistoryView::Media> MediaStory::createView(
message, message,
realParent, realParent,
story->document(), story->document(),
nullptr,
spoiler); spoiler);
} }
} }

View file

@ -69,7 +69,10 @@ QSize Game::countOptimalSize() {
// init attach // init attach
if (!_attach) { if (!_attach) {
_attach = CreateAttach(_parent, _data->document, _data->photo); _attach = CreateAttach(
_parent,
_data->document,
_data->document ? nullptr : _data->photo);
} }
// init strings // init strings

View file

@ -48,6 +48,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/effects/path_shift_gradient.h" #include "ui/effects/path_shift_gradient.h"
#include "ui/effects/spoiler_mess.h" #include "ui/effects/spoiler_mess.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "data/data_streaming.h" #include "data/data_streaming.h"
@ -140,9 +142,11 @@ Gif::Gif(
not_null<Element*> parent, not_null<Element*> parent,
not_null<HistoryItem*> realParent, not_null<HistoryItem*> realParent,
not_null<DocumentData*> document, not_null<DocumentData*> document,
PhotoData *videoCover,
bool spoiler) bool spoiler)
: File(parent, realParent) : File(parent, realParent)
, _data(document) , _data(document)
, _videoCover(videoCover)
, _storyId(realParent->media() , _storyId(realParent->media()
? realParent->media()->storyId() ? realParent->media()->storyId()
: FullStoryId()) : FullStoryId())
@ -200,6 +204,12 @@ Gif::Gif(
if ((_dataMedia = _data->activeMediaView())) { if ((_dataMedia = _data->activeMediaView())) {
dataMediaCreated(); dataMediaCreated();
} else if (_videoCover) {
if (_videoCover->inlineThumbnailBytes().isEmpty()
&& (_videoCover->hasExact(Data::PhotoSize::Small)
|| _videoCover->hasExact(Data::PhotoSize::Thumbnail))) {
_videoCover->load(Data::PhotoSize::Small, realParent->fullId());
}
} else { } else {
_data->loadThumbnail(realParent->fullId()); _data->loadThumbnail(realParent->fullId());
if (!autoplayEnabled()) { if (!autoplayEnabled()) {
@ -955,6 +965,8 @@ QImage Gif::spoilerTagBackground() const {
} }
void Gif::validateVideoThumbnail() const { void Gif::validateVideoThumbnail() const {
Expects(!_videoCover);
const auto content = _dataMedia->videoThumbnailContent(); const auto content = _dataMedia->videoThumbnailContent();
if (_videoThumbnailFrame || content.isEmpty()) { if (_videoThumbnailFrame || content.isEmpty()) {
return; return;
@ -970,13 +982,25 @@ void Gif::validateThumbCache(
QSize outer, QSize outer,
bool isEllipse, bool isEllipse,
std::optional<Ui::BubbleRounding> rounding) const { std::optional<Ui::BubbleRounding> rounding) const {
const auto good = _dataMedia->goodThumbnail(); const auto good = _videoCoverMedia
const auto normal = good ? good : _dataMedia->thumbnail(); ? _videoCoverMedia->image(Data::PhotoSize::Large)
: _dataMedia->goodThumbnail();
const auto normal = good
? good
: _videoCoverMedia
? nullptr
: _dataMedia->thumbnail();
if (!normal) { if (!normal) {
if (_videoCoverMedia) {
_videoCover->load(Data::PhotoSize::Small, _realParent->fullId());
} else {
_data->loadThumbnail(_realParent->fullId()); _data->loadThumbnail(_realParent->fullId());
validateVideoThumbnail(); validateVideoThumbnail();
} }
const auto videothumb = normal ? nullptr : _videoThumbnailFrame.get(); }
const auto videothumb = (normal || _videoCoverMedia)
? nullptr
: _videoThumbnailFrame.get();
const auto blurred = normal const auto blurred = normal
? (!good ? (!good
&& (normal->width() < kUseNonBlurredThreshold) && (normal->width() < kUseNonBlurredThreshold)
@ -998,9 +1022,17 @@ void Gif::validateThumbCache(
} }
QImage Gif::prepareThumbCache(QSize outer) const { QImage Gif::prepareThumbCache(QSize outer) const {
const auto good = _dataMedia->goodThumbnail(); const auto good = _videoCoverMedia
const auto normal = good ? good : _dataMedia->thumbnail(); ? _videoCoverMedia->image(Data::PhotoSize::Large)
const auto videothumb = normal ? nullptr : _videoThumbnailFrame.get(); : _dataMedia->goodThumbnail();
const auto normal = good
? good
: _videoCoverMedia
? nullptr
: _dataMedia->thumbnail();
const auto videothumb = (normal || _videoCoverMedia)
? nullptr
: _videoThumbnailFrame.get();
auto blurred = (!good auto blurred = (!good
&& normal && normal
&& (normal->width() < kUseNonBlurredThreshold) && (normal->width() < kUseNonBlurredThreshold)
@ -1010,6 +1042,10 @@ QImage Gif::prepareThumbCache(QSize outer) const {
const auto blurFromLarge = good || (normal && !blurred); const auto blurFromLarge = good || (normal && !blurred);
const auto large = blurFromLarge ? normal : videothumb; const auto large = blurFromLarge ? normal : videothumb;
if (videothumb) { if (videothumb) {
} else if (_videoCoverMedia) {
if (const auto embedded = _videoCoverMedia->thumbnailInline()) {
blurred = embedded;
}
} else if (const auto embedded = _dataMedia->thumbnailInline()) { } else if (const auto embedded = _dataMedia->thumbnailInline()) {
blurred = embedded; blurred = embedded;
} }
@ -1035,7 +1071,9 @@ void Gif::validateSpoilerImageCache(
&& _spoiler->backgroundRounding == rounding) { && _spoiler->backgroundRounding == rounding) {
return; return;
} }
const auto normal = _dataMedia->thumbnail(); const auto normal = _videoCoverMedia
? _videoCoverMedia->image(Data::PhotoSize::Small)
: _dataMedia->thumbnail();
auto container = std::optional<Image>(); auto container = std::optional<Image>();
const auto downscale = [&](Image *image) { const auto downscale = [&](Image *image) {
if (!image || (image->width() <= 40 && image->height() <= 40)) { if (!image || (image->width() <= 40 && image->height() <= 40)) {
@ -1047,7 +1085,9 @@ void Gif::validateSpoilerImageCache(
Qt::SmoothTransformation)); Qt::SmoothTransformation));
return &*container; return &*container;
}; };
const auto embedded = _dataMedia->thumbnailInline(); const auto embedded = _videoCoverMedia
? _videoCoverMedia->thumbnailInline()
: _dataMedia->thumbnailInline();
const auto blurred = embedded ? embedded : downscale(normal); const auto blurred = embedded ? embedded : downscale(normal);
_spoiler->background = Images::Round( _spoiler->background = Images::Round(
PrepareWithBlurredBackground( PrepareWithBlurredBackground(
@ -1584,21 +1624,30 @@ TextState Gif::getStateGrouped(
} }
void Gif::ensureDataMediaCreated() const { void Gif::ensureDataMediaCreated() const {
if (_dataMedia) { if (_dataMedia && (!_videoCover || _videoCoverMedia)) {
return; return;
} }
_dataMedia = _data->createMediaView(); _dataMedia = _data->createMediaView();
_videoCoverMedia = _videoCover
? _videoCover->createMediaView()
: nullptr;
dataMediaCreated(); dataMediaCreated();
} }
void Gif::dataMediaCreated() const { void Gif::dataMediaCreated() const {
Expects(_dataMedia != nullptr); Expects(_dataMedia != nullptr);
if (_videoCoverMedia) {
_videoCoverMedia->wanted(
Data::PhotoSize::Large,
_realParent->fullId());
} else {
_dataMedia->goodThumbnailWanted(); _dataMedia->goodThumbnailWanted();
_dataMedia->thumbnailWanted(_realParent->fullId()); _dataMedia->thumbnailWanted(_realParent->fullId());
if (!autoplayEnabled()) { if (!autoplayEnabled()) {
_dataMedia->videoThumbnailWanted(_realParent->fullId()); _dataMedia->videoThumbnailWanted(_realParent->fullId());
} }
}
history()->owner().registerHeavyViewPart(_parent); history()->owner().registerHeavyViewPart(_parent);
togglePollingStory(true); togglePollingStory(true);
} }

View file

@ -15,9 +15,11 @@ struct HistoryMessageVia;
struct HistoryMessageReply; struct HistoryMessageReply;
struct HistoryMessageForwarded; struct HistoryMessageForwarded;
class Painter; class Painter;
class PhotoData;
namespace Data { namespace Data {
class DocumentMedia; class DocumentMedia;
class PhotoMedia;
} // namespace Data } // namespace Data
namespace Media { namespace Media {
@ -37,6 +39,7 @@ enum class Error;
namespace HistoryView { namespace HistoryView {
class Photo;
class Reply; class Reply;
class TranscribeButton; class TranscribeButton;
@ -51,6 +54,7 @@ public:
not_null<Element*> parent, not_null<Element*> parent,
not_null<HistoryItem*> realParent, not_null<HistoryItem*> realParent,
not_null<DocumentData*> document, not_null<DocumentData*> document,
PhotoData *videoCover,
bool spoiler); bool spoiler);
~Gif(); ~Gif();
@ -212,12 +216,14 @@ private:
TtlRoundPaintCallback _drawTtl; TtlRoundPaintCallback _drawTtl;
const not_null<DocumentData*> _data; const not_null<DocumentData*> _data;
PhotoData *_videoCover = nullptr;
const FullStoryId _storyId; const FullStoryId _storyId;
std::unique_ptr<Streamed> _streamed; std::unique_ptr<Streamed> _streamed;
const std::unique_ptr<MediaSpoiler> _spoiler; const std::unique_ptr<MediaSpoiler> _spoiler;
mutable std::unique_ptr<MediaSpoilerTag> _spoilerTag; mutable std::unique_ptr<MediaSpoilerTag> _spoilerTag;
mutable std::unique_ptr<TranscribeButton> _transcribe; mutable std::unique_ptr<TranscribeButton> _transcribe;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
mutable std::shared_ptr<Data::PhotoMedia> _videoCoverMedia;
mutable std::unique_ptr<Image> _videoThumbnailFrame; mutable std::unique_ptr<Image> _videoThumbnailFrame;
QString _downloadSize; QString _downloadSize;
mutable QImage _thumbCache; mutable QImage _thumbCache;

View file

@ -101,6 +101,7 @@ std::unique_ptr<Media> CreateAttach(
parent, parent,
parent->data(), parent->data(),
document, document,
photo,
spoiler); spoiler);
} else if (document->isWallPaper() || document->isTheme()) { } else if (document->isWallPaper() || document->isTheme()) {
return std::make_unique<ThemeDocument>( return std::make_unique<ThemeDocument>(

View file

@ -114,7 +114,6 @@ private:
void ensureDataMediaCreated() const; void ensureDataMediaCreated() const;
void dataMediaCreated() const; void dataMediaCreated() const;
void setupSpoilerTag() const;
QSize countOptimalSize() override; QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;

View file

@ -309,13 +309,13 @@ void WebPage::setupAdditionalData() {
UrlClickHandler::Open(link); UrlClickHandler::Open(link);
}); });
if (!_attach) { if (!_attach) {
const auto maybePhoto = details.mediaPhotoId
? session->data().photo(details.mediaPhotoId).get()
: nullptr;
const auto maybeDocument = details.mediaDocumentId const auto maybeDocument = details.mediaDocumentId
? session->data().document( ? session->data().document(
details.mediaDocumentId).get() details.mediaDocumentId).get()
: nullptr; : nullptr;
const auto maybePhoto = (!maybeDocument && details.mediaPhotoId)
? session->data().photo(details.mediaPhotoId).get()
: nullptr;
_attach = CreateAttach( _attach = CreateAttach(
_parent, _parent,
maybeDocument, maybeDocument,
@ -520,7 +520,9 @@ QSize WebPage::countOptimalSize() {
_attach = CreateAttach( _attach = CreateAttach(
_parent, _parent,
_data->document, _data->document,
_data->photo, ((!_data->document || _data->photoIsVideoCover)
? _data->photo
: nullptr),
_collage, _collage,
_data->url); _data->url);
} }