Fix caching of sent photos and document previews.

This commit is contained in:
John Preston 2020-05-26 15:59:45 +04:00
parent 7ad660a0e7
commit 64cf0e1a44
10 changed files with 193 additions and 235 deletions

View File

@ -355,6 +355,8 @@ PRIVATE
data/data_channel.h
data/data_channel_admins.cpp
data/data_channel_admins.h
data/data_cloud_file.cpp
data/data_cloud_file.h
data/data_cloud_themes.cpp
data/data_cloud_themes.h
data/data_countries.cpp

View File

@ -0,0 +1,61 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_cloud_file.h"
#include "data/data_file_origin.h"
#include "storage/cache/storage_cache_database.h"
#include "storage/file_download.h"
namespace Data {
void UpdateCloudFile(
CloudFile &file,
const ImageWithLocation &data,
Storage::Cache::Database &cache,
Fn<void(FileOrigin)> restartLoader,
Fn<void(QImage)> usePreloaded) {
if (!data.location.valid()) {
return;
}
const auto update = !file.location.valid()
|| (data.location.file().cacheKey()
&& (!file.location.file().cacheKey()
|| (file.location.width() < data.location.width())
|| (file.location.height() < data.location.height())));
if (!update) {
return;
}
const auto cacheBytes = !data.bytes.isEmpty()
? data.bytes
: file.location.file().data.is<InMemoryLocation>()
? file.location.file().data.get_unchecked<InMemoryLocation>().bytes
: QByteArray();
if (!cacheBytes.isEmpty()) {
if (const auto cacheKey = data.location.file().cacheKey()) {
cache.putIfEmpty(
cacheKey,
Storage::Cache::Database::TaggedValue(
base::duplicate(data.bytes),
Data::kImageCacheTag));
}
}
file.location = data.location;
file.byteSize = data.bytesCount;
if (!data.preloaded.isNull()) {
file.loader = nullptr;
if (usePreloaded) {
usePreloaded(data.preloaded);
}
} else if (file.loader) {
const auto origin = base::take(file.loader)->fileOrigin();
restartLoader(origin);
}
}
} // namespace Data

View File

@ -0,0 +1,45 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/flags.h"
#include "ui/image/image_location.h"
class FileLoader;
namespace Storage {
namespace Cache {
class Database;
} // namespace Cache
} // namespace Storage
namespace Data {
struct FileOrigin;
struct CloudFile final {
enum class Flag : uchar {
Cancelled = 0x01,
Failed = 0x02,
};
friend inline constexpr bool is_flag_type(Flag) { return true; };
ImageLocation location;
std::unique_ptr<FileLoader> loader;
int byteSize = 0;
base::flags<Flag> flags;
};
void UpdateCloudFile(
CloudFile &file,
const ImageWithLocation &data,
Storage::Cache::Database &cache,
Fn<void(FileOrigin)> restartLoader,
Fn<void(QImage)> usePreloaded = nullptr);
} // namespace Data

View File

