| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- /*
- 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 "data/data_session.h"
- #include "storage/cache/storage_cache_database.h"
- #include "storage/file_download.h"
- #include "ui/image/image.h"
- #include "main/main_session.h"
- namespace Data {
- CloudFile::~CloudFile() {
- // Destroy loader with still alive CloudFile with already zero '.loader'.
- // Otherwise in ~FileLoader it tries to clear file.loader and crashes.
- base::take(loader);
- }
- void CloudFile::clear() {
- location = {};
- base::take(loader);
- byteSize = 0;
- progressivePartSize = 0;
- flags = {};
- }
- CloudImage::CloudImage() = default;
- CloudImage::CloudImage(
- not_null<Main::Session*> session,
- const ImageWithLocation &data) {
- update(session, data);
- }
- void CloudImage::set(
- not_null<Main::Session*> session,
- const ImageWithLocation &data) {
- const auto &was = _file.location.file().data;
- const auto &now = data.location.file().data;
- if (!data.location.valid()) {
- _file.flags |= CloudFile::Flag::Cancelled;
- _file.loader = nullptr;
- _file.location = ImageLocation();
- _file.byteSize = 0;
- _file.flags = CloudFile::Flag();
- _view = std::weak_ptr<QImage>();
- } else if (was != now
- && (!v::is<InMemoryLocation>(was) || v::is<InMemoryLocation>(now))) {
- _file.location = ImageLocation();
- _view = std::weak_ptr<QImage>();
- }
- UpdateCloudFile(
- _file,
- data,
- session->data().cache(),
- kImageCacheTag,
- [=](FileOrigin origin) { load(session, origin); },
- [=](QImage preloaded, QByteArray) {
- setToActive(session, std::move(preloaded));
- });
- }
- void CloudImage::update(
- not_null<Main::Session*> session,
- const ImageWithLocation &data) {
- UpdateCloudFile(
- _file,
- data,
- session->data().cache(),
- kImageCacheTag,
- [=](FileOrigin origin) { load(session, origin); },
- [=](QImage preloaded, QByteArray) {
- setToActive(session, std::move(preloaded));
- });
- }
- bool CloudImage::empty() const {
- return !_file.location.valid();
- }
- bool CloudImage::loading() const {
- return (_file.loader != nullptr);
- }
- bool CloudImage::failed() const {
- return (_file.flags & CloudFile::Flag::Failed);
- }
- bool CloudImage::loadedOnce() const {
- return (_file.flags & CloudFile::Flag::Loaded);
- }
- void CloudImage::load(not_null<Main::Session*> session, FileOrigin origin) {
- const auto autoLoading = false;
- const auto finalCheck = [=] {
- if (const auto active = activeView()) {
- return active->isNull();
- } else if (_file.flags & CloudFile::Flag::Loaded) {
- return false;
- }
- return !(_file.flags & CloudFile::Flag::Loaded);
- };
- const auto done = [=](QImage result, QByteArray) {
- setToActive(session, std::move(result));
- };
- LoadCloudFile(
- session,
- _file,
- origin,
- LoadFromCloudOrLocal,
- autoLoading,
- kImageCacheTag,
- finalCheck,
- done);
- }
- const ImageLocation &CloudImage::location() const {
- return _file.location;
- }
- int CloudImage::byteSize() const {
- return _file.byteSize;
- }
- std::shared_ptr<QImage> CloudImage::createView() {
- if (auto active = activeView()) {
- return active;
- }
- auto view = std::make_shared<QImage>();
- _view = view;
- return view;
- }
- std::shared_ptr<QImage> CloudImage::activeView() const {
- return _view.lock();
- }
- bool CloudImage::isCurrentView(const std::shared_ptr<QImage> &view) const {
- if (!view) {
- return empty();
- }
- return !view.owner_before(_view) && !_view.owner_before(view);
- }
- void CloudImage::setToActive(
- not_null<Main::Session*> session,
- QImage image) {
- if (const auto view = activeView()) {
- *view = image.isNull()
- ? Image::Empty()->original()
- : std::move(image);
- session->notifyDownloaderTaskFinished();
- }
- }
- void UpdateCloudFile(
- CloudFile &file,
- const ImageWithLocation &data,
- Storage::Cache::Database &cache,
- uint8 cacheTag,
- Fn<void(FileOrigin)> restartLoader,
- Fn<void(QImage, QByteArray)> usePreloaded) {
- if (!data.location.valid()) {
- if (data.progressivePartSize && !file.location.valid()) {
- file.progressivePartSize = data.progressivePartSize;
- }
- if (data.location.width()
- && data.location.height()
- && !file.location.valid()
- && !file.location.width()) {
- file.location = data.location;
- }
- return;
- }
- const auto needStickerThumbnailUpdate = [&] {
- const auto was = std::get_if<StorageFileLocation>(
- &file.location.file().data);
- const auto now = std::get_if<StorageFileLocation>(
- &data.location.file().data);
- using Type = StorageFileLocation::Type;
- if (!was || !now || was->type() != Type::StickerSetThumb) {
- return false;
- }
- return now->valid()
- && (now->type() != Type::StickerSetThumb
- || now->cacheKey() != was->cacheKey());
- };
- 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())
- || needStickerThumbnailUpdate()));
- if (!update) {
- return;
- }
- auto cacheBytes = !data.bytes.isEmpty()
- ? data.bytes
- : v::is<InMemoryLocation>(file.location.file().data)
- ? v::get<InMemoryLocation>(file.location.file().data).bytes
- : QByteArray();
- if (!cacheBytes.isEmpty()) {
- if (const auto cacheKey = data.location.file().cacheKey()) {
- cache.putIfEmpty(
- cacheKey,
- Storage::Cache::Database::TaggedValue(
- std::move(cacheBytes),
- cacheTag));
- }
- }
- file.location = data.location;
- file.byteSize = data.bytesCount;
- if (!data.preloaded.isNull()) {
- file.loader = nullptr;
- if (usePreloaded) {
- usePreloaded(data.preloaded, data.bytes);
- }
- } else if (file.loader) {
- const auto origin = base::take(file.loader)->fileOrigin();
- restartLoader(origin);
- } else if (file.flags & CloudFile::Flag::Failed) {
- file.flags &= ~CloudFile::Flag::Failed;
- }
- }
- void LoadCloudFile(
- not_null<Main::Session*> session,
- CloudFile &file,
- FileOrigin origin,
- LoadFromCloudSetting fromCloud,
- bool autoLoading,
- uint8 cacheTag,
- Fn<bool()> finalCheck,
- Fn<void(CloudFile&)> done,
- Fn<void(bool)> fail,
- Fn<void()> progress,
- int downloadFrontPartSize = 0) {
- const auto loadSize = downloadFrontPartSize
- ? std::min(downloadFrontPartSize, file.byteSize)
- : file.byteSize;
- if (file.loader) {
- if (fromCloud == LoadFromCloudOrLocal) {
- file.loader->permitLoadFromCloud();
- }
- if (file.loader->loadSize() < loadSize) {
- file.loader->increaseLoadSize(loadSize, autoLoading);
- }
- return;
- } else if ((file.flags & CloudFile::Flag::Failed)
- || !file.location.valid()
- || (finalCheck && !finalCheck())) {
- return;
- }
- file.flags &= ~CloudFile::Flag::Cancelled;
- file.loader = CreateFileLoader(
- session,
- file.location.file(),
- origin,
- QString(),
- loadSize,
- file.byteSize,
- UnknownFileLocation,
- LoadToCacheAsWell,
- fromCloud,
- autoLoading,
- cacheTag);
- const auto finish = [done](CloudFile &file) {
- if (!file.loader || file.loader->cancelled()) {
- file.flags |= CloudFile::Flag::Cancelled;
- } else {
- file.flags |= CloudFile::Flag::Loaded;
- done(file);
- }
- // NB! file.loader may be in ~FileLoader() already.
- if (const auto loader = base::take(file.loader)) {
- if ((file.flags & CloudFile::Flag::Cancelled)
- && !loader->cancelled()) {
- loader->cancel();
- }
- }
- };
- file.loader->updates(
- ) | rpl::start_with_next_error_done([=] {
- if (const auto onstack = progress) {
- onstack();
- }
- }, [=, &file](FileLoader::Error error) {
- finish(file);
- file.flags |= CloudFile::Flag::Failed;
- if (const auto onstack = fail) {
- onstack(error.started);
- }
- }, [=, &file] {
- finish(file);
- }, file.loader->lifetime());
- file.loader->start();
- }
- void LoadCloudFile(
- not_null<Main::Session*> session,
- CloudFile &file,
- FileOrigin origin,
- LoadFromCloudSetting fromCloud,
- bool autoLoading,
- uint8 cacheTag,
- Fn<bool()> finalCheck,
- Fn<void(QImage, QByteArray)> done,
- Fn<void(bool)> fail,
- Fn<void()> progress,
- int downloadFrontPartSize) {
- const auto callback = [=](CloudFile &file) {
- if (auto read = file.loader->imageData(); read.isNull()) {
- file.flags |= CloudFile::Flag::Failed;
- if (const auto onstack = fail) {
- onstack(true);
- }
- } else if (const auto onstack = done) {
- onstack(std::move(read), file.loader->bytes());
- }
- };
- LoadCloudFile(
- session,
- file,
- origin,
- fromCloud,
- autoLoading,
- cacheTag,
- finalCheck,
- callback,
- std::move(fail),
- std::move(progress),
- downloadFrontPartSize);
- }
- void LoadCloudFile(
- not_null<Main::Session*> session,
- CloudFile &file,
- FileOrigin origin,
- LoadFromCloudSetting fromCloud,
- bool autoLoading,
- uint8 cacheTag,
- Fn<bool()> finalCheck,
- Fn<void(QByteArray)> done,
- Fn<void(bool)> fail,
- Fn<void()> progress) {
- const auto callback = [=](CloudFile &file) {
- if (auto bytes = file.loader->bytes(); bytes.isEmpty()) {
- file.flags |= CloudFile::Flag::Failed;
- if (const auto onstack = fail) {
- onstack(true);
- }
- } else if (const auto onstack = done) {
- onstack(std::move(bytes));
- }
- };
- LoadCloudFile(
- session,
- file,
- origin,
- fromCloud,
- autoLoading,
- cacheTag,
- finalCheck,
- callback,
- std::move(fail),
- std::move(progress));
- }
- } // namespace Data
|