Support file reference refresh in Export.

This commit is contained in:
John Preston 2019-11-29 09:55:22 +03:00
parent 64535251e8
commit ae98e4ae44
4 changed files with 171 additions and 10 deletions

View File

@ -209,6 +209,32 @@ Utf8String FillLeft(const Utf8String &data, int length, char filler) {
return result;
}
bool RefreshFileReference(FileLocation &to, const FileLocation &from) {
if (to.dcId != from.dcId || to.data.type() != from.data.type()) {
return false;
}
if (to.data.type() == mtpc_inputPhotoFileLocation) {
const auto &toData = to.data.c_inputPhotoFileLocation();
const auto &fromData = from.data.c_inputPhotoFileLocation();
if (toData.vid().v != fromData.vid().v
|| toData.vthumb_size().v != fromData.vthumb_size().v) {
return false;
}
to = from;
return true;
} else if (to.data.type() == mtpc_inputDocumentFileLocation) {
const auto &toData = to.data.c_inputDocumentFileLocation();
const auto &fromData = from.data.c_inputDocumentFileLocation();
if (toData.vid().v != fromData.vid().v
|| toData.vthumb_size().v != fromData.vthumb_size().v) {
return false;
}
to = from;
return true;
}
return false;
}
Image ParseMaxImage(
const MTPDphoto &photo,
const QString &suggestedPath) {

View File

@ -60,6 +60,8 @@ struct FileLocation {
}
};
bool RefreshFileReference(FileLocation &to, const FileLocation &from);
struct File {
enum class SkipReason {
None,
@ -526,6 +528,12 @@ struct Message {
const Image &thumb() const;
};
struct FileOrigin {
int split = 0;
MTPInputPeer peer;
int32 messageId = 0;
};
Message ParseMessage(
ParseMediaContext &context,
const MTPMessage &data,

View File

@ -181,6 +181,7 @@ struct ApiWrap::FileProcess {
FnMut<void(const QString &relativePath)> done;
Data::FileLocation location;
Data::FileOrigin origin;
int offset = 0;
int size = 0;
@ -400,6 +401,9 @@ auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) {
} else if (result.type() == qstr("LOCATION_INVALID")
|| result.type() == qstr("VERSION_INVALID")) {
filePartUnavailable();
} else if (result.code() == 400
&& result.type().startsWith(qstr("FILE_REFERENCE_"))) {
filePartRefreshReference(offset);
} else {
error(std::move(result));
}
@ -696,6 +700,7 @@ void ApiWrap::requestOtherData(
_otherDataProcess->file.suggestedPath = suggestedPath;
loadFile(
_otherDataProcess->file,
Data::FileOrigin(),
[](FileProgress progress) { return true; },
[=](const QString &result) { otherDataDone(result); });
}
@ -780,6 +785,7 @@ void ApiWrap::loadNextUserpic() {
; ++_userpicsProcess->fileIndex) {
const auto ready = processFileLoad(
list[_userpicsProcess->fileIndex].image.file,
Data::FileOrigin(),
[=](FileProgress value) { return loadUserpicProgress(value); },
[=](const QString &path) { loadUserpicDone(path); });
if (!ready) {
@ -1382,6 +1388,24 @@ void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
loadNextMessageFile();
}
Data::Message *ApiWrap::currentFileMessage() const {
Expects(_chatProcess != nullptr);
Expects(_chatProcess->slice.has_value());
return &_chatProcess->slice->list[_chatProcess->fileIndex];
}
Data::FileOrigin ApiWrap::currentFileMessageOrigin() const {
Expects(_chatProcess != nullptr);
Expects(_chatProcess->slice.has_value());
auto result = Data::FileOrigin();
result.messageId = currentFileMessage()->id;
result.peer = _chatProcess->info.input;
result.split = _chatProcess->info.splits[_chatProcess->localSplitIndex];
return result;
}
void ApiWrap::loadNextMessageFile() {
Expects(_chatProcess != nullptr);
Expects(_chatProcess->slice.has_value());
@ -1398,9 +1422,10 @@ void ApiWrap::loadNextMessageFile() {
};
const auto ready = processFileLoad(
list[_chatProcess->fileIndex].file(),
currentFileMessageOrigin(),
fileProgress,
[=](const QString &path) { loadMessageFileDone(path); },
&list[_chatProcess->fileIndex]);
currentFileMessage());
if (!ready) {
return;
}
@ -1409,9 +1434,10 @@ void ApiWrap::loadNextMessageFile() {
};
const auto thumbReady = processFileLoad(
list[_chatProcess->fileIndex].thumb().file,
currentFileMessageOrigin(),
thumbProgress,
[=](const QString &path) { loadMessageThumbDone(path); },
&list[_chatProcess->fileIndex]);
currentFileMessage());
if (!thumbReady) {
return;
}
@ -1501,6 +1527,7 @@ void ApiWrap::finishMessages() {
bool ApiWrap::processFileLoad(
Data::File &file,
const Data::FileOrigin &origin,
Fn<bool(FileProgress)> progress,
FnMut<void(QString)> done,
Data::Message *message) {
@ -1512,7 +1539,7 @@ bool ApiWrap::processFileLoad(
} else if (!file.location && file.content.isEmpty()) {
file.skipReason = SkipReason::Unavailable;
return true;
} else if (writePreloadedFile(file)) {
} else if (writePreloadedFile(file, origin)) {
return !file.relativePath.isEmpty();
}
@ -1548,11 +1575,13 @@ bool ApiWrap::processFileLoad(
file.skipReason = SkipReason::FileSize;
return true;
}
loadFile(file, std::move(progress), std::move(done));
loadFile(file, origin, std::move(progress), std::move(done));
return false;
}
bool ApiWrap::writePreloadedFile(Data::File &file) {
bool ApiWrap::writePreloadedFile(
Data::File &file,
const Data::FileOrigin &origin) {
Expects(_settings != nullptr);
using namespace Output;
@ -1561,7 +1590,7 @@ bool ApiWrap::writePreloadedFile(Data::File &file) {
file.relativePath = *path;
return true;
} else if (!file.content.isEmpty()) {
const auto process = prepareFileProcess(file);
const auto process = prepareFileProcess(file, origin);
if (const auto result = process->file.writeBlock(file.content)) {
file.relativePath = process->relativePath;
_fileCache->save(file.location, file.relativePath);
@ -1575,13 +1604,14 @@ bool ApiWrap::writePreloadedFile(Data::File &file) {
void ApiWrap::loadFile(
const Data::File &file,
const Data::FileOrigin &origin,
Fn<bool(FileProgress)> progress,
FnMut<void(QString)> done) {
Expects(_fileProcess == nullptr);
Expects(file.location.dcId != 0
|| file.location.data.type() == mtpc_inputTakeoutFileLocation);
_fileProcess = prepareFileProcess(file);
_fileProcess = prepareFileProcess(file, origin);
_fileProcess->progress = std::move(progress);
_fileProcess->done = std::move(done);
@ -1598,7 +1628,9 @@ void ApiWrap::loadFile(
loadFilePart();
}
auto ApiWrap::prepareFileProcess(const Data::File &file) const
auto ApiWrap::prepareFileProcess(
const Data::File &file,
const Data::FileOrigin &origin) const
-> std::unique_ptr<FileProcess> {
Expects(_settings != nullptr);
@ -1611,6 +1643,7 @@ auto ApiWrap::prepareFileProcess(const Data::File &file) const
result->relativePath = relativePath;
result->location = file.location;
result->size = file.size;
result->origin = origin;
return result;
}
@ -1705,6 +1738,87 @@ void ApiWrap::filePartDone(int offset, const MTPupload_File &result) {
process->done(process->relativePath);
}
void ApiWrap::filePartRefreshReference(int offset) {
Expects(_fileProcess != nullptr);
const auto &origin = _fileProcess->origin;
if (!origin.messageId) {
error("FILE_REFERENCE error for non-message file.");
return;
}
if (origin.peer.type() == mtpc_inputPeerChannel
|| origin.peer.type() == mtpc_inputPeerChannelFromMessage) {
const auto channel = (origin.peer.type() == mtpc_inputPeerChannel)
? MTP_inputChannel(
origin.peer.c_inputPeerChannel().vchannel_id(),
origin.peer.c_inputPeerChannel().vaccess_hash())
: MTP_inputChannelFromMessage(
origin.peer.c_inputPeerChannelFromMessage().vpeer(),
origin.peer.c_inputPeerChannelFromMessage().vmsg_id(),
origin.peer.c_inputPeerChannelFromMessage().vchannel_id());
mainRequest(MTPchannels_GetMessages(
channel,
MTP_vector<MTPInputMessage>(
1,
MTP_inputMessageID(MTP_int(origin.messageId)))
)).fail([=](const RPCError &error) {
filePartUnavailable();
return true;
}).done([=](const MTPmessages_Messages &result) {
filePartExtractReference(offset, result);
}).send();
} else {
splitRequest(origin.split, MTPmessages_GetMessages(
MTP_vector<MTPInputMessage>(
1,
MTP_inputMessageID(MTP_int(origin.messageId)))
)).fail([=](const RPCError &error) {
filePartUnavailable();
return true;
}).done([=](const MTPmessages_Messages &result) {
filePartExtractReference(offset, result);
}).send();
}
}
void ApiWrap::filePartExtractReference(
int offset,
const MTPmessages_Messages &result) {
Expects(_fileProcess != nullptr);
result.match([&](const MTPDmessages_messagesNotModified &data) {
error("Unexpected messagesNotModified received.");
}, [&](const auto &data) {
auto context = Data::ParseMediaContext();
const auto messages = Data::ParseMessagesSlice(
context,
data.vmessages(),
data.vusers(),
data.vchats(),
_chatProcess->info.relativePath);
for (const auto &message : messages.list) {
if (message.id == _fileProcess->origin.messageId) {
const auto refresh1 = Data::RefreshFileReference(
_fileProcess->location,
message.file().location);
const auto refresh2 = Data::RefreshFileReference(
_fileProcess->location,
message.thumb().file.location);
if (refresh1 || refresh2) {
fileRequest(
_fileProcess->location,
offset
).done([=](const MTPupload_File &result) {
filePartDone(offset, result);
}).send();
return;
}
}
}
filePartUnavailable();
});
}
void ApiWrap::filePartUnavailable() {
Expects(_fileProcess != nullptr);
Expects(!_fileProcess->requests.empty());

View File

@ -23,6 +23,7 @@ struct DialogsInfo;
struct DialogInfo;
struct MessagesSlice;
struct Message;
struct FileOrigin;
} // namespace Data
namespace Output {
@ -159,21 +160,33 @@ private:
void finishMessagesSlice();
void finishMessages();
[[nodiscard]] Data::Message *currentFileMessage() const;
[[nodiscard]] Data::FileOrigin currentFileMessageOrigin() const;
bool processFileLoad(
Data::File &file,
const Data::FileOrigin &origin,
Fn<bool(FileProgress)> progress,
FnMut<void(QString)> done,
Data::Message *message = nullptr);
std::unique_ptr<FileProcess> prepareFileProcess(
const Data::File &file) const;
bool writePreloadedFile(Data::File &file);
const Data::File &file,
const Data::FileOrigin &origin) const;
bool writePreloadedFile(
Data::File &file,
const Data::FileOrigin &origin);
void loadFile(
const Data::File &file,
const Data::FileOrigin &origin,
Fn<bool(FileProgress)> progress,
FnMut<void(QString)> done);
void loadFilePart();
void filePartDone(int offset, const MTPupload_File &result);
void filePartUnavailable();
void filePartRefreshReference(int offset);
void filePartExtractReference(
int offset,
const MTPmessages_Messages &result);
template <typename Request>
class RequestBuilder;