Keep track of fully cached media files.

This commit is contained in:
John Preston 2019-05-31 19:45:03 +03:00
parent 2255eb2c68
commit 04e3b250e7
20 changed files with 342 additions and 54 deletions

View file

@ -816,6 +816,35 @@ bool DocumentData::uploading() const {
return (uploadingData != nullptr);
}
bool DocumentData::loadedInMediaCache() const {
return (_flags & Flag::LoadedInMediaCache);
}
void DocumentData::setLoadedInMediaCache(bool loaded) {
const auto flags = loaded
? (_flags | Flag::LoadedInMediaCache)
: (_flags & ~Flag::LoadedInMediaCache);
if (_flags == flags) {
return;
}
_flags = flags;
if (!this->loaded()) {
if (loadedInMediaCache()) {
Local::writeFileLocation(
mediaKey(),
FileLocation::InMediaCacheLocation());
} else {
Local::removeFileLocation(mediaKey());
}
owner().requestDocumentViewRepaint(this);
}
}
void DocumentData::setLoadedInMediaCacheLocation() {
_location = FileLocation();
_flags |= Flag::LoadedInMediaCache;
}
void DocumentData::setWaitingForAlbum() {
if (uploading()) {
uploadingData->waitingForAlbum = true;
@ -1010,13 +1039,21 @@ QByteArray DocumentData::data() const {
const FileLocation &DocumentData::location(bool check) const {
if (check && !_location.check()) {
const_cast<DocumentData*>(this)->_location = Local::readFileLocation(mediaKey());
const auto location = Local::readFileLocation(mediaKey());
const auto that = const_cast<DocumentData*>(this);
if (location.inMediaCache()) {
that->setLoadedInMediaCacheLocation();
} else {
that->_location = location;
}
}
return _location;
}
void DocumentData::setLocation(const FileLocation &loc) {
if (loc.check()) {
if (loc.inMediaCache()) {
setLoadedInMediaCacheLocation();
} else if (loc.check()) {
_location = loc;
}
}
@ -1492,6 +1529,13 @@ void DocumentData::setRemoteLocation(
Local::writeFileLocation(mediaKey(), _location);
} else {
_location = Local::readFileLocation(mediaKey());
if (_location.inMediaCache()) {
setLoadedInMediaCacheLocation();
} else if (_location.isEmpty() && loadedInMediaCache()) {
Local::writeFileLocation(
mediaKey(),
FileLocation::InMediaCacheLocation());
}
}
}
}
@ -1520,7 +1564,7 @@ void DocumentData::collectLocalData(not_null<DocumentData*> local) {
ActiveCache().up(this);
}
}
if (!local->_location.isEmpty()) {
if (!local->_location.inMediaCache() && !local->_location.isEmpty()) {
_location = local->_location;
Local::writeFileLocation(mediaKey(), _location);
}

View file

@ -115,6 +115,8 @@ public:
[[nodiscard]] float64 progress() const;
[[nodiscard]] int loadOffset() const;
[[nodiscard]] bool uploading() const;
[[nodiscard]] bool loadedInMediaCache() const;
void setLoadedInMediaCache(bool loaded);
void setWaitingForAlbum();
[[nodiscard]] bool waitingForAlbum() const;
@ -266,6 +268,7 @@ private:
void validateLottieSticker();
void validateGoodThumbnail();
void setMaybeSupportsStreaming(bool supports);
void setLoadedInMediaCacheLocation();
void destroyLoader() const;

View file

@ -2504,15 +2504,18 @@ void Session::documentApplyFields(
if (!date) {
return;
}
if (dc != 0 && access != 0) {
document->setRemoteLocation(dc, access, fileReference);
}
document->date = date;
document->setMimeString(mime);
document->updateThumbnails(thumbnailInline, thumbnail);
document->size = size;
document->recountIsImage();
document->setattributes(attributes);
// Uses 'type' that is computed from attributes.
document->recountIsImage();
if (dc != 0 && access != 0) {
document->setRemoteLocation(dc, access, fileReference);
}
if (document->sticker()
&& !document->sticker()->loc.valid()
&& thumbLocation.valid()) {

View file

@ -274,7 +274,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, crl
void HistoryVideo::drawCornerStatus(Painter &p, bool selected) const {
const auto padding = st::msgDateImgPadding;
const auto radial = _animation && _animation->radial.animating();
const auto cornerDownload = downloadInCorner() && !_data->loaded();
const auto cornerDownload = downloadInCorner() && !_data->loaded() && !_data->loadedInMediaCache();
const auto addWidth = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : 0;
const auto downloadWidth = cornerDownload ? st::normalFont->width(_downloadSize) : 0;
const auto statusW = std::max(downloadWidth, st::normalFont->width(_statusText)) + 2 * padding.x() + addWidth;

View file

@ -60,6 +60,9 @@ int File::Context::read(bytes::span buffer) {
return -1;
}
}
sendFullInCache();
_offset += amount;
return amount;
}
@ -244,6 +247,9 @@ void File::Context::start(crl::time position) {
}
_reader->headerDone();
if (_reader->isRemoteLoader()) {
sendFullInCache(true);
}
if (video.codec || audio.codec) {
seekToPosition(format.get(), video.codec ? video : audio, position);
}
@ -258,6 +264,17 @@ void File::Context::start(crl::time position) {
_format = std::move(format);
}
void File::Context::sendFullInCache(bool force) {
const auto started = _fullInCache.has_value();
if (force || started) {
const auto nowFullInCache = _reader->fullInCache();
if (!started || *_fullInCache != nowFullInCache) {
_fullInCache = nowFullInCache;
_delegate->fileFullInCache(nowFullInCache);
}
}
}
void File::Context::readNextPacket() {
auto result = readPacket();
if (unroll()) {

View file

@ -81,6 +81,7 @@ private:
[[nodiscard]] base::variant<Packet, AvErrorWrap> readPacket();
void handleEndOfFile();
void sendFullInCache(bool force = false);
const not_null<FileDelegate*> _delegate;
const not_null<Reader*> _reader;
@ -89,6 +90,7 @@ private:
int _size = 0;
bool _failed = false;
bool _readTillEnd = false;
std::optional<bool> _fullInCache;
crl::semaphore _semaphore;
std::atomic<bool> _interrupted = false;

View file

@ -22,6 +22,7 @@ public:
Stream &&audio) = 0;
virtual void fileError(Error error) = 0;
virtual void fileWaitingForData() = 0;
virtual void fileFullInCache(bool fullInCache) = 0;
// Return true if reading and processing more packets is desired.
// Return false if sleeping until 'wake()' is called is desired.

View file

@ -174,7 +174,7 @@ void Player::trackSendReceivedTill(
Expects(state.duration != kTimeUnknown);
Expects(state.receivedTill != kTimeUnknown);
if (!_remoteLoader) {
if (!_remoteLoader || _fullInCacheSinceStart.value_or(false)) {
return;
}
const auto receivedTill = std::max(
@ -302,6 +302,15 @@ void Player::fileError(Error error) {
});
}
void Player::fileFullInCache(bool fullInCache) {
crl::on_main(&_sessionGuard, [=] {
if (!_fullInCacheSinceStart.has_value()) {
_fullInCacheSinceStart = fullInCache;
}
_fullInCache.fire_copy(fullInCache);
});
}
void Player::fileWaitingForData() {
if (_waitingForData) {
return;
@ -736,6 +745,10 @@ bool Player::finished() const {
&& (!_video || _videoFinished);
}
float64 Player::speed() const {
return _options.speed;
}
void Player::setSpeed(float64 speed) {
Expects(active());
Expects(speed >= 0.5 && speed <= 2.);
@ -767,6 +780,10 @@ rpl::producer<Update, Error> Player::updates() const {
return _updates.events();
}
rpl::producer<bool> Player::fullInCache() const {
return _fullInCache.events();
}
QSize Player::videoSize() const {
return _information.video.size;
}
@ -803,7 +820,8 @@ Media::Player::TrackState Player::prepareLegacyState() const {
} else if (_options.loop && result.length > 0) {
result.position %= result.length;
}
result.receivedTill = _remoteLoader
result.receivedTill = (_remoteLoader
&& !_fullInCacheSinceStart.value_or(false))
? getCurrentReceivedTill(result.length)
: 0;
result.frequency = kMsFrequency;

View file

@ -57,6 +57,7 @@ public:
[[nodiscard]] bool finished() const;
[[nodiscard]] rpl::producer<Update, Error> updates() const;
[[nodiscard]] rpl::producer<bool> fullInCache() const;
[[nodiscard]] QSize videoSize() const;
[[nodiscard]] QImage frame(const FrameRequest &request) const;
@ -82,6 +83,7 @@ private:
bool fileReady(int headerSize, Stream &&video, Stream &&audio) override;
void fileError(Error error) override;
void fileWaitingForData() override;
void fileFullInCache(bool fullInCache) override;
bool fileProcessPacket(Packet &&packet) override;
bool fileReadMore() override;
@ -176,6 +178,8 @@ private:
crl::time _nextFrameTime = kTimeUnknown;
base::Timer _renderFrameTimer;
rpl::event_stream<Update, Error> _updates;
rpl::event_stream<bool> _fullInCache;
std::optional<bool> _fullInCacheSinceStart;
crl::time _totalDuration = kTimeUnknown;
crl::time _loopingShift = 0;

View file

@ -225,6 +225,7 @@ struct Reader::CacheHelper {
QMutex mutex;
base::flat_map<int, PartsMap> results;
std::vector<int> sizes;
std::atomic<crl::semaphore*> waiting = nullptr;
};
@ -385,6 +386,17 @@ int Reader::Slices::headerSize() const {
return _header.parts.size() * kPartSize;
}
bool Reader::Slices::fullInCache() const {
return _fullInCache;
}
int Reader::Slices::requestSliceSizesCount() const {
if (!headerModeUnknown() || isFullInHeader()) {
return 0;
}
return _data.size();
}
bool Reader::Slices::headerWontBeFilled() const {
return headerModeUnknown()
&& (_header.parts.size() >= kMaxPartsInHeader);
@ -438,6 +450,7 @@ void Reader::Slices::processCacheResult(int sliceNumber, PartsMap &&result) {
return;
}
slice.processCacheData(std::move(result));
checkSliceFullLoaded(sliceNumber);
if (!sliceNumber) {
applyHeaderCacheData();
if (isGoodHeader()) {
@ -448,6 +461,66 @@ void Reader::Slices::processCacheResult(int sliceNumber, PartsMap &&result) {
}
}
void Reader::Slices::processCachedSizes(const std::vector<int> &sizes) {
Expects(sizes.size() == _data.size());
using Flag = Slice::Flag;
const auto count = int(sizes.size());
auto loadedCount = 0;
for (auto i = 0; i != count; ++i) {
const auto sliceNumber = (i + 1);
const auto sliceSize = (sliceNumber < _data.size())
? kInSlice
: (_size - (sliceNumber - 1) * kInSlice);
const auto loaded = (sizes[i] == sliceSize);
if (_data[i].flags & Flag::FullInCache) {
++loadedCount;
} else if (loaded) {
_data[i].flags |= Flag::FullInCache;
++loadedCount;
}
}
_fullInCache = (loadedCount == count);
}
void Reader::Slices::checkSliceFullLoaded(int sliceNumber) {
if (!sliceNumber && !isFullInHeader()) {
return;
}
const auto partsCount = [&] {
if (!sliceNumber) {
return (_size + kPartSize - 1) / kPartSize;
}
return (sliceNumber < _data.size())
? kPartsInSlice
: ((_size - (sliceNumber - 1) * kInSlice + kPartSize - 1)
/ kPartSize);
}();
auto &slice = (sliceNumber ? _data[sliceNumber - 1] : _header);
const auto loaded = (slice.parts.size() == partsCount);
using Flag = Slice::Flag;
if ((slice.flags & Flag::FullInCache) && !loaded) {
slice.flags &= ~Flag::FullInCache;
_fullInCache = false;
} else if (!(slice.flags & Flag::FullInCache) && loaded) {
slice.flags |= Flag::FullInCache;
_fullInCache = checkFullInCache();
}
}
bool Reader::Slices::checkFullInCache() const {
using Flag = Slice::Flag;
if (isFullInHeader()) {
return (_header.flags & Flag::FullInCache);
}
const auto i = ranges::find_if(_data, [](const Slice &slice) {
return !(slice.flags & Flag::FullInCache);
});
return (i == end(_data));
}
void Reader::Slices::processPart(
int offset,
QByteArray &&bytes) {
@ -455,6 +528,7 @@ void Reader::Slices::processPart(
if (isFullInHeader()) {
_header.addPart(offset, bytes);
checkSliceFullLoaded(0);
return;
} else if (_headerMode == HeaderMode::Unknown) {
if (_header.parts.contains(offset)) {
@ -465,6 +539,7 @@ void Reader::Slices::processPart(
}
const auto index = offset / kInSlice;
_data[index].addPart(offset - index * kInSlice, std::move(bytes));
checkSliceFullLoaded(index + 1);
}
auto Reader::Slices::fill(int offset, bytes::span buffer) -> FillResult {
@ -655,7 +730,7 @@ Reader::SerializedSlice Reader::Slices::serializeAndUnloadUnused() {
return false;
}();
if (noNeedToSaveToCache) {
_data[purgeSlice] = Slice();
unloadSlice(_data[purgeSlice]);
return {};
}
return serializeAndUnloadSlice(purgeSlice + 1);
@ -707,13 +782,21 @@ Reader::SerializedSlice Reader::Slices::serializeAndUnloadSlice(
// HeaderMode::Good and we unload first slice. We still require
// header data to continue working, so don't really unload the header.
if (sliceNumber) {
slice = Slice();
unloadSlice(slice);
} else {
slice.flags &= ~Slice::Flag::ChangedSinceCache;
}
return result;
}
void Reader::Slices::unloadSlice(Slice &slice) const {
const auto full = (slice.flags & Slice::Flag::FullInCache);
slice = Slice();
if (full) {
slice.flags |= Slice::Flag::FullInCache;
}
}
QByteArray Reader::Slices::serializeComplexSlice(const Slice &slice) const {
auto result = QByteArray();
const auto count = slice.parts.size();
@ -742,7 +825,7 @@ QByteArray Reader::Slices::serializeAndUnloadFirstSliceNoHeader() {
slice.parts.erase(offset);
}
auto result = serializeComplexSlice(slice);
slice = Slice();
unloadSlice(slice);
return result;
}
@ -1022,8 +1105,14 @@ void Reader::readFromCache(int sliceNumber) {
const auto key = _cacheHelper->key(sliceNumber);
const auto cache = std::weak_ptr<CacheHelper>(_cacheHelper);
const auto weak = base::make_weak(this);
_owner->cacheBigFile().get(key, [=](QByteArray &&result) {
crl::async([=, result = std::move(result)]{
const auto ready = [=](
QByteArray &&result,
std::vector<int> &&sizes = {}) {
crl::async([
=,
result = std::move(result),
sizes = std::move(sizes)
]() mutable{
auto entry = ParseCacheEntry(
bytes::make_span(result),
sliceNumber,
@ -1034,6 +1123,7 @@ void Reader::readFromCache(int sliceNumber) {
if (!sliceNumber && entry.included) {
strong->results.emplace(1, std::move(*entry.included));
}
strong->sizes = std::move(sizes);
if (const auto waiting = strong->waiting.load()) {
strong->waiting.store(nullptr, std::memory_order_release);
waiting->release();
@ -1044,7 +1134,13 @@ void Reader::readFromCache(int sliceNumber) {
}
}
});
});
};
auto keys = std::vector<Storage::Cache::Key>();
const auto count = _slices.requestSliceSizesCount();
for (auto i = 0; i != count; ++i) {
keys.push_back(_cacheHelper->key(i + 1));
}
_owner->cacheBigFile().getWithSizes(key, std::move(keys), ready);
}
bool Reader::readFromCacheForDownloader(int sliceNumber) {
@ -1083,6 +1179,10 @@ int Reader::headerSize() const {
return _slices.headerSize();
}
bool Reader::fullInCache() const {
return _slices.fullInCache();
}
bool Reader::fill(
int offset,
bytes::span buffer,
@ -1184,6 +1284,7 @@ bool Reader::processCacheResults() {
QMutexLocker lock(&_cacheHelper->mutex);
auto loaded = base::take(_cacheHelper->results);
auto sizes = base::take(_cacheHelper->sizes);
lock.unlock();
for (auto &[sliceNumber, cachedParts] : _downloaderReadCache) {
@ -1201,6 +1302,9 @@ bool Reader::processCacheResults() {
for (auto &[sliceNumber, result] : loaded) {
_slices.processCacheResult(sliceNumber, std::move(result));
}
if (!sizes.empty()) {
_slices.processCachedSizes(sizes);
}
if (!loaded.empty()
&& (loaded.front().first == 0)
&& _slices.isGoodHeader()) {

View file

@ -50,6 +50,7 @@ public:
[[nodiscard]] std::optional<Error> streamingError() const;
void headerDone();
[[nodiscard]] int headerSize() const;
[[nodiscard]] bool fullInCache() const;
// Thread safe.
void startSleep(not_null<crl::semaphore*> wake);
@ -104,6 +105,7 @@ private:
LoadingFromCache = 0x01,
LoadedFromCache = 0x02,
ChangedSinceCache = 0x04,
FullInCache = 0x08,
};
friend constexpr inline bool is_flag_type(Flag) { return true; }
using Flags = base::flags<Flag>;
@ -135,13 +137,17 @@ private:
void headerDone(bool fromCache);
[[nodiscard]] int headerSize() const;
[[nodiscard]] bool fullInCache() const;
[[nodiscard]] bool headerWontBeFilled() const;
[[nodiscard]] bool headerModeUnknown() const;
[[nodiscard]] bool isFullInHeader() const;
[[nodiscard]] bool isGoodHeader() const;
[[nodiscard]] bool waitingForHeaderCache() const;
[[nodiscard]] int requestSliceSizesCount() const;
void processCacheResult(int sliceNumber, PartsMap &&result);
void processCachedSizes(const std::vector<int> &sizes);
void processPart(int offset, QByteArray &&bytes);
[[nodiscard]] FillResult fill(int offset, bytes::span buffer);
@ -172,12 +178,16 @@ private:
[[nodiscard]] FillResult fillFromHeader(
int offset,
bytes::span buffer);
void unloadSlice(Slice &slice) const;
void checkSliceFullLoaded(int sliceNumber);
[[nodiscard]] bool checkFullInCache() const;
std::vector<Slice> _data;
Slice _header;
std::deque<int> _usedSlices;
int _size = 0;
HeaderMode _headerMode = HeaderMode::Unknown;
bool _fullInCache = false;
};

View file

@ -1993,6 +1993,11 @@ void OverlayWidget::initStreaming() {
handleStreamingError(std::move(error));
}, _streamed->player.lifetime());
_streamed->player.fullInCache(
) | rpl::start_with_next([=](bool fullInCache) {
_doc->setLoadedInMediaCache(fullInCache);
}, _streamed->player.lifetime());
restartAtSeekPosition(0);
}

View file

@ -153,6 +153,19 @@ void Database::getWithTag(
});
}
void Database::getWithSizes(
const Key &key,
std::vector<Key> &&keys,
FnMut<void(QByteArray&&, std::vector<int>&&)> &&done) {
_wrapped.with([
key,
keys = std::move(keys),
done = std::move(done)
](Implementation &unwrapped) mutable {
unwrapped.getWithSizes(key, std::move(keys), std::move(done));
});
}
auto Database::statsOnMain() const -> rpl::producer<Stats> {
return _wrapped.producer_on_main([](const Implementation &unwrapped) {
return unwrapped.stats();

View file

@ -64,6 +64,11 @@ public:
FnMut<void(Error)> &&done = nullptr);
void getWithTag(const Key &key, FnMut<void(TaggedValue&&)> &&done);
void getWithSizes(
const Key &key,
std::vector<Key> &&keys,
FnMut<void(QByteArray&&, std::vector<int>&&)> &&done);
using Stats = details::Stats;
using TaggedSummary = details::TaggedSummary;
rpl::producer<Stats> statsOnMain() const;

View file

@ -955,7 +955,28 @@ void DatabaseObject::get(
}
}
QByteArray DatabaseObject::readValueData(PlaceId place, size_type size) const {
void DatabaseObject::getWithSizes(
const Key &key,
std::vector<Key> &&keys,
FnMut<void(QByteArray&&, std::vector<int>&&)> &&done) {
get(key, [&](TaggedValue &&value) {
if (value.bytes.isEmpty()) {
invokeCallback(done, QByteArray(), std::vector<int>());
return;
}
auto sizes = keys | ranges::view::transform([&](const Key &sizeKey) {
const auto i = _map.find(sizeKey);
return (i != end(_map)) ? int(i->second.size) : 0;
}) | ranges::to_vector;
invokeCallback(done, std::move(value.bytes), std::move(sizes));
});
}
QByteArray DatabaseObject::readValueData(
PlaceId place,
size_type size) const {
const auto path = placePath(place);
File data;
const auto result = data.open(path, File::Mode::Read, _key);

View file

@ -56,6 +56,11 @@ public:
const Key &to,
FnMut<void(Error)> &&done);
void getWithSizes(
const Key &key,
std::vector<Key> &&keys,
FnMut<void(QByteArray&&, std::vector<int>&&)> &&done);
rpl::producer<Stats> stats() const;
void clear(FnMut<void(Error)> &&done);

View file

@ -810,8 +810,10 @@ void _readLocations() {
MediaKey key(first, second);
_fileLocations.insert(key, loc);
if (!loc.inMediaCache()) {
_fileLocationPairs.insert(loc.fname, FileLocationPair(key, loc));
}
}
if (endMarkFound) {
quint32 cnt;
@ -3133,8 +3135,10 @@ bool hasDraft(const PeerId &peer) {
}
void writeFileLocation(MediaKey location, const FileLocation &local) {
if (local.fname.isEmpty()) return;
if (local.fname.isEmpty()) {
return;
}
if (!local.inMediaCache()) {
FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location);
if (aliasIt != _fileLocationAliases.cend()) {
location = aliasIt.value();
@ -3159,26 +3163,40 @@ void writeFileLocation(MediaKey location, const FileLocation &local) {
_fileLocationPairs.erase(i);
}
}
_fileLocations.insert(location, local);
_fileLocationPairs.insert(local.fname, FileLocationPair(location, local));
} else {
for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) {
if (i.value().inMediaCache() || i.value().check()) {
return;
}
i = _fileLocations.erase(i);
}
}
_fileLocations.insert(location, local);
_writeLocations(WriteMapWhen::Fast);
}
FileLocation readFileLocation(MediaKey location, bool check) {
void removeFileLocation(MediaKey location) {
FileLocations::iterator i = _fileLocations.find(location);
if (i == _fileLocations.end()) {
return;
}
while (i != _fileLocations.end() && (i.key() == location)) {
i = _fileLocations.erase(i);
}
_writeLocations(WriteMapWhen::Fast);
}
FileLocation readFileLocation(MediaKey location) {
FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location);
if (aliasIt != _fileLocationAliases.cend()) {
location = aliasIt.value();
}
FileLocations::iterator i = _fileLocations.find(location);
for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) {
if (check) {
if (!i.value().check()) {
_fileLocationPairs.remove(i.value().fname);
i = _fileLocations.erase(i);
_writeLocations();
continue;
}
if (i != _fileLocations.end()) {
if (i.value().inMediaCache()) {
int a = 0;
}
return i.value();
}

View file

@ -110,7 +110,8 @@ bool hasDraftCursors(const PeerId &peer);
bool hasDraft(const PeerId &peer);
void writeFileLocation(MediaKey location, const FileLocation &local);
FileLocation readFileLocation(MediaKey location, bool check = true);
FileLocation readFileLocation(MediaKey location);
void removeFileLocation(MediaKey location);
Storage::EncryptionKey cacheKey();
QString cachePath();

View file

@ -20,6 +20,7 @@ namespace {
constexpr auto kDocumentBaseCacheTag = 0x0000000000010000ULL;
constexpr auto kDocumentBaseCacheMask = 0x000000000000FF00ULL;
constexpr auto kSerializeTypeShift = quint8(0x08);
const auto kInMediaCacheLocation = QString("*media_cache*");
MTPInputPeer GenerateInputPeer(
uint64 id,
@ -622,7 +623,7 @@ ReadAccessEnabler::~ReadAccessEnabler() {
}
FileLocation::FileLocation(const QString &name) : fname(name) {
if (fname.isEmpty()) {
if (fname.isEmpty() || fname == kInMediaCacheLocation) {
size = 0;
} else {
setBookmark(psPathBookmark(name));
@ -646,8 +647,14 @@ FileLocation::FileLocation(const QString &name) : fname(name) {
}
}
FileLocation FileLocation::InMediaCacheLocation() {
return FileLocation(kInMediaCacheLocation);
}
bool FileLocation::check() const {
if (fname.isEmpty()) return false;
if (fname.isEmpty() || fname == kInMediaCacheLocation) {
return false;
}
ReadAccessEnabler enabler(_bookmark);
if (enabler.failed()) {
@ -683,6 +690,10 @@ QByteArray FileLocation::bookmark() const {
return _bookmark ? _bookmark->bookmark() : QByteArray();
}
bool FileLocation::inMediaCache() const {
return (fname == kInMediaCacheLocation);
}
void FileLocation::setBookmark(const QByteArray &bm) {
_bookmark.reset(bm.isEmpty() ? nullptr : new PsFileBookmark(bm));
}

View file

@ -434,13 +434,16 @@ public:
FileLocation() = default;
explicit FileLocation(const QString &name);
bool check() const;
const QString &name() const;
static FileLocation InMediaCacheLocation();
[[nodiscard]] bool check() const;
[[nodiscard]] const QString &name() const;
void setBookmark(const QByteArray &bookmark);
QByteArray bookmark() const;
bool isEmpty() const {
[[nodiscard]] bool isEmpty() const {
return name().isEmpty();
}
[[nodiscard]] bool inMediaCache() const;
bool accessEnable() const;
void accessDisable() const;