@ -452,8 +452,8 @@ DocumentData::DocumentData(not_null<Data::Session*> owner, DocumentId id)
}
DocumentData::~DocumentData() {
base::take(_thumbnailLoader).reset();
base::take(_videoThumbnailLoader).reset();
base::take(_thumbnail.loader).reset();
base::take(_videoThumbnail.loader).reset();
destroyLoader();
unload();
}
@ -613,41 +613,21 @@ void DocumentData::updateThumbnails(
&& _inlineThumbnailBytes.isEmpty()) {
_inlineThumbnailBytes = inlineThumbnailBytes;
}
if (thumbnail.location.valid()
&& (!_thumbnailLocation.valid()
|| _thumbnailLocation.width() < thumbnail.location.width()
|| _thumbnailLocation.height() < thumbnail.location.height())) {
_thumbnailLocation = thumbnail.location;
_thumbnailByteSize = thumbnail.bytesCount;
if (!thumbnail.preloaded.isNull()) {
_thumbnailLoader = nullptr;
Data::UpdateCloudFile(
_thumbnail,
thumbnail,
owner().cache(),
[&](Data::FileOrigin origin) { loadThumbnail(origin); },
[&](QImage preloaded) {
if (const auto media = activeMediaView()) {
media->setThumbnail(thumbnail.preloaded);
media->setThumbnail(std::move(preloaded));
}
} else if (_thumbnailLoader) {
const auto origin = base::take(_thumbnailLoader)->fileOrigin();
loadThumbnail(origin);
}
if (!thumbnail.bytes.isEmpty()) {
if (const auto cacheKey = _thumbnailLocation.file().cacheKey()) {
owner().cache().putIfEmpty(
cacheKey,
Storage::Cache::Database::TaggedValue(
base::duplicate(thumbnail.bytes),
Data::kImageCacheTag));
}
}
}
if (videoThumbnail.location.valid()
&& !_videoThumbnailLocation.valid()) {
_videoThumbnailLocation = videoThumbnail.location;
_videoThumbnailByteSize = videoThumbnail.bytesCount;
if (_videoThumbnailLoader) {
const auto origin
= base::take(_videoThumbnailLoader)->fileOrigin();
loadVideoThumbnail(origin);
}
}
});
Data::UpdateCloudFile(
_videoThumbnail,
videoThumbnail,
owner().cache(),
[&](Data::FileOrigin origin) { loadVideoThumbnail(origin); });
}
bool DocumentData::isWallPaper() const {
@ -659,13 +639,11 @@ bool DocumentData::isPatternWallPaper() const {
}
bool DocumentData::hasThumbnail() const {
return _thumbnailLocation.valid()
&& (_thumbnailLocation.width() > 0)
&& (_thumbnailLocation.height() > 0);
return _thumbnail.location.valid();
}
bool DocumentData::thumbnailLoading() const {
return _thumbnailLoader != nullptr;
return _thumbnail.loader != nullptr;
}
bool DocumentData::thumbnailFailed() const {
@ -673,9 +651,9 @@ bool DocumentData::thumbnailFailed() const {
}
void DocumentData::loadThumbnail(Data::FileOrigin origin) {
if (_thumbnailLoader
if (_thumbnail.loader
|| (_flags & Flag::ThumbnailFailed)
|| !_thumbnailLocation.valid()) {
|| !_thumbnail.location.valid()) {
return;
} else if (const auto active = activeMediaView()) {
if (active->thumbnail()) {
@ -683,49 +661,49 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) {
}
}
const auto autoLoading = false;
_thumbnailLoader = CreateFileLoader(
_thumbnailLocation.file(),
_thumbnail.loader = CreateFileLoader(
_thumbnail.location.file(),
origin,
QString(),
_thumbnailByteSize,
_thumbnail.byteSize,
UnknownFileLocation,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
autoLoading,
Data::kImageCacheTag);
_thumbnailLoader->updates(
_thumbnail.loader->updates(
) | rpl::start_with_error_done([=](bool started) {
_thumbnailLoader = nullptr;
_thumbnail.loader = nullptr;
_flags |= Flag::ThumbnailFailed;
}, [=] {
if (_thumbnailLoader && !_thumbnailLoader->cancelled()) {
if (auto read = _thumbnailLoader->imageData(); read.isNull()) {
if (_thumbnail.loader && !_thumbnail.loader->cancelled()) {
if (auto read = _thumbnail.loader->imageData(); read.isNull()) {
_flags |= Flag::ThumbnailFailed;
} else if (const auto active = activeMediaView()) {
active->setThumbnail(std::move(read));
}
}
_thumbnailLoader = nullptr;
}, _thumbnailLoader->lifetime());
_thumbnail.loader = nullptr;
}, _thumbnail.loader->lifetime());
_thumbnailLoader->start();
_thumbnail.loader->start();
}
const ImageLocation &DocumentData::thumbnailLocation() const {
return _thumbnailLocation;
return _thumbnail.location;
}
int DocumentData::thumbnailByteSize() const {
return _thumbnailByteSize;
return _thumbnail.byteSize;
}
bool DocumentData::hasVideoThumbnail() const {
return _videoThumbnailLocation.valid();
return _videoThumbnail.location.valid();
}
bool DocumentData::videoThumbnailLoading() const {
return _videoThumbnailLoader != nullptr;
return _videoThumbnail.loader != nullptr;
}
bool DocumentData::videoThumbnailFailed() const {
@ -733,9 +711,9 @@ bool DocumentData::videoThumbnailFailed() const {
}
void DocumentData::loadVideoThumbnail(Data::FileOrigin origin) {
if (_videoThumbnailLoader
if (_videoThumbnail.loader
|| (_flags & Flag::VideoThumbnailFailed)
|| !_videoThumbnailLocation.valid()) {
|| !_videoThumbnail.location.valid()) {
return;
} else if (const auto active = activeMediaView()) {
if (!active->videoThumbnailContent().isEmpty()) {
@ -743,42 +721,42 @@ void DocumentData::loadVideoThumbnail(Data::FileOrigin origin) {
}
}
const auto autoLoading = false;
_videoThumbnailLoader = CreateFileLoader(
_videoThumbnailLocation.file(),
_videoThumbnail.loader = CreateFileLoader(
_videoThumbnail.location.file(),
origin,
QString(),
_videoThumbnailByteSize,
_videoThumbnail.byteSize,
UnknownFileLocation,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
autoLoading,
Data::kAnimationCacheTag);
_videoThumbnailLoader->updates(
_videoThumbnail.loader->updates(
) | rpl::start_with_error_done([=](bool started) {
_videoThumbnailLoader = nullptr;
_videoThumbnail.loader = nullptr;
_flags |= Flag::VideoThumbnailFailed;
}, [=] {
if (_videoThumbnailLoader && !_videoThumbnailLoader->cancelled()) {
auto bytes = _videoThumbnailLoader->bytes();
if (_videoThumbnail.loader && !_videoThumbnail.loader->cancelled()) {
auto bytes = _videoThumbnail.loader->bytes();
if (bytes.isEmpty()) {
_flags |= Flag::VideoThumbnailFailed;
} else if (const auto active = activeMediaView()) {
active->setVideoThumbnail(std::move(bytes));
}
}
_videoThumbnailLoader = nullptr;
}, _videoThumbnailLoader->lifetime());
_videoThumbnail.loader = nullptr;
}, _videoThumbnail.loader->lifetime());
_videoThumbnailLoader->start();
_videoThumbnail.loader->start();
}
const ImageLocation &DocumentData::videoThumbnailLocation() const {
return _videoThumbnailLocation;
return _videoThumbnail.location;
}
int DocumentData::videoThumbnailByteSize() const {
return _videoThumbnailByteSize;
return _videoThumbnail.byteSize;
}
Storage::Cache::Key DocumentData::goodThumbnailCacheKey() const {
@ -1407,7 +1385,8 @@ QByteArray DocumentData::fileReference() const {
void DocumentData::refreshFileReference(const QByteArray &value) {
_fileReference = value;
_thumbnailLocation.refreshFileReference(value);
_thumbnail.location.refreshFileReference(value);
_videoThumbnail.location.refreshFileReference(value);
}
QString DocumentData::filename() const {

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/flags.h"
#include "base/binary_guard.h"
#include "data/data_types.h"
#include "data/data_cloud_file.h"
#include "ui/image/image.h"
class mtpFileLoader;
@ -310,12 +311,8 @@ private:
WebFileLocation _urlLocation;
QByteArray _inlineThumbnailBytes;
ImageLocation _thumbnailLocation;
ImageLocation _videoThumbnailLocation;
std::unique_ptr<FileLoader> _thumbnailLoader;
std::unique_ptr<FileLoader> _videoThumbnailLoader;
int _thumbnailByteSize = 0;
int _videoThumbnailByteSize = 0;
Data::CloudFile _thumbnail;
Data::CloudFile _videoThumbnail;
std::unique_ptr<Data::ReplyPreview> _replyPreview;
std::weak_ptr<Data::DocumentMedia> _media;
PhotoData *_goodThumbnailPhoto = nullptr;

View File

@ -382,99 +382,6 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
return false;
}
parent()->history()->owner().photoConvert(_photo, *content);
//if (content->type() != mtpc_photo) { // #TODO optimize
// return false;
//}
//const auto &photo = content->c_photo();
//struct SizeData {
// MTPstring type = MTP_string();
// int width = 0;
// int height = 0;
// QByteArray bytes;
//};
//const auto saveImageToCache = [&](
// not_null<Image*> image,
// SizeData size) {
// Expects(!size.type.v.isEmpty());
// const auto key = StorageImageLocation(
// StorageFileLocation(
// photo.vdc_id().v,
// _photo->session().userId(),
// MTP_inputPhotoFileLocation(
// photo.vid(),
// photo.vaccess_hash(),
// photo.vfile_reference(),
// size.type)),
// size.width,
// size.height);
// if (!key.valid() || image->isNull() || !image->loaded()) {
// return;
// }
// if (size.bytes.isEmpty()) {
// size.bytes = image->bytesForCache();
// }
// const auto length = size.bytes.size();
// if (!length || length > Storage::kMaxFileInMemory) {
// LOG(("App Error: Bad photo data for saving to cache."));
// return;
// }
// parent()->history()->owner().cache().putIfEmpty(
// key.file().cacheKey(),
// Storage::Cache::Database::TaggedValue(
// std::move(size.bytes),
// Data::kImageCacheTag));
// image->replaceSource(
// std::make_unique<Images::StorageSource>(key, length));
//};
//auto &sizes = photo.vsizes().v;
//auto max = 0;
//auto maxSize = SizeData();
//for (const auto &data : sizes) {
// const auto size = data.match([](const MTPDphotoSize &data) {
// return SizeData{
// data.vtype(),
// data.vw().v,
// data.vh().v,
// QByteArray()
// };
// }, [](const MTPDphotoCachedSize &data) {
// return SizeData{
// data.vtype(),
// data.vw().v,
// data.vh().v,
// qba(data.vbytes())
// };
// }, [](const MTPDphotoSizeEmpty &) {
// return SizeData();
// }, [](const MTPDphotoStrippedSize &data) {
// // No need to save stripped images to local cache.
// return SizeData();
// });
// const auto letter = size.type.v.isEmpty() ? char(0) : size.type.v[0];
// if (!letter) {
// continue;
// }
// if (letter == 's') {
// saveImageToCache(_photo->thumbnailSmall(), size);
// } else if (letter == 'm') {
// saveImageToCache(_photo->thumbnail(), size);
// } else if (letter == 'x' && max < 1) {
// max = 1;
// maxSize = size;
// } else if (letter == 'y' && max < 2) {
// max = 2;
// maxSize = size;
// //} else if (letter == 'w' && max < 3) {
// // max = 3;
// // maxSize = size;
// }
//}
//if (!maxSize.type.v.isEmpty()) {
// saveImageToCache(_photo->large(), maxSize);
//}
return true;
}

View File

@ -52,11 +52,11 @@ Main::Session &PhotoData::session() const {
void PhotoData::automaticLoadSettingsChanged() {
const auto index = PhotoSizeIndex(PhotoSize::Large);
if (!(_images[index].flags & ImageFlag::Cancelled)) {
if (!(_images[index].flags & Data::CloudFile::Flag::Cancelled)) {
return;
}
_images[index].loader = nullptr;
_images[index].flags &= ~ImageFlag::Cancelled;
_images[index].flags &= ~Data::CloudFile::Flag::Cancelled;
}
void PhotoData::load(
@ -92,7 +92,8 @@ bool PhotoData::loading(PhotoSize size) const {
}
bool PhotoData::failed(PhotoSize size) const {
return (_images[validSizeIndex(size)].flags & ImageFlag::Failed);
const auto flags = _images[validSizeIndex(size)].flags;
return (flags & Data::CloudFile::Flag::Failed);
}
const ImageLocation &PhotoData::location(PhotoSize size) const {
@ -134,7 +135,7 @@ void PhotoData::cancel() {
}
const auto index = PhotoSizeIndex(PhotoSize::Large);
_images[index].flags |= ImageFlag::Cancelled;
_images[index].flags |= Data::CloudFile::Flag::Cancelled;
destroyLoader(PhotoSize::Large);
_owner->photoLoadDone(this);
}
@ -154,7 +155,7 @@ float64 PhotoData::progress() const {
bool PhotoData::cancelled() const {
const auto index = PhotoSizeIndex(PhotoSize::Large);
return (_images[index].flags & ImageFlag::Cancelled);
return (_images[index].flags & Data::CloudFile::Flag::Cancelled);
}
void PhotoData::setWaitingForAlbum() {
@ -255,7 +256,7 @@ void PhotoData::load(
image.loader->permitLoadFromCloud();
}
return;
} else if ((image.flags & ImageFlag::Failed)
} else if ((image.flags & Data::CloudFile::Flag::Failed)
|| !image.location.valid()) {
return;
} else if (const auto active = activeMediaView()) {
@ -266,7 +267,7 @@ void PhotoData::load(
// Could've changed, if the requested size didn't have a location.
size = static_cast<PhotoSize>(index);
image.flags &= ~ImageFlag::Cancelled;
image.flags &= ~Data::CloudFile::Flag::Cancelled;
image.loader = CreateFileLoader(
image.location.file(),
origin,
@ -285,7 +286,7 @@ void PhotoData::load(
}
}, [=, &image](bool started) {
finishLoad(size);
image.flags |= ImageFlag::Failed;
image.flags |= Data::CloudFile::Flag::Failed;
if (size == PhotoSize::Large) {
_owner->photoLoadFail(this, started);
}
@ -312,10 +313,10 @@ void PhotoData::finishLoad(PhotoSize size) {
destroyLoader(size);
});
if (!image.loader || image.loader->cancelled()) {
image.flags |= ImageFlag::Cancelled;
image.flags |= Data::CloudFile::Flag::Cancelled;
return;
} else if (auto read = image.loader->imageData(); read.isNull()) {
image.flags |= ImageFlag::Failed;
image.flags |= Data::CloudFile::Flag::Failed;
} else if (const auto active = activeMediaView()) {
active->set(size, std::move(read));
}
@ -330,7 +331,7 @@ void PhotoData::destroyLoader(PhotoSize size) {
return;
}
const auto loader = base::take(image.loader);
if (image.flags & ImageFlag::Cancelled) {
if (image.flags & Data::CloudFile::Flag::Cancelled) {
loader->cancel();
}
}
@ -358,37 +359,16 @@ void PhotoData::updateImages(
_inlineThumbnailBytes = inlineThumbnailBytes;
}
const auto update = [&](PhotoSize size, const ImageWithLocation &data) {
auto &image = _images[PhotoSizeIndex(size)];
if (!data.location.valid()) {
return;
}
const auto changed = !image.location.valid()
|| (image.location.width() != data.location.width())
|| (image.location.height() != data.location.height());
if (changed || (data.bytesCount && !image.byteSize)) {
image.byteSize = data.bytesCount;
}
if (changed) {
image.location = data.location;
}
if (!data.preloaded.isNull()) {
image.loader = nullptr;
if (const auto media = activeMediaView()) {
media->set(size, data.preloaded);
}
} else if (changed && image.loader) {
const auto origin = base::take(image.loader)->fileOrigin();
load(size, origin);
}
if (!data.bytes.isEmpty()) {
if (const auto cacheKey = image.location.file().cacheKey()) {
owner().cache().putIfEmpty(
cacheKey,
Storage::Cache::Database::TaggedValue(
base::duplicate(data.bytes),
Data::kImageCacheTag));
}
}
Data::UpdateCloudFile(
_images[PhotoSizeIndex(size)],
data,
owner().cache(),
[=](Data::FileOrigin origin) { load(size, origin); },
[=](QImage preloaded) {
if (const auto media = activeMediaView()) {
media->set(size, data.preloaded);
}
});
};
update(PhotoSize::Small, small);
update(PhotoSize::Thumbnail, thumbnail);

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "data/data_types.h"
#include "data/data_cloud_file.h"
namespace Main {
class Session;
@ -123,24 +124,11 @@ public:
std::unique_ptr<Data::UploadState> uploadingData;
private:
enum class ImageFlag : uchar {
Cancelled = 0x01,
Failed = 0x02,
};
friend inline constexpr bool is_flag_type(ImageFlag) { return true; };
struct Image final {
ImageLocation location;
std::unique_ptr<FileLoader> loader;
int byteSize = 0;
base::flags<ImageFlag> flags;
};
void finishLoad(Data::PhotoSize size);
void destroyLoader(Data::PhotoSize size);
QByteArray _inlineThumbnailBytes;
std::array<Image, Data::kPhotoSizeCount> _images;
std::array<Data::CloudFile, Data::kPhotoSizeCount> _images;
int32 _dc = 0;
uint64 _access = 0;

View File

@ -2255,7 +2255,8 @@ void Session::photoConvert(
const auto id = data.match([](const auto &data) {
return data.vid().v;
});
if (original->id != id) {
const auto idChanged = (original->id != id);
if (idChanged) {
auto i = _photos.find(id);
if (i == _photos.end()) {
const auto j = _photos.find(original->id);
@ -2446,18 +2447,12 @@ not_null<DocumentData*> Session::document(
void Session::documentConvert(
not_null<DocumentData*> original,
const MTPDocument &data) {
const auto id = [&] {
switch (data.type()) {
case mtpc_document: return data.c_document().vid().v;
case mtpc_documentEmpty: return data.c_documentEmpty().vid().v;
}
Unexpected("Type in Session::documentConvert().");
}();
const auto oldKey = original->mediaKey();
const auto id = data.match([](const auto &data) {
return data.vid().v;
});
const auto oldCacheKey = original->cacheKey();
const auto oldGoodKey = original->goodThumbnailCacheKey();
const auto idChanged = (original->id != id);
const auto sentSticker = idChanged && (original->sticker() != nullptr);
if (idChanged) {
auto i = _documents.find(id);
if (i == _documents.end()) {

View File

@ -18,6 +18,10 @@ ImageWithLocation FromPhotoSize(
not_null<Main::Session*> session,
const MTPDphoto &photo,
const MTPPhotoSize &size) {
if (!photo.vaccess_hash().v && photo.vfile_reference().v.isEmpty()) {
// Locally created fake photo.
return ImageWithLocation();
}
return size.match([&](const MTPDphotoSize &data) {
return ImageWithLocation{
.location = ImageLocation(