tdesktop/Telegram/SourceFiles/media/stories/media_stories_sibling.cpp
2023-07-20 07:20:08 +04:00

263 lines
6.5 KiB
C++

/*
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 "media/stories/media_stories_sibling.h"
#include "base/weak_ptr.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_file_origin.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "media/stories/media_stories_controller.h"
#include "media/streaming/media_streaming_instance.h"
#include "media/streaming/media_streaming_player.h"
namespace Media::Stories {
namespace {
constexpr auto kGoodFadeDuration = crl::time(200);
} // namespace
class Sibling::Loader {
public:
virtual ~Loader() = default;
virtual QImage blurred() = 0;
virtual QImage good() = 0;
};
class Sibling::LoaderPhoto final : public Sibling::Loader {
public:
LoaderPhoto(
not_null<PhotoData*> photo,
Data::FileOrigin origin,
Fn<void()> update);
QImage blurred() override;
QImage good() override;
private:
const not_null<PhotoData*> _photo;
const Fn<void()> _update;
std::shared_ptr<Data::PhotoMedia> _media;
rpl::lifetime _waitingLoading;
};
class Sibling::LoaderVideo final
: public Sibling::Loader
, public base::has_weak_ptr {
public:
LoaderVideo(
not_null<DocumentData*> video,
Data::FileOrigin origin,
Fn<void()> update);
QImage blurred() override;
QImage good() override;
private:
void waitForGoodThumbnail();
bool updateAfterGoodCheck();
void streamedFailed();
const not_null<DocumentData*> _video;
const Data::FileOrigin _origin;
const Fn<void()> _update;
std::shared_ptr<Data::DocumentMedia> _media;
std::unique_ptr<Streaming::Instance> _streamed;
rpl::lifetime _waitingGoodGeneration;
bool _checkingGoodInCache = false;
bool _failed = false;
};
Sibling::LoaderPhoto::LoaderPhoto(
not_null<PhotoData*> photo,
Data::FileOrigin origin,
Fn<void()> update)
: _photo(photo)
, _update(std::move(update))
, _media(_photo->createMediaView()) {
_photo->load(origin, LoadFromCloudOrLocal, true);
}
QImage Sibling::LoaderPhoto::blurred() {
if (const auto image = _media->thumbnailInline()) {
return image->original();
}
const auto ratio = style::DevicePixelRatio();
auto result = QImage(ratio, ratio, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::black);
result.setDevicePixelRatio(ratio);
return result;
}
QImage Sibling::LoaderPhoto::good() {
if (const auto image = _media->image(Data::PhotoSize::Large)) {
return image->original();
} else if (!_waitingLoading) {
_photo->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
if (_media->loaded()) {
_update();
}
}, _waitingLoading);
}
return QImage();
}
Sibling::LoaderVideo::LoaderVideo(
not_null<DocumentData*> video,
Data::FileOrigin origin,
Fn<void()> update)
: _video(video)
, _origin(origin)
, _update(std::move( update))
, _media(_video->createMediaView()) {
_media->goodThumbnailWanted();
}
QImage Sibling::LoaderVideo::blurred() {
if (const auto image = _media->thumbnailInline()) {
return image->original();
}
const auto ratio = style::DevicePixelRatio();
auto result = QImage(ratio, ratio, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::black);
result.setDevicePixelRatio(ratio);
return result;
}
QImage Sibling::LoaderVideo::good() {
if (const auto image = _media->goodThumbnail()) {
return image->original();
} else if (!_video->goodThumbnailChecked()) {
if (!_checkingGoodInCache) {
waitForGoodThumbnail();
}
} else if (_failed) {
return QImage();
} else if (!_streamed) {
_streamed = std::make_unique<Streaming::Instance>(
_video,
_origin,
[] {}); // waitingCallback
_streamed->lockPlayer();
_streamed->player().updates(
) | rpl::start_with_next_error([=](Streaming::Update &&update) {
v::match(update.data, [&](Streaming::Information &update) {
_update();
}, [](const auto &update) {
});
}, [=](Streaming::Error &&error) {
streamedFailed();
}, _streamed->lifetime());
if (_streamed->ready()) {
_update();
} else if (!_streamed->valid()) {
streamedFailed();
}
} else if (_streamed->ready()) {
return _streamed->info().video.cover;
}
return QImage();
}
void Sibling::LoaderVideo::streamedFailed() {
_failed = true;
_streamed = nullptr;
_update();
}
void Sibling::LoaderVideo::waitForGoodThumbnail() {
_checkingGoodInCache = true;
const auto weak = make_weak(this);
_video->owner().cache().get({}, [=](const auto &) {
crl::on_main([=] {
if (const auto strong = weak.get()) {
if (!strong->updateAfterGoodCheck()) {
strong->_video->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
strong->updateAfterGoodCheck();
}, strong->_waitingGoodGeneration);
}
}
});
});
}
bool Sibling::LoaderVideo::updateAfterGoodCheck() {
if (!_video->goodThumbnailChecked()) {
return false;
}
_checkingGoodInCache = false;
_waitingGoodGeneration.destroy();
_update();
return true;
}
Sibling::Sibling(
not_null<Controller*> controller,
const Data::StoriesList &list)
: _controller(controller)
, _id{ list.user, list.items.front().id } {
const auto &item = list.items.front();
const auto &data = item.media.data;
const auto origin = Data::FileOrigin();
if (const auto video = std::get_if<not_null<DocumentData*>>(&data)) {
_loader = std::make_unique<LoaderVideo>((*video), origin, [=] {
check();
});
} else if (const auto photo = std::get_if<not_null<PhotoData*>>(&data)) {
_loader = std::make_unique<LoaderPhoto>((*photo), origin, [=] {
check();
});
} else {
Unexpected("Media type in stories list.");
}
_blurred = _loader->blurred();
check();
_goodShown.stop();
}
Sibling::~Sibling() = default;
Data::FullStoryId Sibling::shownId() const {
return _id;
}
bool Sibling::shows(const Data::StoriesList &list) const {
Expects(!list.items.empty());
return _id == Data::FullStoryId{ list.user, list.items.front().id };
}
QImage Sibling::image() const {
return _good.isNull() ? _blurred : _good;
}
void Sibling::check() {
Expects(_loader != nullptr);
auto good = _loader->good();
if (good.isNull()) {
return;
}
_loader = nullptr;
_good = std::move(good);
_goodShown.start([=] {
_controller->repaintSibling(this);
}, 0., 1., kGoodFadeDuration, anim::linear);
}
} // namespace Media::Stories