From 705753efb26e9f3489da68514bdef4805aaf1cd3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 3 Jun 2022 15:58:02 +0400 Subject: [PATCH] Use small userpic video in dialogs list / chat history. --- .../boxes/peers/prepare_short_info_box.cpp | 4 +- .../SourceFiles/data/data_auto_download.cpp | 2 +- Telegram/SourceFiles/data/data_photo.cpp | 108 +++++++++++++----- Telegram/SourceFiles/data/data_photo.h | 40 ++++--- .../SourceFiles/data/data_photo_media.cpp | 26 +++-- Telegram/SourceFiles/data/data_photo_media.h | 11 +- Telegram/SourceFiles/data/data_session.cpp | 54 ++++++--- Telegram/SourceFiles/data/data_session.h | 6 +- .../dialogs/ui/dialogs_video_userpic.cpp | 14 ++- .../media/view/media_view_overlay_widget.cpp | 15 +-- 10 files changed, 186 insertions(+), 94 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp b/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp index 5ca433f79..17ccc4969 100644 --- a/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp @@ -115,7 +115,9 @@ void Preload( : Data::FileOriginUserPhoto(peerToUser(peer->id), photo->id); state->photoPreloads.push_back(photo->createMediaView()); if (photo->hasVideo()) { - state->photoPreloads.back()->videoWanted(origin); + state->photoPreloads.back()->videoWanted( + Data::PhotoSize::Large, + origin); } else { state->photoPreloads.back()->wanted( Data::PhotoSize::Large, diff --git a/Telegram/SourceFiles/data/data_auto_download.cpp b/Telegram/SourceFiles/data/data_auto_download.cpp index bbbcd84b8..c0ea85055 100644 --- a/Telegram/SourceFiles/data/data_auto_download.cpp +++ b/Telegram/SourceFiles/data/data_auto_download.cpp @@ -317,7 +317,7 @@ bool ShouldAutoPlay( not_null peer, not_null photo) { const auto source = SourceFromPeer(peer); - const auto size = photo->videoByteSize(); + const auto size = photo->videoByteSize(PhotoSize::Large); return photo->hasVideo() && (data.shouldDownload(source, Type::AutoPlayGIF, size) || data.shouldDownload(source, Type::AutoPlayVideo, size) diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 7eec4041a..aa9e148a7 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -51,7 +51,7 @@ PhotoData::~PhotoData() { for (auto &image : _images) { base::take(image.loader).reset(); } - base::take(_video.loader).reset(); + base::take(_videoSizes); } Data::Session &PhotoData::owner() const { @@ -369,7 +369,8 @@ void PhotoData::updateImages( const ImageWithLocation &small, const ImageWithLocation &thumbnail, const ImageWithLocation &large, - const ImageWithLocation &video, + const ImageWithLocation &videoSmall, + const ImageWithLocation &videoLarge, crl::time videoStartTime) { if (!inlineThumbnailBytes.isEmpty() && _inlineThumbnailBytes.isEmpty()) { @@ -399,15 +400,28 @@ void PhotoData::updateImages( update(PhotoSize::Thumbnail, thumbnail); update(PhotoSize::Large, large); - if (video.location.valid()) { - _videoStartTime = videoStartTime; + if (!videoLarge.location.valid()) { + _videoSizes = nullptr; + } else { + if (!_videoSizes) { + _videoSizes = std::make_unique(); + } + _videoSizes->startTime = videoStartTime; + constexpr auto large = PhotoSize::Large; + constexpr auto small = PhotoSize::Small; + Data::UpdateCloudFile( + _videoSizes->large, + videoLarge, + owner().cache(), + Data::kAnimationCacheTag, + [&](Data::FileOrigin origin) { loadVideo(large, origin); }); + Data::UpdateCloudFile( + _videoSizes->small, + videoSmall, + owner().cache(), + Data::kAnimationCacheTag, + [&](Data::FileOrigin origin) { loadVideo(small, origin); }); } - Data::UpdateCloudFile( - _video, - video, - owner().cache(), - Data::kAnimationCacheTag, - [&](Data::FileOrigin origin) { loadVideo(origin); }); } [[nodiscard]] bool PhotoData::hasAttachedStickers() const { @@ -426,34 +440,55 @@ int PhotoData::height() const { return _images[PhotoSizeIndex(PhotoSize::Large)].location.height(); } +Data::CloudFile &PhotoData::videoFile(PhotoSize size) { + Expects(_videoSizes != nullptr); + + return (size == PhotoSize::Small) + ? _videoSizes->small + : _videoSizes->large; +} + +const Data::CloudFile &PhotoData::videoFile(PhotoSize size) const { + Expects(_videoSizes != nullptr); + + return (size == PhotoSize::Small) + ? _videoSizes->small + : _videoSizes->large; +} + + bool PhotoData::hasVideo() const { - return _video.location.valid(); + return _videoSizes != nullptr; } -bool PhotoData::videoLoading() const { - return _video.loader != nullptr; +bool PhotoData::videoLoading(Data::PhotoSize size) const { + return _videoSizes && videoFile(size).loader != nullptr; } -bool PhotoData::videoFailed() const { - return (_video.flags & Data::CloudFile::Flag::Failed); +bool PhotoData::videoFailed(Data::PhotoSize size) const { + return _videoSizes + && (videoFile(size).flags & Data::CloudFile::Flag::Failed); } -void PhotoData::loadVideo(Data::FileOrigin origin) { +void PhotoData::loadVideo(Data::PhotoSize size, Data::FileOrigin origin) { + if (!_videoSizes) { + return; + } const auto autoLoading = false; const auto finalCheck = [=] { if (const auto active = activeMediaView()) { - return active->videoContent().isEmpty(); + return active->videoContent(size).isEmpty(); } return true; }; const auto done = [=](QByteArray result) { if (const auto active = activeMediaView()) { - active->setVideo(std::move(result)); + active->setVideo(size, std::move(result)); } }; Data::LoadCloudFile( &session(), - _video, + videoFile(size), origin, LoadFromCloudOrLocal, autoLoading, @@ -462,12 +497,27 @@ void PhotoData::loadVideo(Data::FileOrigin origin) { done); } -const ImageLocation &PhotoData::videoLocation() const { - return _video.location; +const ImageLocation &PhotoData::videoLocation(Data::PhotoSize size) const { + static const auto empty = ImageLocation(); + return _videoSizes ? videoFile(size).location : empty; } -int PhotoData::videoByteSize() const { - return _video.byteSize; +int PhotoData::videoByteSize(Data::PhotoSize size) const { + return _videoSizes ? videoFile(size).byteSize : 0; +} + +crl::time PhotoData::videoStartPosition() const { + return _videoSizes ? _videoSizes->startTime : crl::time(0); +} + +void PhotoData::setVideoPlaybackFailed() { + if (_videoSizes) { + _videoSizes->playbackFailed = true; + } +} + +bool PhotoData::videoPlaybackFailed() const { + return _videoSizes && _videoSizes->playbackFailed; } bool PhotoData::videoCanBePlayed() const { @@ -481,17 +531,19 @@ auto PhotoData::createStreamingLoader( if (!hasVideo()) { return nullptr; } + constexpr auto large = PhotoSize::Large; if (!forceRemoteLoader) { const auto media = activeMediaView(); - if (media && !media->videoContent().isEmpty()) { - return Media::Streaming::MakeBytesLoader(media->videoContent()); + const auto bytes = media ? media->videoContent(large) : QByteArray(); + if (media && !bytes.isEmpty()) { + return Media::Streaming::MakeBytesLoader(bytes); } } - return v::is(videoLocation().file().data) + return v::is(videoLocation(large).file().data) ? std::make_unique( &session().downloader(), - v::get(videoLocation().file().data), - videoByteSize(), + v::get(videoLocation(large).file().data), + videoByteSize(large), origin) : nullptr; } diff --git a/Telegram/SourceFiles/data/data_photo.h b/Telegram/SourceFiles/data/data_photo.h index 4e6127965..e4138dbe4 100644 --- a/Telegram/SourceFiles/data/data_photo.h +++ b/Telegram/SourceFiles/data/data_photo.h @@ -93,7 +93,8 @@ public: const ImageWithLocation &small, const ImageWithLocation &thumbnail, const ImageWithLocation &large, - const ImageWithLocation &video, + const ImageWithLocation &videoSmall, + const ImageWithLocation &videoLarge, crl::time videoStartTime); [[nodiscard]] int validSizeIndex(Data::PhotoSize size) const; [[nodiscard]] int existingSizeIndex(Data::PhotoSize size) const; @@ -126,20 +127,15 @@ public: [[nodiscard]] int imageByteSize(Data::PhotoSize size) const; [[nodiscard]] bool hasVideo() const; - [[nodiscard]] bool videoLoading() const; - [[nodiscard]] bool videoFailed() const; - void loadVideo(Data::FileOrigin origin); - [[nodiscard]] const ImageLocation &videoLocation() const; - [[nodiscard]] int videoByteSize() const; - [[nodiscard]] crl::time videoStartPosition() const { - return _videoStartTime; - } - void setVideoPlaybackFailed() { - _videoPlaybackFailed = true; - } - [[nodiscard]] bool videoPlaybackFailed() const { - return _videoPlaybackFailed; - } + [[nodiscard]] bool videoLoading(Data::PhotoSize size) const; + [[nodiscard]] bool videoFailed(Data::PhotoSize size) const; + void loadVideo(Data::PhotoSize size, Data::FileOrigin origin); + [[nodiscard]] const ImageLocation &videoLocation( + Data::PhotoSize size) const; + [[nodiscard]] int videoByteSize(Data::PhotoSize size) const; + [[nodiscard]] crl::time videoStartPosition() const; + void setVideoPlaybackFailed(); + [[nodiscard]] bool videoPlaybackFailed() const; [[nodiscard]] bool videoCanBePlayed() const; [[nodiscard]] auto createStreamingLoader( Data::FileOrigin origin, @@ -162,11 +158,19 @@ public: std::unique_ptr uploadingData; private: + [[nodiscard]] Data::CloudFile &videoFile(Data::PhotoSize size); + [[nodiscard]] const Data::CloudFile &videoFile( + Data::PhotoSize size) const; + + struct VideoSizes { + Data::CloudFile small; + Data::CloudFile large; + crl::time startTime = 0; + bool playbackFailed = false; + }; QByteArray _inlineThumbnailBytes; std::array _images; - Data::CloudFile _video; - crl::time _videoStartTime = 0; - bool _videoPlaybackFailed = false; + std::unique_ptr _videoSizes; int32 _dc = 0; uint64 _access = 0; diff --git a/Telegram/SourceFiles/data/data_photo_media.cpp b/Telegram/SourceFiles/data/data_photo_media.cpp index 532ccf8e4..54c8ab73c 100644 --- a/Telegram/SourceFiles/data/data_photo_media.cpp +++ b/Telegram/SourceFiles/data/data_photo_media.cpp @@ -117,23 +117,24 @@ void PhotoMedia::set( _owner->session().notifyDownloaderTaskFinished(); } -QByteArray PhotoMedia::videoContent() const { - return _videoBytes; +QByteArray PhotoMedia::videoContent(PhotoSize size) const { + return (size == PhotoSize::Large) ? _videoBytesLarge : _videoBytesSmall; } -QSize PhotoMedia::videoSize() const { - const auto &location = _owner->videoLocation(); +QSize PhotoMedia::videoSize(PhotoSize size) const { + const auto &location = _owner->videoLocation(size); return { location.width(), location.height() }; } -void PhotoMedia::videoWanted(Data::FileOrigin origin) { - if (_videoBytes.isEmpty()) { - _owner->loadVideo(origin); +void PhotoMedia::videoWanted(PhotoSize size, Data::FileOrigin origin) { + if (videoContent(size).isEmpty()) { + _owner->loadVideo(size, origin); } } -void PhotoMedia::setVideo(QByteArray content) { - _videoBytes = std::move(content); +void PhotoMedia::setVideo(PhotoSize size, QByteArray content) { + ((size == PhotoSize::Large) ? _videoBytesLarge : _videoBytesSmall) + = std::move(content); } bool PhotoMedia::loaded() const { @@ -191,16 +192,17 @@ void PhotoMedia::collectLocalData(not_null local) { } bool PhotoMedia::saveToFile(const QString &path) { - if (const auto video = videoContent(); !video.isEmpty()) { + constexpr auto large = PhotoSize::Large; + if (const auto video = videoContent(large); !video.isEmpty()) { QFile f(path); return f.open(QIODevice::WriteOnly) && (f.write(video) == video.size()); - } else if (const auto photo = imageBytes(Data::PhotoSize::Large) + } else if (const auto photo = imageBytes(large) ; !photo.isEmpty()) { QFile f(path); return f.open(QIODevice::WriteOnly) && (f.write(photo) == photo.size()); - } else if (const auto fallback = image(Data::PhotoSize::Large)->original() + } else if (const auto fallback = image(large)->original() ; !fallback.isNull()) { return fallback.save(path, "JPG"); } diff --git a/Telegram/SourceFiles/data/data_photo_media.h b/Telegram/SourceFiles/data/data_photo_media.h index 99d4d4287..4e97aa911 100644 --- a/Telegram/SourceFiles/data/data_photo_media.h +++ b/Telegram/SourceFiles/data/data_photo_media.h @@ -33,10 +33,10 @@ public: QImage image, QByteArray bytes); - [[nodiscard]] QByteArray videoContent() const; - [[nodiscard]] QSize videoSize() const; - void videoWanted(Data::FileOrigin origin); - void setVideo(QByteArray content); + [[nodiscard]] QByteArray videoContent(PhotoSize size) const; + [[nodiscard]] QSize videoSize(PhotoSize size) const; + void videoWanted(PhotoSize size, Data::FileOrigin origin); + void setVideo(PhotoSize size, QByteArray content); [[nodiscard]] bool loaded() const; [[nodiscard]] float64 progress() const; @@ -64,7 +64,8 @@ private: const not_null _owner; mutable std::unique_ptr _inlineThumbnail; std::array _images; - QByteArray _videoBytes; + QByteArray _videoBytesSmall; + QByteArray _videoBytesLarge; }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 49352699b..8e9ee0fbb 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2478,6 +2478,7 @@ not_null Session::processPhoto( thumbnail, large, ImageWithLocation{}, + ImageWithLocation{}, crl::time(0)); }, [&](const MTPDphotoEmpty &data) { return photo(data.vid().v); @@ -2495,7 +2496,8 @@ not_null Session::photo( const ImageWithLocation &small, const ImageWithLocation &thumbnail, const ImageWithLocation &large, - const ImageWithLocation &video, + const ImageWithLocation &videoSmall, + const ImageWithLocation &videoLarge, crl::time videoStartTime) { const auto result = photo(id); photoApplyFields( @@ -2509,7 +2511,8 @@ not_null Session::photo( small, thumbnail, large, - video, + videoSmall, + videoLarge, videoStartTime); return result; } @@ -2560,6 +2563,7 @@ PhotoData *Session::photoFromWeb( ImageWithLocation{ .location = thumbnailLocation }, ImageWithLocation{ .location = large }, ImageWithLocation{}, + ImageWithLocation{}, crl::time(0)); } @@ -2612,9 +2616,10 @@ void Session::photoApplyFields( ? ImageWithLocation() : Images::FromPhotoSize(_session, data, *i); }; - const auto findVideoSize = [&]() -> std::optional { + const auto findVideoSize = [&](PhotoSize size) + -> std::optional { const auto sizes = data.vvideo_sizes(); - if (!sizes || sizes->v.isEmpty()) { + if (!sizes) { return std::nullopt; } const auto area = [](const MTPVideoSize &size) { @@ -2622,18 +2627,28 @@ void Session::photoApplyFields( return data.vsize().v ? (data.vw().v * data.vh().v) : 0; }); }; - const auto result = *ranges::max_element( - sizes->v, - std::greater<>(), - area); - return (area(result) > 0) ? std::make_optional(result) : std::nullopt; + const auto type = [](const MTPVideoSize &size) { + return size.match([](const MTPDvideoSize &data) { + return data.vtype().v.isEmpty() + ? char(0) + : data.vtype().v.front(); + }); + }; + const auto result = (size == PhotoSize::Small) + ? ranges::find(sizes->v, 'p', type) + : ranges::max_element(sizes->v, std::less<>(), area); + if (result == sizes->v.end() || area(*result) <= 0) { + return std::nullopt; + } + return std::make_optional(*result); }; const auto useProgressive = (progressive != sizes.end()); const auto large = useProgressive ? Images::FromPhotoSize(_session, data, *progressive) : image(LargeLevels); if (large.location.valid()) { - const auto video = findVideoSize(); + const auto videoSmall = findVideoSize(PhotoSize::Small); + const auto videoLarge = findVideoSize(PhotoSize::Large); photoApplyFields( photo, data.vaccess_hash().v, @@ -2649,12 +2664,15 @@ void Session::photoApplyFields( ? Images::FromProgressiveSize(_session, *progressive, 1) : image(ThumbnailLevels)), large, - (video - ? Images::FromVideoSize(_session, data, *video) + (videoSmall + ? Images::FromVideoSize(_session, data, *videoSmall) : ImageWithLocation()), - (video - ? VideoStartTime( - *video->match([](const auto &data) { return &data; })) + (videoLarge + ? Images::FromVideoSize(_session, data, *videoLarge) + : ImageWithLocation()), + (videoLarge + ? VideoStartTime(*videoLarge->match( + [](const auto &data) { return &data; })) : 0)); } } @@ -2670,7 +2688,8 @@ void Session::photoApplyFields( const ImageWithLocation &small, const ImageWithLocation &thumbnail, const ImageWithLocation &large, - const ImageWithLocation &video, + const ImageWithLocation &videoSmall, + const ImageWithLocation &videoLarge, crl::time videoStartTime) { if (!date) { return; @@ -2683,7 +2702,8 @@ void Session::photoApplyFields( small, thumbnail, large, - video, + videoSmall, + videoLarge, videoStartTime); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 04c392c6a..631eec61a 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -482,7 +482,8 @@ public: const ImageWithLocation &small, const ImageWithLocation &thumbnail, const ImageWithLocation &large, - const ImageWithLocation &video, + const ImageWithLocation &videoSmall, + const ImageWithLocation &videoLarge, crl::time videoStartTime); void photoConvert( not_null original, @@ -740,7 +741,8 @@ private: const ImageWithLocation &small, const ImageWithLocation &thumbnail, const ImageWithLocation &large, - const ImageWithLocation &video, + const ImageWithLocation &videoSmall, + const ImageWithLocation &videoLarge, crl::time videoStartTime); void documentApplyFields( diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_video_userpic.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_video_userpic.cpp index dacbf74f5..95307efee 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_video_userpic.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_video_userpic.cpp @@ -47,7 +47,9 @@ void VideoUserpic::paintLeft( _peer->updateFullForced(); } else { _videoPhotoMedia = photo->createMediaView(); - _videoPhotoMedia->videoWanted(_peer->userpicPhotoOrigin()); + _videoPhotoMedia->videoWanted( + Data::PhotoSize::Small, + _peer->userpicPhotoOrigin()); } } if (!_video) { @@ -55,11 +57,17 @@ void VideoUserpic::paintLeft( const auto photo = _peer->owner().photo(photoId); if (!photo->isNull()) { _videoPhotoMedia = photo->createMediaView(); - _videoPhotoMedia->videoWanted(_peer->userpicPhotoOrigin()); + _videoPhotoMedia->videoWanted( + Data::PhotoSize::Small, + _peer->userpicPhotoOrigin()); } } if (_videoPhotoMedia) { - auto bytes = _videoPhotoMedia->videoContent(); + auto small = _videoPhotoMedia->videoContent( + Data::PhotoSize::Small); + auto bytes = small.isEmpty() + ? _videoPhotoMedia->videoContent(Data::PhotoSize::Large) + : small; if (!bytes.isEmpty()) { auto callback = [=](Media::Clip::Notification notification) { clipCallback(notification); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 88981eb8a..fa6daffe8 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -755,7 +755,7 @@ void OverlayWidget::checkForSaveLoaded() { return; } else if (!_photo || !_photo->hasVideo() - || _photoMedia->videoContent().isEmpty()) { + || _photoMedia->videoContent(Data::PhotoSize::Large).isEmpty()) { return; } else if (_savePhotoVideoWhenLoaded == SavePhotoVideo::QuickSave) { _savePhotoVideoWhenLoaded = SavePhotoVideo::None; @@ -1647,7 +1647,8 @@ void OverlayWidget::saveAs() { updateOver(_lastMouseMovePos); } } else if (_photo && _photo->hasVideo()) { - if (const auto bytes = _photoMedia->videoContent(); !bytes.isEmpty()) { + constexpr auto large = Data::PhotoSize::Large; + if (const auto bytes = _photoMedia->videoContent(large); !bytes.isEmpty()) { const auto photo = _photo; auto filter = qsl("Video Files (*.mp4);;") + FileDialog::AllFilesFilter(); FileDialog::GetWritePath( @@ -1669,7 +1670,7 @@ void OverlayWidget::saveAs() { } })); } else { - _photo->loadVideo(fileOrigin()); + _photo->loadVideo(large, fileOrigin()); _savePhotoVideoWhenLoaded = SavePhotoVideo::SaveAs; } } else { @@ -1765,7 +1766,7 @@ void OverlayWidget::downloadMedia() { updateOver(_lastMouseMovePos); } } else if (_photo && _photo->hasVideo()) { - if (!_photoMedia->videoContent().isEmpty()) { + if (!_photoMedia->videoContent(Data::PhotoSize::Large).isEmpty()) { if (!QDir().exists(path)) { QDir().mkpath(path); } @@ -1774,7 +1775,7 @@ void OverlayWidget::downloadMedia() { toName = QString(); } } else { - _photo->loadVideo(fileOrigin()); + _photo->loadVideo(Data::PhotoSize::Large, fileOrigin()); _savePhotoVideoWhenLoaded = SavePhotoVideo::QuickSave; } } else { @@ -2783,8 +2784,8 @@ void OverlayWidget::initStreamingThumbnail() { : _photoMedia->thumbnailInline(); const auto size = _photo ? QSize( - _photo->videoLocation().width(), - _photo->videoLocation().height()) + _photo->videoLocation(Data::PhotoSize::Large).width(), + _photo->videoLocation(Data::PhotoSize::Large).height()) : good ? good->size() : _document->dimensions;