mirror of https://github.com/procxx/kepka.git
Improve photo/video/sticker/GIF export layout.
This commit is contained in:
parent
0ef7503917
commit
c3736c6fa3
|
@ -56,6 +56,7 @@ pre {
|
||||||
}
|
}
|
||||||
.page_header {
|
.page_header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
z-index: 10;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: 1px solid #e3e6e8;
|
border-bottom: 1px solid #e3e6e8;
|
||||||
|
@ -126,7 +127,8 @@ pre {
|
||||||
}
|
}
|
||||||
.color_green,
|
.color_green,
|
||||||
.userpic2,
|
.userpic2,
|
||||||
.media_call.success .fill {
|
.media_call.success .fill,
|
||||||
|
.media_photo .fill {
|
||||||
background-color: #64bf47;
|
background-color: #64bf47;
|
||||||
}
|
}
|
||||||
.color_yellow,
|
.color_yellow,
|
||||||
|
@ -152,7 +154,8 @@ pre {
|
||||||
}
|
}
|
||||||
.color_sea,
|
.color_sea,
|
||||||
.userpic7,
|
.userpic7,
|
||||||
.media_location .fill {
|
.media_location .fill,
|
||||||
|
.media_video .fill {
|
||||||
background-color: #47bcd1;
|
background-color: #47bcd1;
|
||||||
}
|
}
|
||||||
.color_orange,
|
.color_orange,
|
||||||
|
@ -313,6 +316,63 @@ a.block_link:hover {
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
.default .video_file_wrap,
|
||||||
|
.default .animated_wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.default .video_file,
|
||||||
|
.default .animated,
|
||||||
|
.default .photo,
|
||||||
|
.default .sticker {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.video_duration {
|
||||||
|
background: rgba(0, 0, 0, .4);
|
||||||
|
padding: 0px 5px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 2px;
|
||||||
|
right: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.video_play_bg {
|
||||||
|
background: rgba(0, 0, 0, .4);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 0;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: -20px auto 0 -20px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.video_play {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
margin-top: -9px;
|
||||||
|
z-index: 1;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 9px 0 9px 14px;
|
||||||
|
border-color: transparent transparent transparent #fff;
|
||||||
|
}
|
||||||
|
.gif_play {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #FFF;
|
||||||
|
display: block;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
.pagination {
|
.pagination {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
@ -349,32 +409,38 @@ a.block_link:hover {
|
||||||
.page_header a.content {
|
.page_header a.content {
|
||||||
background-image: url(../images/back.png);
|
background-image: url(../images/back.png);
|
||||||
}
|
}
|
||||||
.media_call .thumb {
|
.media_call .fill {
|
||||||
background-image: url(../images/media_call.png)
|
background-image: url(../images/media_call.png)
|
||||||
}
|
}
|
||||||
.media_contact .thumb {
|
.media_contact .fill {
|
||||||
background-image: url(../images/media_contact.png)
|
background-image: url(../images/media_contact.png)
|
||||||
}
|
}
|
||||||
.media_file .thumb {
|
.media_file .fill {
|
||||||
background-image: url(../images/media_file.png)
|
background-image: url(../images/media_file.png)
|
||||||
}
|
}
|
||||||
.media_game .thumb {
|
.media_game .fill {
|
||||||
background-image: url(../images/media_game.png)
|
background-image: url(../images/media_game.png)
|
||||||
}
|
}
|
||||||
.media_live_location .thumb,
|
.media_live_location .fill,
|
||||||
.media_location .thumb,
|
.media_location .fill,
|
||||||
.media_venue .thumb {
|
.media_venue .fill {
|
||||||
background-image: url(../images/media_location.png)
|
background-image: url(../images/media_location.png)
|
||||||
}
|
}
|
||||||
.media_audio_file .thumb {
|
.media_audio_file .fill {
|
||||||
background-image: url(../images/media_music.png)
|
background-image: url(../images/media_music.png)
|
||||||
}
|
}
|
||||||
.media_invoice .thumb {
|
.media_invoice .fill {
|
||||||
background-image: url(../images/media_shop.png)
|
background-image: url(../images/media_shop.png)
|
||||||
}
|
}
|
||||||
.media_voice_message .thumb {
|
.media_voice_message .fill {
|
||||||
background-image: url(../images/media_voice.png)
|
background-image: url(../images/media_voice.png)
|
||||||
}
|
}
|
||||||
|
.media_photo .fill {
|
||||||
|
background-image: url(../images/media_photo.png)
|
||||||
|
}
|
||||||
|
.media_video .fill {
|
||||||
|
background-image: url(../images/media_video.png)
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
|
@media only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
|
||||||
.section.calls {
|
.section.calls {
|
||||||
|
@ -433,4 +499,10 @@ a.block_link:hover {
|
||||||
.media_voice_message .fill {
|
.media_voice_message .fill {
|
||||||
background-image: url(../images/media_voice@2x.png)
|
background-image: url(../images/media_voice@2x.png)
|
||||||
}
|
}
|
||||||
|
.media_photo .fill {
|
||||||
|
background-image: url(../images/media_photo@2x.png)
|
||||||
|
}
|
||||||
|
.media_video .fill {
|
||||||
|
background-image: url(../images/media_video@2x.png)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 243 B |
Binary file not shown.
After Width: | Height: | Size: 458 B |
Binary file not shown.
After Width: | Height: | Size: 235 B |
Binary file not shown.
After Width: | Height: | Size: 411 B |
|
@ -15,8 +15,12 @@
|
||||||
<file alias="images/media_location@2x.png">../export_html/images/media_location@2x.png</file>
|
<file alias="images/media_location@2x.png">../export_html/images/media_location@2x.png</file>
|
||||||
<file alias="images/media_music.png">../export_html/images/media_music.png</file>
|
<file alias="images/media_music.png">../export_html/images/media_music.png</file>
|
||||||
<file alias="images/media_music@2x.png">../export_html/images/media_music@2x.png</file>
|
<file alias="images/media_music@2x.png">../export_html/images/media_music@2x.png</file>
|
||||||
|
<file alias="images/media_photo.png">../export_html/images/media_photo.png</file>
|
||||||
|
<file alias="images/media_photo@2x.png">../export_html/images/media_photo@2x.png</file>
|
||||||
<file alias="images/media_shop.png">../export_html/images/media_shop.png</file>
|
<file alias="images/media_shop.png">../export_html/images/media_shop.png</file>
|
||||||
<file alias="images/media_shop@2x.png">../export_html/images/media_shop@2x.png</file>
|
<file alias="images/media_shop@2x.png">../export_html/images/media_shop@2x.png</file>
|
||||||
|
<file alias="images/media_video.png">../export_html/images/media_video.png</file>
|
||||||
|
<file alias="images/media_video@2x.png">../export_html/images/media_video@2x.png</file>
|
||||||
<file alias="images/media_voice.png">../export_html/images/media_voice.png</file>
|
<file alias="images/media_voice.png">../export_html/images/media_voice.png</file>
|
||||||
<file alias="images/media_voice@2x.png">../export_html/images/media_voice@2x.png</file>
|
<file alias="images/media_voice@2x.png">../export_html/images/media_voice@2x.png</file>
|
||||||
<file alias="images/section_calls.png">../export_html/images/section_calls.png</file>
|
<file alias="images/section_calls.png">../export_html/images/section_calls.png</file>
|
||||||
|
|
|
@ -501,41 +501,66 @@ UserpicsSlice ParseUserpicsSlice(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WriteImageThumb(
|
std::pair<QString, QSize> WriteImageThumb(
|
||||||
const QString &basePath,
|
const QString &basePath,
|
||||||
const QString &largePath,
|
const QString &largePath,
|
||||||
int width,
|
Fn<QSize(QSize)> convertSize,
|
||||||
int height,
|
base::optional<QByteArray> format,
|
||||||
|
base::optional<int> quality,
|
||||||
const QString &postfix) {
|
const QString &postfix) {
|
||||||
if (largePath.isEmpty()) {
|
if (largePath.isEmpty()) {
|
||||||
return QString();
|
return {};
|
||||||
}
|
}
|
||||||
const auto path = basePath + largePath;
|
const auto path = basePath + largePath;
|
||||||
QImageReader reader(path);
|
QImageReader reader(path);
|
||||||
if (!reader.canRead()) {
|
if (!reader.canRead()) {
|
||||||
return QString();
|
return {};
|
||||||
}
|
}
|
||||||
const auto size = reader.size();
|
const auto size = reader.size();
|
||||||
if (size.isEmpty()
|
if (size.isEmpty()
|
||||||
|| size.width() >= kMaxImageSize
|
|| size.width() >= kMaxImageSize
|
||||||
|| size.height() >= kMaxImageSize) {
|
|| size.height() >= kMaxImageSize) {
|
||||||
return QString();
|
return {};
|
||||||
}
|
}
|
||||||
auto image = reader.read();
|
auto image = reader.read();
|
||||||
if (image.isNull()) {
|
if (image.isNull()) {
|
||||||
return QString();
|
return {};
|
||||||
}
|
}
|
||||||
const auto format = reader.format();
|
const auto finalSize = convertSize(image.size());
|
||||||
|
if (finalSize.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
image = std::move(image).scaled(
|
||||||
|
finalSize,
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
const auto finalFormat = format ? *format : reader.format();
|
||||||
|
const auto finalQuality = quality ? *quality : reader.quality();
|
||||||
const auto lastSlash = largePath.lastIndexOf('/');
|
const auto lastSlash = largePath.lastIndexOf('/');
|
||||||
const auto firstDot = largePath.indexOf('.', lastSlash + 1);
|
const auto firstDot = largePath.indexOf('.', lastSlash + 1);
|
||||||
const auto thumb = (firstDot >= 0)
|
const auto thumb = (firstDot >= 0)
|
||||||
? largePath.mid(0, firstDot) + postfix + largePath.mid(firstDot)
|
? largePath.mid(0, firstDot) + postfix + largePath.mid(firstDot)
|
||||||
: largePath + postfix;
|
: largePath + postfix;
|
||||||
const auto result = Output::File::PrepareRelativePath(basePath, thumb);
|
const auto result = Output::File::PrepareRelativePath(basePath, thumb);
|
||||||
if (!image.save(basePath + result, reader.format(), reader.quality())) {
|
if (!image.save(basePath + result, finalFormat, finalQuality)) {
|
||||||
return QString();
|
return {};
|
||||||
}
|
}
|
||||||
return result;
|
return { result, finalSize };
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WriteImageThumb(
|
||||||
|
const QString &basePath,
|
||||||
|
const QString &largePath,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const QString &postfix) {
|
||||||
|
return WriteImageThumb(
|
||||||
|
basePath,
|
||||||
|
largePath,
|
||||||
|
[=](QSize size) { return QSize(width, height); },
|
||||||
|
base::none,
|
||||||
|
base::none,
|
||||||
|
postfix).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactInfo ParseContactInfo(const MTPUser &data) {
|
ContactInfo ParseContactInfo(const MTPUser &data) {
|
||||||
|
@ -780,7 +805,7 @@ Media ParseMedia(
|
||||||
|
|
||||||
auto result = Media();
|
auto result = Media();
|
||||||
data.match([&](const MTPDmessageMediaPhoto &data) {
|
data.match([&](const MTPDmessageMediaPhoto &data) {
|
||||||
result.content = data.has_photo()
|
auto photo = data.has_photo()
|
||||||
? ParsePhoto(
|
? ParsePhoto(
|
||||||
data.vphoto,
|
data.vphoto,
|
||||||
folder + "photos/"
|
folder + "photos/"
|
||||||
|
@ -788,7 +813,9 @@ Media ParseMedia(
|
||||||
: Photo();
|
: Photo();
|
||||||
if (data.has_ttl_seconds()) {
|
if (data.has_ttl_seconds()) {
|
||||||
result.ttl = data.vttl_seconds.v;
|
result.ttl = data.vttl_seconds.v;
|
||||||
|
photo.image.file = File();
|
||||||
}
|
}
|
||||||
|
result.content = photo;
|
||||||
}, [&](const MTPDmessageMediaGeo &data) {
|
}, [&](const MTPDmessageMediaGeo &data) {
|
||||||
result.content = ParseGeoPoint(data.vgeo);
|
result.content = ParseGeoPoint(data.vgeo);
|
||||||
}, [&](const MTPDmessageMediaContact &data) {
|
}, [&](const MTPDmessageMediaContact &data) {
|
||||||
|
@ -796,12 +823,14 @@ Media ParseMedia(
|
||||||
}, [&](const MTPDmessageMediaUnsupported &data) {
|
}, [&](const MTPDmessageMediaUnsupported &data) {
|
||||||
result.content = UnsupportedMedia();
|
result.content = UnsupportedMedia();
|
||||||
}, [&](const MTPDmessageMediaDocument &data) {
|
}, [&](const MTPDmessageMediaDocument &data) {
|
||||||
result.content = data.has_document()
|
auto document = data.has_document()
|
||||||
? ParseDocument(context, data.vdocument, folder)
|
? ParseDocument(context, data.vdocument, folder)
|
||||||
: Document();
|
: Document();
|
||||||
if (data.has_ttl_seconds()) {
|
if (data.has_ttl_seconds()) {
|
||||||
result.ttl = data.vttl_seconds.v;
|
result.ttl = data.vttl_seconds.v;
|
||||||
|
document.file = File();
|
||||||
}
|
}
|
||||||
|
result.content = document;
|
||||||
}, [&](const MTPDmessageMediaWebPage &data) {
|
}, [&](const MTPDmessageMediaWebPage &data) {
|
||||||
// Ignore web pages.
|
// Ignore web pages.
|
||||||
}, [&](const MTPDmessageMediaVenue &data) {
|
}, [&](const MTPDmessageMediaVenue &data) {
|
||||||
|
@ -1053,6 +1082,10 @@ Message ParseMessage(
|
||||||
context,
|
context,
|
||||||
data.vmedia,
|
data.vmedia,
|
||||||
mediaFolder);
|
mediaFolder);
|
||||||
|
if (result.media.ttl && !data.is_out()) {
|
||||||
|
result.media.file() = File();
|
||||||
|
result.media.thumb().file = File();
|
||||||
|
}
|
||||||
context.botId = 0;
|
context.botId = 0;
|
||||||
}
|
}
|
||||||
result.text = ParseText(
|
result.text = ParseText(
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/optional.h"
|
#include "base/optional.h"
|
||||||
#include "base/variant.h"
|
#include "base/variant.h"
|
||||||
|
|
||||||
|
#include <QtCore/QSize>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
|
|
||||||
|
@ -82,6 +83,14 @@ struct Image {
|
||||||
File file;
|
File file;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::pair<QString, QSize> WriteImageThumb(
|
||||||
|
const QString &basePath,
|
||||||
|
const QString &largePath,
|
||||||
|
Fn<QSize(QSize)> convertSize,
|
||||||
|
base::optional<QByteArray> format = base::none,
|
||||||
|
base::optional<int> quality = base::none,
|
||||||
|
const QString &postfix = "_thumb");
|
||||||
|
|
||||||
QString WriteImageThumb(
|
QString WriteImageThumb(
|
||||||
const QString &basePath,
|
const QString &basePath,
|
||||||
const QString &largePath,
|
const QString &largePath,
|
||||||
|
|
|
@ -1096,6 +1096,11 @@ void ApiWrap::appendChatsSlice(
|
||||||
auto filtered = ranges::view::all(
|
auto filtered = ranges::view::all(
|
||||||
info.list
|
info.list
|
||||||
) | ranges::view::filter([&](const Data::DialogInfo &info) {
|
) | ranges::view::filter([&](const Data::DialogInfo &info) {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
return (info.name == "Anta");
|
||||||
|
#else
|
||||||
|
#error "test"
|
||||||
|
#endif
|
||||||
return (types & SettingsFromDialogsType(info.type)) != 0;
|
return (types & SettingsFromDialogsType(info.type)) != 0;
|
||||||
});
|
});
|
||||||
auto &list = to.info.list;
|
auto &list = to.info.list;
|
||||||
|
@ -1365,10 +1370,12 @@ bool ApiWrap::processFileLoad(
|
||||||
return Type::Photo;
|
return Type::Photo;
|
||||||
}) : Type(0);
|
}) : Type(0);
|
||||||
|
|
||||||
|
const auto limit = _settings->media.sizeLimit;
|
||||||
if ((_settings->media.types & type) != type) {
|
if ((_settings->media.types & type) != type) {
|
||||||
file.skipReason = SkipReason::FileType;
|
file.skipReason = SkipReason::FileType;
|
||||||
return true;
|
return true;
|
||||||
} else if (file.size >= _settings->media.sizeLimit) {
|
} else if ((message ? message->file().size : file.size) >= limit) {
|
||||||
|
// Don't load thumbs for large files that we skip.
|
||||||
file.skipReason = SkipReason::FileSize;
|
file.skipReason = SkipReason::FileSize;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QSize>
|
||||||
#include <QtCore/QTextStream>
|
#include <QtCore/QTextStream>
|
||||||
#include <QtCore/QDateTime>
|
#include <QtCore/QDateTime>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "export/data/export_data_types.h"
|
#include "export/data/export_data_types.h"
|
||||||
#include "core/utils.h"
|
#include "core/utils.h"
|
||||||
|
|
||||||
|
#include <QtCore/QSize>
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
#include <QtCore/QDateTime>
|
#include <QtCore/QDateTime>
|
||||||
|
|
||||||
|
@ -28,6 +29,14 @@ constexpr auto kServiceMessagePhotoSize = 60;
|
||||||
constexpr auto kHistoryUserpicSize = 42;
|
constexpr auto kHistoryUserpicSize = 42;
|
||||||
constexpr auto kSavedMessagesColorIndex = 3;
|
constexpr auto kSavedMessagesColorIndex = 3;
|
||||||
constexpr auto kJoinWithinSeconds = 900;
|
constexpr auto kJoinWithinSeconds = 900;
|
||||||
|
constexpr auto kPhotoMaxWidth = 520;
|
||||||
|
constexpr auto kPhotoMaxHeight = 520;
|
||||||
|
constexpr auto kPhotoMinWidth = 80;
|
||||||
|
constexpr auto kPhotoMinHeight = 80;
|
||||||
|
constexpr auto kStickerMaxWidth = 384;
|
||||||
|
constexpr auto kStickerMaxHeight = 384;
|
||||||
|
constexpr auto kStickerMinWidth = 80;
|
||||||
|
constexpr auto kStickerMinHeight = 80;
|
||||||
|
|
||||||
const auto kLineBreak = QByteArrayLiteral("<br>");
|
const auto kLineBreak = QByteArrayLiteral("<br>");
|
||||||
|
|
||||||
|
@ -41,6 +50,50 @@ bool IsGlobalLink(const QString &link) {
|
||||||
|| link.startsWith(qstr("https://"), Qt::CaseInsensitive);
|
|| link.startsWith(qstr("https://"), Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray NoFileDescription(Data::File::SkipReason reason) {
|
||||||
|
using SkipReason = Data::File::SkipReason;
|
||||||
|
switch (reason) {
|
||||||
|
case SkipReason::Unavailable:
|
||||||
|
return "Unavailable, please try again later.";
|
||||||
|
case SkipReason::FileSize:
|
||||||
|
return "Exceeds maximum size, "
|
||||||
|
"change data exporting settings to download.";
|
||||||
|
case SkipReason::FileType:
|
||||||
|
return "Not included, "
|
||||||
|
"change data exporting settings to download.";
|
||||||
|
case SkipReason::None:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Unexpected("Skip reason in NoFileDescription.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CalculateThumbSize(
|
||||||
|
int maxWidth,
|
||||||
|
int maxHeight,
|
||||||
|
int minWidth,
|
||||||
|
int minHeight,
|
||||||
|
bool expandForRetina = false) {
|
||||||
|
return [=](QSize largeSize) {
|
||||||
|
const auto multiplier = (expandForRetina ? 2 : 1);
|
||||||
|
const auto checkWidth = largeSize.width() * multiplier;
|
||||||
|
const auto checkHeight = largeSize.height() * multiplier;
|
||||||
|
const auto smallSize = (checkWidth > maxWidth
|
||||||
|
|| checkHeight > maxHeight)
|
||||||
|
? largeSize.scaled(
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
Qt::KeepAspectRatio)
|
||||||
|
: largeSize;
|
||||||
|
const auto retinaSize = QSize(
|
||||||
|
smallSize.width() & ~0x01,
|
||||||
|
smallSize.height() & ~0x01);
|
||||||
|
return (retinaSize.width() < kPhotoMinWidth
|
||||||
|
|| retinaSize.height() < kPhotoMinHeight)
|
||||||
|
? QSize()
|
||||||
|
: retinaSize;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray SerializeString(const QByteArray &value) {
|
QByteArray SerializeString(const QByteArray &value) {
|
||||||
const auto size = value.size();
|
const auto size = value.size();
|
||||||
const auto begin = value.data();
|
const auto begin = value.data();
|
||||||
|
@ -517,11 +570,6 @@ public:
|
||||||
const QString &basePath,
|
const QString &basePath,
|
||||||
const PeersMap &peers,
|
const PeersMap &peers,
|
||||||
const QString &internalLinksDomain);
|
const QString &internalLinksDomain);
|
||||||
[[nodiscard]] QByteArray pushMedia(
|
|
||||||
const Data::Message &message,
|
|
||||||
const QString &basePath,
|
|
||||||
const PeersMap &peers,
|
|
||||||
const QString &internalLinksDomain);
|
|
||||||
|
|
||||||
[[nodiscard]] Result writeBlock(const QByteArray &block);
|
[[nodiscard]] Result writeBlock(const QByteArray &block);
|
||||||
|
|
||||||
|
@ -554,6 +602,24 @@ private:
|
||||||
const QString &basePath,
|
const QString &basePath,
|
||||||
const PeersMap &peers,
|
const PeersMap &peers,
|
||||||
const QString &internalLinksDomain) const;
|
const QString &internalLinksDomain) const;
|
||||||
|
[[nodiscard]] QByteArray pushMedia(
|
||||||
|
const Data::Message &message,
|
||||||
|
const QString &basePath,
|
||||||
|
const PeersMap &peers,
|
||||||
|
const QString &internalLinksDomain);
|
||||||
|
[[nodiscard]] QByteArray pushGenericMedia(const MediaData &data);
|
||||||
|
[[nodiscard]] QByteArray pushStickerMedia(
|
||||||
|
const Data::Document &data,
|
||||||
|
const QString &basePath);
|
||||||
|
[[nodiscard]] QByteArray pushAnimatedMedia(
|
||||||
|
const Data::Document &data,
|
||||||
|
const QString &basePath);
|
||||||
|
[[nodiscard]] QByteArray pushVideoFileMedia(
|
||||||
|
const Data::Document &data,
|
||||||
|
const QString &basePath);
|
||||||
|
[[nodiscard]] QByteArray pushPhotoMedia(
|
||||||
|
const Data::Photo &data,
|
||||||
|
const QString &basePath);
|
||||||
|
|
||||||
File _file;
|
File _file;
|
||||||
bool _closed = false;
|
bool _closed = false;
|
||||||
|
@ -1137,9 +1203,29 @@ QByteArray HtmlWriter::Wrap::pushMedia(
|
||||||
basePath,
|
basePath,
|
||||||
peers,
|
peers,
|
||||||
internalLinksDomain);
|
internalLinksDomain);
|
||||||
if (data.classes.isEmpty()) {
|
if (!data.classes.isEmpty()) {
|
||||||
return QByteArray();
|
return pushGenericMedia(data);
|
||||||
}
|
}
|
||||||
|
const auto &content = message.media.content;
|
||||||
|
if (const auto document = base::get_if<Data::Document>(&content)) {
|
||||||
|
Assert(!message.media.ttl);
|
||||||
|
if (document->isSticker) {
|
||||||
|
return pushStickerMedia(*document, basePath);
|
||||||
|
} else if (document->isAnimated) {
|
||||||
|
return pushAnimatedMedia(*document, basePath);
|
||||||
|
} else if (document->isVideoFile) {
|
||||||
|
return pushVideoFileMedia(*document, basePath);
|
||||||
|
}
|
||||||
|
Unexpected("Non generic document in HtmlWriter::Wrap::pushMedia.");
|
||||||
|
} else if (const auto photo = base::get_if<Data::Photo>(&content)) {
|
||||||
|
Assert(!message.media.ttl);
|
||||||
|
return pushPhotoMedia(*photo, basePath);
|
||||||
|
}
|
||||||
|
Assert(!content.has_value());
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HtmlWriter::Wrap::pushGenericMedia(const MediaData &data) {
|
||||||
auto result = pushDiv("media_wrap clearfix");
|
auto result = pushDiv("media_wrap clearfix");
|
||||||
if (data.link.isEmpty()) {
|
if (data.link.isEmpty()) {
|
||||||
result.append(pushDiv("media clearfix pull_left " + data.classes));
|
result.append(pushDiv("media clearfix pull_left " + data.classes));
|
||||||
|
@ -1189,6 +1275,222 @@ QByteArray HtmlWriter::Wrap::pushMedia(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray HtmlWriter::Wrap::pushStickerMedia(
|
||||||
|
const Data::Document &data,
|
||||||
|
const QString &basePath) {
|
||||||
|
using namespace Data;
|
||||||
|
|
||||||
|
const auto [thumb, size] = WriteImageThumb(
|
||||||
|
basePath,
|
||||||
|
data.file.relativePath,
|
||||||
|
CalculateThumbSize(
|
||||||
|
kStickerMaxWidth,
|
||||||
|
kStickerMaxHeight,
|
||||||
|
kStickerMinWidth,
|
||||||
|
kStickerMinHeight),
|
||||||
|
"PNG",
|
||||||
|
-1);
|
||||||
|
if (thumb.isEmpty()) {
|
||||||
|
auto generic = MediaData();
|
||||||
|
generic.title = "Sticker";
|
||||||
|
generic.status = data.stickerEmoji;
|
||||||
|
if (data.file.relativePath.isEmpty()) {
|
||||||
|
generic.status += ", " + FormatFileSize(data.file.size);
|
||||||
|
} else {
|
||||||
|
generic.link = data.file.relativePath;
|
||||||
|
}
|
||||||
|
generic.description = NoFileDescription(data.file.skipReason);
|
||||||
|
generic.classes = "media_photo";
|
||||||
|
return pushGenericMedia(generic);
|
||||||
|
}
|
||||||
|
auto result = pushDiv("media_wrap clearfix");
|
||||||
|
result.append(pushTag("a", {
|
||||||
|
{ "class", "sticker_wrap clearfix pull_left" },
|
||||||
|
{
|
||||||
|
"href",
|
||||||
|
relativePath(data.file.relativePath).toUtf8()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
const auto sizeStyle = "width: "
|
||||||
|
+ NumberToString(size.width() / 2)
|
||||||
|
+ "px; height: "
|
||||||
|
+ NumberToString(size.height() / 2)
|
||||||
|
+ "px";
|
||||||
|
result.append(pushTag("img", {
|
||||||
|
{ "class", "sticker" },
|
||||||
|
{ "style", sizeStyle },
|
||||||
|
{ "src", relativePath(thumb).toUtf8() },
|
||||||
|
{ "empty", "" }
|
||||||
|
}));
|
||||||
|
result.append(popTag());
|
||||||
|
result.append(popTag());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HtmlWriter::Wrap::pushAnimatedMedia(
|
||||||
|
const Data::Document &data,
|
||||||
|
const QString &basePath) {
|
||||||
|
using namespace Data;
|
||||||
|
|
||||||
|
auto size = QSize(data.width, data.height);
|
||||||
|
auto thumbSize = CalculateThumbSize(
|
||||||
|
kPhotoMaxWidth,
|
||||||
|
kPhotoMaxHeight,
|
||||||
|
kPhotoMinWidth,
|
||||||
|
kPhotoMinHeight,
|
||||||
|
true)(size);
|
||||||
|
if (data.thumb.file.relativePath.isEmpty()
|
||||||
|
|| data.file.relativePath.isEmpty()
|
||||||
|
|| !thumbSize.width()
|
||||||
|
|| !thumbSize.height()) {
|
||||||
|
auto generic = MediaData();
|
||||||
|
generic.title = "Animation";
|
||||||
|
generic.status = FormatFileSize(data.file.size);
|
||||||
|
generic.link = data.file.relativePath;
|
||||||
|
generic.description = NoFileDescription(data.file.skipReason);
|
||||||
|
generic.classes = "media_video";
|
||||||
|
return pushGenericMedia(generic);
|
||||||
|
}
|
||||||
|
auto result = pushDiv("media_wrap clearfix");
|
||||||
|
result.append(pushTag("a", {
|
||||||
|
{ "class", "animated_wrap clearfix pull_left" },
|
||||||
|
{
|
||||||
|
"href",
|
||||||
|
relativePath(data.file.relativePath).toUtf8()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
result.append(pushDiv("video_play_bg"));
|
||||||
|
result.append(pushDiv("gif_play"));
|
||||||
|
result.append("GIF");
|
||||||
|
result.append(popTag());
|
||||||
|
result.append(popTag());
|
||||||
|
const auto sizeStyle = "width: "
|
||||||
|
+ NumberToString(thumbSize.width() / 2)
|
||||||
|
+ "px; height: "
|
||||||
|
+ NumberToString(thumbSize.height() / 2)
|
||||||
|
+ "px";
|
||||||
|
result.append(pushTag("img", {
|
||||||
|
{ "class", "animated" },
|
||||||
|
{ "style", sizeStyle },
|
||||||
|
{ "src", relativePath(data.thumb.file.relativePath).toUtf8() },
|
||||||
|
{ "empty", "" }
|
||||||
|
}));
|
||||||
|
result.append(popTag());
|
||||||
|
result.append(popTag());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HtmlWriter::Wrap::pushVideoFileMedia(
|
||||||
|
const Data::Document &data,
|
||||||
|
const QString &basePath) {
|
||||||
|
using namespace Data;
|
||||||
|
|
||||||
|
auto size = QSize(data.width, data.height);
|
||||||
|
auto thumbSize = CalculateThumbSize(
|
||||||
|
kPhotoMaxWidth,
|
||||||
|
kPhotoMaxHeight,
|
||||||
|
kPhotoMinWidth,
|
||||||
|
kPhotoMinHeight,
|
||||||
|
true)(size);
|
||||||
|
if (data.thumb.file.relativePath.isEmpty()
|
||||||
|
|| data.file.relativePath.isEmpty()
|
||||||
|
|| !thumbSize.width()
|
||||||
|
|| !thumbSize.height()) {
|
||||||
|
auto generic = MediaData();
|
||||||
|
generic.title = "Video file";
|
||||||
|
generic.status = FormatDuration(data.duration);
|
||||||
|
if (data.file.relativePath.isEmpty()) {
|
||||||
|
generic.status += ", " + FormatFileSize(data.file.size);
|
||||||
|
} else {
|
||||||
|
generic.link = data.file.relativePath;
|
||||||
|
}
|
||||||
|
generic.description = NoFileDescription(data.file.skipReason);
|
||||||
|
generic.classes = "media_video";
|
||||||
|
return pushGenericMedia(generic);
|
||||||
|
}
|
||||||
|
auto result = pushDiv("media_wrap clearfix");
|
||||||
|
result.append(pushTag("a", {
|
||||||
|
{ "class", "video_file_wrap clearfix pull_left" },
|
||||||
|
{
|
||||||
|
"href",
|
||||||
|
relativePath(data.file.relativePath).toUtf8()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
result.append(pushDiv("video_play_bg"));
|
||||||
|
result.append(pushDiv("video_play"));
|
||||||
|
result.append(popTag());
|
||||||
|
result.append(popTag());
|
||||||
|
result.append(pushDiv("video_duration"));
|
||||||
|
result.append(FormatDuration(data.duration));
|
||||||
|
result.append(popTag());
|
||||||
|
const auto sizeStyle = "width: "
|
||||||
|
+ NumberToString(thumbSize.width() / 2)
|
||||||
|
+ "px; height: "
|
||||||
|
+ NumberToString(thumbSize.height() / 2)
|
||||||
|
+ "px";
|
||||||
|
result.append(pushTag("img", {
|
||||||
|
{ "class", "video_file" },
|
||||||
|
{ "style", sizeStyle },
|
||||||
|
{ "src", relativePath(data.thumb.file.relativePath).toUtf8() },
|
||||||
|
{ "empty", "" }
|
||||||
|
}));
|
||||||
|
result.append(popTag());
|
||||||
|
result.append(popTag());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HtmlWriter::Wrap::pushPhotoMedia(
|
||||||
|
const Data::Photo &data,
|
||||||
|
const QString &basePath) {
|
||||||
|
using namespace Data;
|
||||||
|
|
||||||
|
const auto [thumb, size] = WriteImageThumb(
|
||||||
|
basePath,
|
||||||
|
data.image.file.relativePath,
|
||||||
|
CalculateThumbSize(
|
||||||
|
kPhotoMaxWidth,
|
||||||
|
kPhotoMaxHeight,
|
||||||
|
kPhotoMinWidth,
|
||||||
|
kPhotoMinHeight));
|
||||||
|
if (thumb.isEmpty()) {
|
||||||
|
auto generic = MediaData();
|
||||||
|
generic.title = "Photo";
|
||||||
|
generic.status = NumberToString(data.image.width)
|
||||||
|
+ "x"
|
||||||
|
+ NumberToString(data.image.height);
|
||||||
|
if (data.image.file.relativePath.isEmpty()) {
|
||||||
|
generic.status += ", " + FormatFileSize(data.image.file.size);
|
||||||
|
} else {
|
||||||
|
generic.link = data.image.file.relativePath;
|
||||||
|
}
|
||||||
|
generic.description = NoFileDescription(data.image.file.skipReason);
|
||||||
|
generic.classes = "media_photo";
|
||||||
|
return pushGenericMedia(generic);
|
||||||
|
}
|
||||||
|
auto result = pushDiv("media_wrap clearfix");
|
||||||
|
result.append(pushTag("a", {
|
||||||
|
{ "class", "photo_wrap clearfix pull_left" },
|
||||||
|
{
|
||||||
|
"href",
|
||||||
|
relativePath(data.image.file.relativePath).toUtf8()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
const auto sizeStyle = "width: "
|
||||||
|
+ NumberToString(size.width() / 2)
|
||||||
|
+ "px; height: "
|
||||||
|
+ NumberToString(size.height() / 2)
|
||||||
|
+ "px";
|
||||||
|
result.append(pushTag("img", {
|
||||||
|
{ "class", "photo" },
|
||||||
|
{ "style", sizeStyle },
|
||||||
|
{ "src", relativePath(thumb).toUtf8() },
|
||||||
|
{ "empty", "" }
|
||||||
|
}));
|
||||||
|
result.append(popTag());
|
||||||
|
result.append(popTag());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
MediaData HtmlWriter::Wrap::prepareMediaData(
|
MediaData HtmlWriter::Wrap::prepareMediaData(
|
||||||
const Data::Message &message,
|
const Data::Message &message,
|
||||||
const QString &basePath,
|
const QString &basePath,
|
||||||
|
@ -1222,50 +1524,64 @@ MediaData HtmlWriter::Wrap::prepareMediaData(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
message.media.content.match([&](const Photo &photo) {
|
message.media.content.match([&](const Photo &data) {
|
||||||
// #TODO export: photo + self destruct (ttl)
|
if (message.media.ttl) {
|
||||||
result.title = "Photo";
|
result.title = "Self-destructing photo";
|
||||||
result.status = NumberToString(photo.image.width)
|
result.status = data.id
|
||||||
+ "x"
|
? "Please view it on your mobile"
|
||||||
+ NumberToString(photo.image.height);
|
: "Expired";
|
||||||
result.classes = "media_file"; // #TODO export
|
result.classes = "media_photo";
|
||||||
result.link = photo.image.file.relativePath;
|
return;
|
||||||
|
}
|
||||||
|
// At least try to pushPhotoMedia.
|
||||||
}, [&](const Document &data) {
|
}, [&](const Document &data) {
|
||||||
// #TODO export: sticker + thumb (video, video message) + self destruct (ttl)
|
if (message.media.ttl) {
|
||||||
|
result.title = "Self-destructing video";
|
||||||
|
result.status = data.id
|
||||||
|
? "Please view it on your mobile"
|
||||||
|
: "Expired";
|
||||||
|
result.classes = "media_video";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto hasFile = !data.file.relativePath.isEmpty();
|
||||||
result.link = data.file.relativePath;
|
result.link = data.file.relativePath;
|
||||||
|
result.description = NoFileDescription(data.file.skipReason);
|
||||||
if (data.isSticker) {
|
if (data.isSticker) {
|
||||||
result.title = "Sticker";
|
// At least try to pushStickerMedia.
|
||||||
result.status = data.stickerEmoji;
|
|
||||||
result.classes = "media_file"; // #TODO export
|
|
||||||
} else if (data.isVideoMessage) {
|
} else if (data.isVideoMessage) {
|
||||||
result.title = "Video message";
|
result.title = "Video message";
|
||||||
result.status = FormatDuration(data.duration);
|
result.status = FormatDuration(data.duration);
|
||||||
|
if (!hasFile) {
|
||||||
|
result.status += ", " + FormatFileSize(data.file.size);
|
||||||
|
}
|
||||||
result.thumb = data.thumb.file.relativePath;
|
result.thumb = data.thumb.file.relativePath;
|
||||||
result.classes = "media_file";
|
result.classes = "media_video";
|
||||||
} else if (data.isVoiceMessage) {
|
} else if (data.isVoiceMessage) {
|
||||||
result.title = "Voice message";
|
result.title = "Voice message";
|
||||||
result.status = FormatDuration(data.duration);
|
result.status = FormatDuration(data.duration);
|
||||||
|
if (!hasFile) {
|
||||||
|
result.status += ", " + FormatFileSize(data.file.size);
|
||||||
|
}
|
||||||
result.classes = "media_voice_message";
|
result.classes = "media_voice_message";
|
||||||
} else if (data.isAnimated) {
|
} else if (data.isAnimated) {
|
||||||
result.title = "Animation";
|
// At least try to pushAnimatedMedia.
|
||||||
result.status = FormatFileSize(data.duration);
|
|
||||||
result.classes = "media_file"; // #TODO export
|
|
||||||
} else if (data.isVideoFile) {
|
} else if (data.isVideoFile) {
|
||||||
result.title = "Video file";
|
// At least try to pushVideoFileMedia.
|
||||||
result.status = FormatDuration(data.duration);
|
|
||||||
result.classes = "media_file"; // #TODO export
|
|
||||||
} else if (data.isAudioFile) {
|
} else if (data.isAudioFile) {
|
||||||
result.title = (data.songPerformer.isEmpty()
|
result.title = (data.songPerformer.isEmpty()
|
||||||
|| data.songTitle.isEmpty())
|
|| data.songTitle.isEmpty())
|
||||||
? QByteArray("Audio file")
|
? QByteArray("Audio file")
|
||||||
: data.songPerformer + " \xe2\x80\x93 " + data.songTitle;
|
: data.songPerformer + " \xe2\x80\x93 " + data.songTitle;
|
||||||
result.status = FormatDuration(data.duration);
|
result.status = FormatDuration(data.duration);
|
||||||
|
if (!hasFile) {
|
||||||
|
result.status += ", " + FormatFileSize(data.file.size);
|
||||||
|
}
|
||||||
result.classes = "media_audio_file";
|
result.classes = "media_audio_file";
|
||||||
} else {
|
} else {
|
||||||
result.title = data.name.isEmpty()
|
result.title = data.name.isEmpty()
|
||||||
? QByteArray("File")
|
? QByteArray("File")
|
||||||
: data.name;
|
: data.name;
|
||||||
result.status = FormatFileSize(data.duration);
|
result.status = FormatFileSize(data.file.size);
|
||||||
result.classes = "media_file";
|
result.classes = "media_file";
|
||||||
}
|
}
|
||||||
}, [&](const SharedContact &data) {
|
}, [&](const SharedContact &data) {
|
||||||
|
@ -1434,7 +1750,9 @@ Result HtmlWriter::start(
|
||||||
"images/media_game.png",
|
"images/media_game.png",
|
||||||
"images/media_location.png",
|
"images/media_location.png",
|
||||||
"images/media_music.png",
|
"images/media_music.png",
|
||||||
|
"images/media_photo.png",
|
||||||
"images/media_shop.png",
|
"images/media_shop.png",
|
||||||
|
"images/media_video.png",
|
||||||
"images/media_voice.png",
|
"images/media_voice.png",
|
||||||
"images/section_calls.png",
|
"images/section_calls.png",
|
||||||
"images/section_chats.png",
|
"images/section_chats.png",
|
||||||
|
|
Loading…
Reference in New Issue