mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-28 19:40:54 +00:00
Export file thumbs, use in video messages.
This commit is contained in:
parent
cb8ff398a5
commit
cef50e5f52
@ -119,45 +119,45 @@ pre {
|
||||
}
|
||||
.color_red,
|
||||
.userpic1,
|
||||
.media_call .thumb,
|
||||
.media_file .thumb,
|
||||
.media_live_location .thumb {
|
||||
.media_call .fill,
|
||||
.media_file .fill,
|
||||
.media_live_location .fill {
|
||||
background-color: #ff5555;
|
||||
}
|
||||
.color_green,
|
||||
.userpic2,
|
||||
.media_call.success .thumb {
|
||||
.media_call.success .fill {
|
||||
background-color: #64bf47;
|
||||
}
|
||||
.color_yellow,
|
||||
.userpic3,
|
||||
.media_venue .thumb {
|
||||
.media_venue .fill {
|
||||
background-color: #ffab00;
|
||||
}
|
||||
.color_blue,
|
||||
.userpic4,
|
||||
.media_audio_file .thumb,
|
||||
.media_voice_message .thumb {
|
||||
.media_audio_file .fill,
|
||||
.media_voice_message .fill {
|
||||
background-color: #4f9cd9;
|
||||
}
|
||||
.color_purple,
|
||||
.userpic5,
|
||||
.media_game .thumb {
|
||||
.media_game .fill {
|
||||
background-color: #9884e8;
|
||||
}
|
||||
.color_pink,
|
||||
.userpic6,
|
||||
.media_invoice .thumb {
|
||||
.media_invoice .fill {
|
||||
background-color: #e671a5;
|
||||
}
|
||||
.color_sea,
|
||||
.userpic7,
|
||||
.media_location .thumb {
|
||||
.media_location .fill {
|
||||
background-color: #47bcd1;
|
||||
}
|
||||
.color_orange,
|
||||
.userpic8,
|
||||
.media_contact .thumb {
|
||||
.media_contact .fill {
|
||||
background-color: #ff8c44;
|
||||
}
|
||||
.personal_info {
|
||||
@ -289,10 +289,13 @@ a.block_link:hover {
|
||||
margin: 0 -10px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.default .media .fill,
|
||||
.default .media .thumb {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.default .media .fill {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 12px 12px;
|
||||
background-size: 24px 24px;
|
||||
@ -399,30 +402,30 @@ a.block_link:hover {
|
||||
.page_header a.content {
|
||||
background-image: url(../images/back@2x.png);
|
||||
}
|
||||
.media_call .thumb {
|
||||
.media_call .fill {
|
||||
background-image: url(../images/media_call@2x.png)
|
||||
}
|
||||
.media_contact .thumb {
|
||||
.media_contact .fill {
|
||||
background-image: url(../images/media_contact@2x.png)
|
||||
}
|
||||
.media_file .thumb {
|
||||
.media_file .fill {
|
||||
background-image: url(../images/media_file@2x.png)
|
||||
}
|
||||
.media_game .thumb {
|
||||
.media_game .fill {
|
||||
background-image: url(../images/media_game@2x.png)
|
||||
}
|
||||
.media_live_location .thumb,
|
||||
.media_location .thumb,
|
||||
.media_venue .thumb {
|
||||
.media_live_location .fill,
|
||||
.media_location .fill,
|
||||
.media_venue .fill {
|
||||
background-image: url(../images/media_location@2x.png)
|
||||
}
|
||||
.media_audio_file .thumb {
|
||||
.media_audio_file .fill {
|
||||
background-image: url(../images/media_music@2x.png)
|
||||
}
|
||||
.media_invoice .thumb {
|
||||
.media_invoice .fill {
|
||||
background-image: url(../images/media_shop@2x.png)
|
||||
}
|
||||
.media_voice_message .thumb {
|
||||
.media_voice_message .fill {
|
||||
background-image: url(../images/media_voice@2x.png)
|
||||
}
|
||||
}
|
||||
|
@ -389,17 +389,36 @@ Document ParseDocument(
|
||||
data.match([&](const MTPDdocument &data) {
|
||||
result.id = data.vid.v;
|
||||
result.date = data.vdate.v;
|
||||
result.mime = ParseString(data.vmime_type);
|
||||
ParseAttributes(result, data.vattributes);
|
||||
|
||||
result.file.size = data.vsize.v;
|
||||
result.file.location.dcId = data.vdc_id.v;
|
||||
result.file.location.data = MTP_inputDocumentFileLocation(
|
||||
data.vid,
|
||||
data.vaccess_hash,
|
||||
data.vversion);
|
||||
result.mime = ParseString(data.vmime_type);
|
||||
ParseAttributes(result, data.vattributes);
|
||||
result.file.suggestedPath = suggestedFolder
|
||||
const auto path = result.file.suggestedPath = suggestedFolder
|
||||
+ DocumentFolder(result) + '/'
|
||||
+ CleanDocumentName(ComputeDocumentName(context, result));
|
||||
|
||||
result.thumb = data.vthumb.match([](const MTPDphotoSizeEmpty &) {
|
||||
return Image();
|
||||
}, [&](const auto &data) {
|
||||
auto result = Image();
|
||||
result.width = data.vw.v;
|
||||
result.height = data.vh.v;
|
||||
result.file.location = ParseLocation(data.vlocation);
|
||||
if constexpr (MTPDphotoCachedSize::Is<decltype(data)>()) {
|
||||
result.file.content = data.vbytes.v;
|
||||
result.file.size = result.file.content.size();
|
||||
} else {
|
||||
result.file.content = QByteArray();
|
||||
result.file.size = data.vsize.v;
|
||||
}
|
||||
result.file.suggestedPath = path + "_thumb.jpg";
|
||||
return result;
|
||||
});
|
||||
}, [&](const MTPDdocumentEmpty &data) {
|
||||
result.id = data.vid.v;
|
||||
});
|
||||
@ -735,6 +754,24 @@ const File &Media::file() const {
|
||||
});
|
||||
}
|
||||
|
||||
Image &Media::thumb() {
|
||||
return content.match([](Document &data) -> Image& {
|
||||
return data.thumb;
|
||||
}, [](auto&) -> Image& {
|
||||
static Image result;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
const Image &Media::thumb() const {
|
||||
return content.match([](const Document &data) -> const Image& {
|
||||
return data.thumb;
|
||||
}, [](const auto &) -> const Image& {
|
||||
static const Image result;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
Media ParseMedia(
|
||||
ParseMediaContext &context,
|
||||
const MTPMessageMedia &data,
|
||||
@ -936,6 +973,14 @@ const File &Message::file() const {
|
||||
return media.file();
|
||||
}
|
||||
|
||||
Image &Message::thumb() {
|
||||
return media.thumb();
|
||||
}
|
||||
|
||||
const Image &Message::thumb() const {
|
||||
return media.thumb();
|
||||
}
|
||||
|
||||
Message ParseMessage(
|
||||
ParseMediaContext &context,
|
||||
const MTPMessage &data,
|
||||
|
@ -114,6 +114,7 @@ struct Document {
|
||||
TimeId date = 0;
|
||||
|
||||
File file;
|
||||
Image thumb;
|
||||
|
||||
Utf8String name;
|
||||
Utf8String mime;
|
||||
@ -292,6 +293,8 @@ struct Media {
|
||||
|
||||
File &file();
|
||||
const File &file() const;
|
||||
Image &thumb();
|
||||
const Image &thumb() const;
|
||||
};
|
||||
|
||||
struct ParseMediaContext {
|
||||
@ -478,6 +481,8 @@ struct Message {
|
||||
|
||||
File &file();
|
||||
const File &file() const;
|
||||
Image &thumb();
|
||||
const Image &thumb() const;
|
||||
};
|
||||
|
||||
Message ParseMessage(
|
||||
|
@ -139,7 +139,7 @@ struct ApiWrap::UserpicsProcess {
|
||||
base::optional<Data::UserpicsSlice> slice;
|
||||
uint64 maxId = 0;
|
||||
bool lastSlice = false;
|
||||
int fileIndex = -1;
|
||||
int fileIndex = 0;
|
||||
};
|
||||
|
||||
struct ApiWrap::OtherDataProcess {
|
||||
@ -210,7 +210,7 @@ struct ApiWrap::ChatProcess {
|
||||
Data::ParseMediaContext context;
|
||||
base::optional<Data::MessagesSlice> slice;
|
||||
bool lastSlice = false;
|
||||
int fileIndex = -1;
|
||||
int fileIndex = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -727,7 +727,7 @@ void ApiWrap::loadUserpicsFiles(Data::UserpicsSlice &&slice) {
|
||||
_userpicsProcess->lastSlice = true;
|
||||
}
|
||||
_userpicsProcess->slice = std::move(slice);
|
||||
_userpicsProcess->fileIndex = -1;
|
||||
_userpicsProcess->fileIndex = 0;
|
||||
loadNextUserpic();
|
||||
}
|
||||
|
||||
@ -735,14 +735,11 @@ void ApiWrap::loadNextUserpic() {
|
||||
Expects(_userpicsProcess != nullptr);
|
||||
Expects(_userpicsProcess->slice.has_value());
|
||||
|
||||
auto &list = _userpicsProcess->slice->list;
|
||||
while (true) {
|
||||
const auto index = ++_userpicsProcess->fileIndex;
|
||||
if (index >= list.size()) {
|
||||
break;
|
||||
}
|
||||
for (auto &list = _userpicsProcess->slice->list
|
||||
; _userpicsProcess->fileIndex < list.size()
|
||||
; ++_userpicsProcess->fileIndex) {
|
||||
const auto ready = processFileLoad(
|
||||
list[index].image.file,
|
||||
list[_userpicsProcess->fileIndex].image.file,
|
||||
[=](FileProgress value) { return loadUserpicProgress(value); },
|
||||
[=](const QString &path) { loadUserpicDone(path); });
|
||||
if (!ready) {
|
||||
@ -1213,7 +1210,7 @@ void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
|
||||
_chatProcess->lastSlice = true;
|
||||
}
|
||||
_chatProcess->slice = std::move(slice);
|
||||
_chatProcess->fileIndex = -1;
|
||||
_chatProcess->fileIndex = 0;
|
||||
|
||||
loadNextMessageFile();
|
||||
}
|
||||
@ -1222,23 +1219,31 @@ void ApiWrap::loadNextMessageFile() {
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(_chatProcess->slice.has_value());
|
||||
|
||||
auto &list = _chatProcess->slice->list;
|
||||
while (true) {
|
||||
const auto index = ++_chatProcess->fileIndex;
|
||||
if (index >= list.size()) {
|
||||
break;
|
||||
}
|
||||
for (auto &list = _chatProcess->slice->list
|
||||
; _chatProcess->fileIndex < list.size()
|
||||
; ++_chatProcess->fileIndex) {
|
||||
const auto fileProgress = [=](FileProgress value) {
|
||||
return loadMessageFileProgress(value);
|
||||
};
|
||||
const auto ready = processFileLoad(
|
||||
list[index].file(),
|
||||
list[_chatProcess->fileIndex].file(),
|
||||
fileProgress,
|
||||
[=](const QString &path) { loadMessageFileDone(path); },
|
||||
&list[index]);
|
||||
&list[_chatProcess->fileIndex]);
|
||||
if (!ready) {
|
||||
return;
|
||||
}
|
||||
const auto thumbProgress = [=](FileProgress value) {
|
||||
return loadMessageThumbProgress(value);
|
||||
};
|
||||
const auto thumbReady = processFileLoad(
|
||||
list[_chatProcess->fileIndex].thumb().file,
|
||||
thumbProgress,
|
||||
[=](const QString &path) { loadMessageThumbDone(path); },
|
||||
&list[_chatProcess->fileIndex]);
|
||||
if (!thumbReady) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
finishMessagesSlice();
|
||||
}
|
||||
@ -1296,6 +1301,25 @@ void ApiWrap::loadMessageFileDone(const QString &relativePath) {
|
||||
loadNextMessageFile();
|
||||
}
|
||||
|
||||
bool ApiWrap::loadMessageThumbProgress(FileProgress progress) {
|
||||
return loadMessageFileProgress(progress);
|
||||
}
|
||||
|
||||
void ApiWrap::loadMessageThumbDone(const QString &relativePath) {
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(_chatProcess->slice.has_value());
|
||||
Expects((_chatProcess->fileIndex >= 0)
|
||||
&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
|
||||
|
||||
const auto index = _chatProcess->fileIndex;
|
||||
auto &file = _chatProcess->slice->list[index].thumb().file;
|
||||
file.relativePath = relativePath;
|
||||
if (relativePath.isEmpty()) {
|
||||
file.skipReason = Data::File::SkipReason::Unavailable;
|
||||
}
|
||||
loadNextMessageFile();
|
||||
}
|
||||
|
||||
void ApiWrap::finishMessages() {
|
||||
Expects(_chatProcess != nullptr);
|
||||
Expects(!_chatProcess->slice.has_value());
|
||||
@ -1311,7 +1335,8 @@ bool ApiWrap::processFileLoad(
|
||||
Data::Message *message) {
|
||||
using SkipReason = Data::File::SkipReason;
|
||||
|
||||
if (!file.relativePath.isEmpty()) {
|
||||
if (!file.relativePath.isEmpty()
|
||||
|| file.skipReason != SkipReason::None) {
|
||||
return true;
|
||||
} else if (!file.location && file.content.isEmpty()) {
|
||||
file.skipReason = SkipReason::Unavailable;
|
||||
|
@ -150,6 +150,8 @@ private:
|
||||
void loadNextMessageFile();
|
||||
bool loadMessageFileProgress(FileProgress value);
|
||||
void loadMessageFileDone(const QString &relativePath);
|
||||
bool loadMessageThumbProgress(FileProgress value);
|
||||
void loadMessageThumbDone(const QString &relativePath);
|
||||
void finishMessagesSlice();
|
||||
void finishMessages();
|
||||
|
||||
|
@ -262,10 +262,6 @@ Data::Utf8String FormatUsername(const Data::Utf8String &username) {
|
||||
return username.isEmpty() ? username : ('@' + username);
|
||||
}
|
||||
|
||||
QByteArray FormatFilePath(const Data::File &file) {
|
||||
return file.relativePath.toUtf8();
|
||||
}
|
||||
|
||||
bool DisplayDate(TimeId date, TimeId previousDate) {
|
||||
if (!previousDate) {
|
||||
return true;
|
||||
@ -353,6 +349,7 @@ struct MediaData {
|
||||
QByteArray description;
|
||||
QByteArray status;
|
||||
QByteArray classes;
|
||||
QString thumb;
|
||||
QString link;
|
||||
};
|
||||
|
||||
@ -884,33 +881,6 @@ auto HtmlWriter::Wrap::pushMessage(
|
||||
"of Telegram Desktop. Please update the application.") };
|
||||
}
|
||||
|
||||
using SkipReason = Data::File::SkipReason;
|
||||
const auto formatPath = [&](
|
||||
const Data::File &file,
|
||||
const QByteArray &label,
|
||||
const QByteArray &name = QByteArray()) {
|
||||
Expects(!file.relativePath.isEmpty()
|
||||
|| file.skipReason != SkipReason::None);
|
||||
|
||||
const auto pre = name.isEmpty()
|
||||
? QByteArray()
|
||||
: SerializeString(name + ' ');
|
||||
switch (file.skipReason) {
|
||||
case SkipReason::Unavailable:
|
||||
return pre + "(" + label + " unavailable, "
|
||||
"please try again later)";
|
||||
case SkipReason::FileSize:
|
||||
return pre + "(" + label + " exceeds maximum size. "
|
||||
"Change data exporting settings to download.)";
|
||||
case SkipReason::FileType:
|
||||
return pre + "(" + label + " not included. "
|
||||
"Change data exporting settings to download.)";
|
||||
case SkipReason::None: return SerializeLink(
|
||||
FormatFilePath(file),
|
||||
relativePath(file.relativePath));
|
||||
}
|
||||
Unexpected("Skip reason while writing file path.");
|
||||
};
|
||||
const auto wrapReplyToLink = [&](const QByteArray &text) {
|
||||
return "<a href=\"#message"
|
||||
+ NumberToString(message.replyToMsgId)
|
||||
@ -1184,8 +1154,16 @@ QByteArray HtmlWriter::Wrap::pushMedia(
|
||||
}
|
||||
}));
|
||||
}
|
||||
result.append(pushDiv("thumb pull_left"));
|
||||
result.append(popTag());
|
||||
if (data.thumb.isEmpty()) {
|
||||
result.append(pushDiv("fill pull_left"));
|
||||
result.append(popTag());
|
||||
} else {
|
||||
result.append(pushTag("img", {
|
||||
{ "class", "thumb pull_left" },
|
||||
{ "src", relativePath(data.thumb).toUtf8() },
|
||||
{ "empty", "" }
|
||||
}));
|
||||
}
|
||||
result.append(pushDiv("body"));
|
||||
if (!data.title.isEmpty()) {
|
||||
result.append(pushDiv("title bold"));
|
||||
@ -1248,10 +1226,10 @@ MediaData HtmlWriter::Wrap::prepareMediaData(
|
||||
+ "x"
|
||||
+ NumberToString(photo.image.height);
|
||||
result.classes = "media_file"; // #TODO export
|
||||
result.link = FormatFilePath(photo.image.file);
|
||||
result.link = photo.image.file.relativePath;
|
||||
}, [&](const Document &data) {
|
||||
// #TODO export: sticker + thumb (video, video message) + self destruct (ttl)
|
||||
result.link = FormatFilePath(data.file);
|
||||
result.link = data.file.relativePath;
|
||||
if (data.isSticker) {
|
||||
result.title = "Sticker";
|
||||
result.status = data.stickerEmoji;
|
||||
@ -1259,7 +1237,8 @@ MediaData HtmlWriter::Wrap::prepareMediaData(
|
||||
} else if (data.isVideoMessage) {
|
||||
result.title = "Video message";
|
||||
result.status = FormatDuration(data.duration);
|
||||
result.classes = "media_file"; // #TODO export
|
||||
result.thumb = data.thumb.file.relativePath;
|
||||
result.classes = "media_file";
|
||||
} else if (data.isVoiceMessage) {
|
||||
result.title = "Voice message";
|
||||
result.status = FormatDuration(data.duration);
|
||||
@ -1292,7 +1271,7 @@ MediaData HtmlWriter::Wrap::prepareMediaData(
|
||||
result.status = FormatPhoneNumber(data.info.phoneNumber);
|
||||
if (!data.vcard.content.isEmpty()) {
|
||||
result.status += " - vCard";
|
||||
result.link = FormatFilePath(data.vcard);
|
||||
result.link = data.vcard.relativePath;
|
||||
}
|
||||
}, [&](const GeoPoint &data) {
|
||||
if (message.media.ttl) {
|
||||
|
@ -486,6 +486,9 @@ QByteArray SerializeMessage(
|
||||
pushTTL();
|
||||
}, [&](const Document &data) {
|
||||
pushPath(data.file, "file");
|
||||
if (data.thumb.width > 0) {
|
||||
pushPath(data.thumb.file, "thumbnail");
|
||||
}
|
||||
const auto pushType = [&](const QByteArray &value) {
|
||||
push("media_type", value);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user