mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-27 11:37:45 +02:00
209 lines
5 KiB
C++
209 lines
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 "data/data_media_preload.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 "main/main_session_settings.h"
|
|
#include "media/streaming/media_streaming_reader.h"
|
|
#include "storage/file_download.h" // kMaxFileInMemory.
|
|
|
|
namespace Data {
|
|
namespace {
|
|
|
|
constexpr auto kDefaultPreloadPrefix = 4 * 1024 * 1024;
|
|
|
|
[[nodiscard]] int64 ChoosePreloadPrefix(not_null<DocumentData*> video) {
|
|
const auto result = video->videoPreloadPrefix();
|
|
return result
|
|
? result
|
|
: std::min(int64(kDefaultPreloadPrefix), video->size);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
MediaPreload::MediaPreload(Fn<void()> done)
|
|
: _done(std::move(done)) {
|
|
}
|
|
|
|
void MediaPreload::callDone() {
|
|
if (const auto onstack = _done) {
|
|
onstack();
|
|
}
|
|
}
|
|
|
|
PhotoPreload::PhotoPreload(
|
|
not_null<PhotoData*> photo,
|
|
FileOrigin origin,
|
|
Fn<void()> done)
|
|
: MediaPreload(std::move(done))
|
|
, _photo(photo->createMediaView()) {
|
|
start(origin);
|
|
}
|
|
|
|
PhotoPreload::~PhotoPreload() {
|
|
if (_photo) {
|
|
base::take(_photo)->owner()->cancel();
|
|
}
|
|
}
|
|
|
|
bool PhotoPreload::Should(
|
|
not_null<PhotoData*> photo,
|
|
not_null<PeerData*> context) {
|
|
return !photo->cancelled()
|
|
&& AutoDownload::Should(
|
|
photo->session().settings().autoDownload(),
|
|
context,
|
|
photo);
|
|
}
|
|
|
|
void PhotoPreload::start(FileOrigin origin) {
|
|
if (_photo->loaded()) {
|
|
callDone();
|
|
} else {
|
|
_photo->owner()->load(origin, LoadFromCloudOrLocal, true);
|
|
_photo->owner()->session().downloaderTaskFinished(
|
|
) | rpl::filter([=] {
|
|
return _photo->loaded();
|
|
}) | rpl::start_with_next([=] { callDone(); }, _lifetime);
|
|
}
|
|
}
|
|
|
|
VideoPreload::VideoPreload(
|
|
not_null<DocumentData*> video,
|
|
FileOrigin origin,
|
|
Fn<void()> done)
|
|
: MediaPreload(std::move(done))
|
|
, DownloadMtprotoTask(
|
|
&video->session().downloader(),
|
|
video->videoPreloadLocation(),
|
|
origin)
|
|
, _video(video)
|
|
, _full(video->size) {
|
|
if (Can(video)) {
|
|
check();
|
|
} else {
|
|
callDone();
|
|
}
|
|
}
|
|
|
|
void VideoPreload::check() {
|
|
const auto key = _video->bigFileBaseCacheKey();
|
|
const auto weak = base::make_weak(static_cast<has_weak_ptr*>(this));
|
|
_video->owner().cacheBigFile().get(key, [weak](
|
|
const QByteArray &result) {
|
|
if (!result.isEmpty()) {
|
|
crl::on_main([weak] {
|
|
if (const auto strong = weak.get()) {
|
|
static_cast<VideoPreload*>(strong)->callDone();
|
|
}
|
|
});
|
|
} else {
|
|
crl::on_main([weak] {
|
|
if (const auto strong = weak.get()) {
|
|
static_cast<VideoPreload*>(strong)->load();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
void VideoPreload::load() {
|
|
if (!Can(_video)) {
|
|
callDone();
|
|
return;
|
|
}
|
|
const auto prefix = ChoosePreloadPrefix(_video);
|
|
Assert(prefix > 0 && prefix <= _video->size);
|
|
const auto part = Storage::kDownloadPartSize;
|
|
const auto parts = (prefix + part - 1) / part;
|
|
for (auto i = 0; i != parts; ++i) {
|
|
_parts.emplace(i * part, QByteArray());
|
|
}
|
|
addToQueue();
|
|
}
|
|
|
|
void VideoPreload::done(QByteArray result) {
|
|
const auto key = _video->bigFileBaseCacheKey();
|
|
if (!result.isEmpty() && key) {
|
|
Assert(result.size() < Storage::kMaxFileInMemory);
|
|
_video->owner().cacheBigFile().putIfEmpty(
|
|
key,
|
|
Storage::Cache::Database::TaggedValue(std::move(result), 0));
|
|
}
|
|
callDone();
|
|
}
|
|
|
|
VideoPreload::~VideoPreload() {
|
|
if (!_finished && !_failed) {
|
|
cancelAllRequests();
|
|
}
|
|
}
|
|
|
|
bool VideoPreload::Can(not_null<DocumentData*> video) {
|
|
return video->canBeStreamed(nullptr)
|
|
&& video->videoPreloadLocation().valid()
|
|
&& video->bigFileBaseCacheKey();
|
|
}
|
|
|
|
bool VideoPreload::readyToRequest() const {
|
|
const auto part = Storage::kDownloadPartSize;
|
|
return !_failed && (_nextRequestOffset < _parts.size() * part);
|
|
}
|
|
|
|
int64 VideoPreload::takeNextRequestOffset() {
|
|
Expects(readyToRequest());
|
|
|
|
_requestedOffsets.emplace(_nextRequestOffset);
|
|
_nextRequestOffset += Storage::kDownloadPartSize;
|
|
return _requestedOffsets.back();
|
|
}
|
|
|
|
bool VideoPreload::feedPart(
|
|
int64 offset,
|
|
const QByteArray &bytes) {
|
|
Expects(offset < _parts.size() * Storage::kDownloadPartSize);
|
|
Expects(_requestedOffsets.contains(int(offset)));
|
|
Expects(bytes.size() <= Storage::kDownloadPartSize);
|
|
|
|
const auto part = Storage::kDownloadPartSize;
|
|
_requestedOffsets.remove(int(offset));
|
|
_parts[offset] = bytes;
|
|
if ((_nextRequestOffset + part >= _parts.size() * part)
|
|
&& _requestedOffsets.empty()) {
|
|
_finished = true;
|
|
removeFromQueue();
|
|
auto result = ::Media::Streaming::SerializeComplexPartsMap(_parts);
|
|
if (result.size() == _full) {
|
|
// Make sure it is parsed as a complex map.
|
|
result.push_back(char(0));
|
|
}
|
|
done(std::move(result));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void VideoPreload::cancelOnFail() {
|
|
_failed = true;
|
|
cancelAllRequests();
|
|
done({});
|
|
}
|
|
|
|
bool VideoPreload::setWebFileSizeHook(int64 size) {
|
|
_failed = true;
|
|
cancelAllRequests();
|
|
done({});
|
|
return false;
|
|
}
|
|
|
|
} // namespace Data
|