Use small userpic video in dialogs list / chat history.

This commit is contained in:
John Preston 2022-06-03 15:58:02 +04:00
parent 499923b6d3
commit 705753efb2
10 changed files with 186 additions and 94 deletions

View file

@ -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,

View file

@ -317,7 +317,7 @@ bool ShouldAutoPlay(
not_null<PeerData*> peer,
not_null<PhotoData*> 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)

View file

@ -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>();
}
_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<StorageFileLocation>(videoLocation().file().data)
return v::is<StorageFileLocation>(videoLocation(large).file().data)
? std::make_unique<Media::Streaming::LoaderMtproto>(
&session().downloader(),
v::get<StorageFileLocation>(videoLocation().file().data),
videoByteSize(),
v::get<StorageFileLocation>(videoLocation(large).file().data),
videoByteSize(large),
origin)
: nullptr;
}

View file

@ -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<Data::UploadState> 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<Data::CloudFile, Data::kPhotoSizeCount> _images;
Data::CloudFile _video;
crl::time _videoStartTime = 0;
bool _videoPlaybackFailed = false;
std::unique_ptr<VideoSizes> _videoSizes;
int32 _dc = 0;
uint64 _access = 0;

View file

@ -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<PhotoMedia*> 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");
}

View file

@ -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<PhotoData*> _owner;
mutable std::unique_ptr<Image> _inlineThumbnail;
std::array<PhotoImage, kPhotoSizeCount> _images;
QByteArray _videoBytes;
QByteArray _videoBytesSmall;
QByteArray _videoBytesLarge;
};

View file

@ -2478,6 +2478,7 @@ not_null<PhotoData*> Session::processPhoto(
thumbnail,
large,
ImageWithLocation{},
ImageWithLocation{},
crl::time(0));
}, [&](const MTPDphotoEmpty &data) {
return photo(data.vid().v);
@ -2495,7 +2496,8 @@ not_null<PhotoData*> 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<PhotoData*> 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<MTPVideoSize> {
const auto findVideoSize = [&](PhotoSize size)
-> std::optional<MTPVideoSize> {
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);
}

View file

@ -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<PhotoData*> 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(

View file

@ -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);

View file

@ -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;