diff --git a/Telegram/Resources/export_html/css/style.css b/Telegram/Resources/export_html/css/style.css index 31136ab9c..1e4bcface 100644 --- a/Telegram/Resources/export_html/css/style.css +++ b/Telegram/Resources/export_html/css/style.css @@ -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) } } diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index eba6f47c4..949530e5d 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -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()) { + 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, diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 7facf8ec7..d14dc0ddd 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -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( diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index 34ddd1b5a..148fe2472 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -139,7 +139,7 @@ struct ApiWrap::UserpicsProcess { base::optional 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 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; diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h index 6362efb9a..cdddc9df6 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.h +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -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(); diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index e226fc0fd..b0b1dfad6 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -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 " 0) { + pushPath(data.thumb.file, "thumbnail"); + } const auto pushType = [&](const QByteArray &value) { push("media_type", value); };