Support new ('modern') API file locations.

This commit is contained in:
John Preston 2019-03-22 18:19:43 +04:00
parent eba2a98703
commit aa8f62da9d
41 changed files with 947 additions and 566 deletions

View File

@ -89,9 +89,8 @@ constexpr auto kFeedReadTimeout = crl::time(1000);
constexpr auto kStickersByEmojiInvalidateTimeout = crl::time(60 * 60 * 1000);
constexpr auto kNotifySettingSaveTimeout = crl::time(1000);
using SimpleFileLocationId = Data::SimpleFileLocationId;
using PhotoFileLocationId = Data::PhotoFileLocationId;
using DocumentFileLocationId = Data::DocumentFileLocationId;
using FileLocationId = Data::FileLocationId;
using UpdatedFileReferences = Data::UpdatedFileReferences;
bool IsSilentPost(not_null<HistoryItem*> item, bool silent) {
@ -2840,7 +2839,13 @@ void ApiWrap::requestFileReference(
&origin);
if (documentId) {
_session->data().document(
*documentId
documentId->id
)->refreshFileReference(reference);
}
const auto photoId = base::get_if<PhotoFileLocationId>(&origin);
if (photoId) {
_session->data().photo(
photoId->id
)->refreshFileReference(reference);
}
}
@ -2857,7 +2862,7 @@ void ApiWrap::requestFileReference(
auto handlers = std::move(i->second);
_fileReferenceHandlers.erase(i);
for (auto &handler : handlers) {
handler(Data::UpdatedFileReferences());
handler(UpdatedFileReferences());
}
}).send();
}
@ -2868,7 +2873,7 @@ void ApiWrap::refreshFileReference(
int requestId,
const QByteArray &current) {
return refreshFileReference(origin, crl::guard(loader, [=](
const Data::UpdatedFileReferences &data) {
const UpdatedFileReferences &data) {
loader->refreshFileReferenceFrom(data, requestId, current);
}));
}
@ -2894,7 +2899,7 @@ void ApiWrap::refreshFileReference(
}
};
const auto fail = [&] {
handler(Data::UpdatedFileReferences());
handler(UpdatedFileReferences());
};
origin.data.match([&](Data::FileOriginMessage data) {
if (const auto item = App::histItemById(data)) {
@ -2924,22 +2929,7 @@ void ApiWrap::refreshFileReference(
fail();
}
}, [&](Data::FileOriginPeerPhoto data) {
if (const auto peer = _session->data().peer(data.peerId)) {
if (const auto user = peer->asUser()) {
request(MTPusers_GetUsers(
MTP_vector<MTPInputUser>(1, user->inputUser)));
} else if (const auto chat = peer->asChat()) {
request(MTPmessages_GetChats(
MTP_vector<MTPint>(1, chat->inputChat)));
} else if (const auto channel = peer->asChannel()) {
request(MTPchannels_GetChannels(
MTP_vector<MTPInputChannel>(1, channel->inputChannel)));
} else {
fail();
}
} else {
fail();
}
fail();
}, [&](Data::FileOriginStickerSet data) {
if (data.setId == Stickers::CloudRecentSetId
|| data.setId == Stickers::RecentSetId) {
@ -3152,7 +3142,7 @@ void ApiWrap::toggleFavedSticker(
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
const auto current = document->fileReference();
auto refreshed = [=](const Data::UpdatedFileReferences &data) {
auto refreshed = [=](const UpdatedFileReferences &data) {
if (document->fileReference() != current) {
performRequest();
}
@ -3190,7 +3180,7 @@ void ApiWrap::toggleSavedGif(
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
const auto current = document->fileReference();
auto refreshed = [=](const Data::UpdatedFileReferences &data) {
auto refreshed = [=](const UpdatedFileReferences &data) {
if (document->fileReference() != current) {
performRequest();
}
@ -4937,7 +4927,7 @@ void ApiWrap::sendExistingDocument(
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
const auto current = document->fileReference();
auto refreshed = [=](const Data::UpdatedFileReferences &data) {
auto refreshed = [=](const UpdatedFileReferences &data) {
if (document->fileReference() != current) {
performRequest();
} else {

View File

@ -228,100 +228,6 @@ namespace App {
return feedMsgs(msgs.v, type);
}
ImagePtr image(const MTPPhotoSize &size) {
switch (size.type()) {
case mtpc_photoSize: {
auto &d = size.c_photoSize();
if (d.vlocation.type() == mtpc_fileLocation) {
auto &l = d.vlocation.c_fileLocation();
return Images::Create(
StorageImageLocation(
d.vw.v,
d.vh.v,
l.vdc_id.v,
l.vvolume_id.v,
l.vlocal_id.v,
l.vsecret.v,
l.vfile_reference.v),
d.vsize.v);
}
} break;
case mtpc_photoCachedSize: {
auto &d = size.c_photoCachedSize();
if (d.vlocation.type() == mtpc_fileLocation) {
auto &l = d.vlocation.c_fileLocation();
auto bytes = qba(d.vbytes);
return Images::Create(
StorageImageLocation(
d.vw.v,
d.vh.v,
l.vdc_id.v,
l.vvolume_id.v,
l.vlocal_id.v,
l.vsecret.v,
l.vfile_reference.v),
bytes);
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
const auto bytes = qba(d.vbytes);
if (auto image = App::readImage(bytes); !image.isNull()) {
return Images::Create(std::move(image), "JPG");
}
}
} break;
case mtpc_photoStrippedSize: {
const auto &d = size.c_photoStrippedSize();
auto bytes = qba(d.vbytes);
if (bytes.size() >= 3 && bytes[0] == '\x01') {
const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
"\x3f\x00";
const char footer[] = "\xff\xd9";
auto real = QByteArray(header, sizeof(header) - 1);
real[164] = bytes[1];
real[166] = bytes[2];
bytes = real + bytes.mid(3) + QByteArray::fromRawData(footer, sizeof(footer) - 1);
if (auto image = App::readImage(bytes); !image.isNull()) {
return Images::Create(std::move(image), "JPG");
}
}
} break;
}
return ImagePtr();
}
void feedInboxRead(const PeerId &peer, MsgId upTo) {
if (const auto history = Auth().data().historyLoaded(peer)) {
history->inboxRead(upTo);

View File

@ -77,8 +77,6 @@ namespace App {
void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds);
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
ImagePtr image(const MTPPhotoSize &size);
[[nodiscard]] QString peerName(const PeerData *peer, bool forDialogs = false);
[[nodiscard]] HistoryItem *histItemById(ChannelId channelId, MsgId itemId);

View File

@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_controller.h"
#include "storage/localstorage.h"
#include "data/data_session.h"
#include "data/data_photo.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
@ -802,24 +803,17 @@ ConfirmInviteBox::ConfirmInviteBox(
}();
_title->setText(title);
_status->setText(status);
if (data.vphoto.type() == mtpc_chatPhoto) {
const auto &photo = data.vphoto.c_chatPhoto();
const auto size = 160;
const auto location = StorageImageLocation::FromMTP(
size,
size,
photo.vphoto_small);
if (!location.isNull()) {
_photo = Images::Create(location);
if (!_photo->loaded()) {
subscribe(Auth().downloaderTaskFinished(), [=] {
update();
});
_photo->load(Data::FileOrigin());
}
const auto photo = Auth().data().processPhoto(data.vphoto);
if (!photo->isNull()) {
_photo = photo->thumbnail();
if (!_photo->loaded()) {
subscribe(Auth().downloaderTaskFinished(), [=] {
update();
});
_photo->load(Data::FileOrigin());
}
}
if (!_photo) {
} else {
_photoEmpty = std::make_unique<Ui::EmptyUserpic>(
Data::PeerUserpicColor(0),
title);

View File

@ -211,7 +211,7 @@ private:
Fn<void()> _submit;
object_ptr<Ui::FlatLabel> _title;
object_ptr<Ui::FlatLabel> _status;
ImagePtr _photo;
Image *_photo = nullptr;
std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
std::vector<not_null<UserData*>> _participants;
bool _isChannel = false;

View File

@ -225,7 +225,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
}
});
}
data.vset.match([&](const MTPDstickerSet & set) {
data.vset.match([&](const MTPDstickerSet &set) {
_setTitle = Stickers::GetSetTitle(set);
_setShortName = qs(set.vshort_name);
_setId = set.vid.v;
@ -237,7 +237,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
? set.vinstalled_date.v
: TimeId(0);
_setThumbnail = set.has_thumb()
? App::image(set.vthumb)
? Images::Create(set, set.vthumb)
: ImagePtr();
auto &sets = Auth().data().stickerSetsRef();
const auto it = sets.find(_setId);

View File

@ -90,7 +90,8 @@ bool ApplyArchivedResultFake() {
MTP_long(set.access),
MTP_string(set.title),
MTP_string(set.shortName),
MTP_photoSizeEmpty(MTP_string("a")),
MTP_photoSizeEmpty(MTP_string(QString())),
MTP_int(0),
MTP_int(set.count),
MTP_int(set.hash));
sets.push_back(MTP_stickerSetCovered(
@ -595,7 +596,7 @@ void FeaturedSetsReceived(
? set->vinstalled_date.v
: TimeId(0);
const auto thumbnail = set->has_thumb()
? App::image(set->vthumb)
? Images::Create(*set, set->vthumb)
: ImagePtr();
if (it == sets.cend()) {
auto setClientFlags = MTPDstickerSet_ClientFlag::f_featured
@ -885,7 +886,7 @@ Set *FeedSet(const MTPDstickerSet &set) {
set.vhash.v,
set.vflags.v | MTPDstickerSet_ClientFlag::f_not_loaded,
set.has_installed_date() ? set.vinstalled_date.v : TimeId(0),
set.has_thumb() ? App::image(set.vthumb) : ImagePtr()));
set.has_thumb() ? Images::Create(set, set.vthumb) : ImagePtr()));
} else {
it->access = set.vaccess_hash.v;
it->title = title;
@ -901,7 +902,7 @@ Set *FeedSet(const MTPDstickerSet &set) {
? (set.vinstalled_date.v ? set.vinstalled_date.v : unixtime())
: TimeId(0);
it->thumbnail = set.has_thumb()
? App::image(set.vthumb)
? Images::Create(set, set.vthumb)
: ImagePtr();
if (it->count != set.vcount.v
|| it->hash != set.vhash.v

View File

@ -64,12 +64,11 @@ void ChannelData::setPhoto(const MTPChatPhoto &photo) {
}
void ChannelData::setPhoto(PhotoId photoId, const MTPChatPhoto &photo) {
if (photo.type() == mtpc_chatPhoto) {
const auto &data = photo.c_chatPhoto();
updateUserpic(photoId, data.vphoto_small);
} else {
photo.match([&](const MTPDchatPhoto & data) {
updateUserpic(photoId, data.vdc_id.v, data.vphoto_small);
}, [&](const MTPDchatPhotoEmpty &) {
clearUserpic();
}
});
}
void ChannelData::setName(const QString &newName, const QString &newUsername) {

View File

@ -31,12 +31,11 @@ void ChatData::setPhoto(const MTPChatPhoto &photo) {
}
void ChatData::setPhoto(PhotoId photoId, const MTPChatPhoto &photo) {
if (photo.type() == mtpc_chatPhoto) {
const auto &data = photo.c_chatPhoto();
updateUserpic(photoId, data.vphoto_small);
} else {
photo.match([&](const MTPDchatPhoto &data) {
updateUserpic(photoId, data.vdc_id.v, data.vphoto_small);
}, [&](const MTPDchatPhotoEmpty &) {
clearUserpic();
}
});
}
auto ChatData::DefaultAdminRights() -> AdminRights {

View File

@ -1199,7 +1199,8 @@ auto DocumentData::createStreamingLoader(Data::FileOrigin origin) const
MTP_inputDocumentFileLocation(
MTP_long(id),
MTP_long(_access),
MTP_bytes(_fileReference)),
MTP_bytes(_fileReference),
MTP_string(QString())),
size,
origin)
: nullptr;
@ -1229,6 +1230,10 @@ QByteArray DocumentData::fileReference() const {
void DocumentData::refreshFileReference(const QByteArray &value) {
_fileReference = value;
_thumbnail->refreshFileReference(value);
if (const auto data = sticker()) {
data->loc.refreshFileReference(value);
}
}
void DocumentData::refreshStickerThumbFileReference() {

View File

@ -212,7 +212,7 @@ int GoodThumbSource::loadOffset() {
}
const StorageImageLocation &GoodThumbSource::location() {
return StorageImageLocation::Null;
return StorageImageLocation::Invalid();
}
void GoodThumbSource::refreshFileReference(const QByteArray &data) {

View File

@ -17,37 +17,18 @@ struct FileReferenceAccumulator {
push(item);
}
}
void push(const MTPFileLocation &data) {
data.match([&](const MTPDfileLocation &data) {
result.data.emplace(SimpleFileLocationId(
data.vvolume_id.v,
data.vdc_id.v,
data.vlocal_id.v), data.vfile_reference.v);
}, [](const MTPDfileLocationUnavailable &data) {
});
}
void push(const MTPPhotoSize &data) {
data.match([](const MTPDphotoSizeEmpty &) {
}, [](const MTPDphotoStrippedSize &) {
}, [&](const auto &data) {
push(data.vlocation);
});
}
void push(const MTPPhoto &data) {
data.match([&](const MTPDphoto &data) {
for (const auto &size : data.vsizes.v) {
push(size);
}
result.data.emplace(
PhotoFileLocationId{ data.vid.v },
data.vfile_reference.v);
}, [](const MTPDphotoEmpty &data) {
});
}
void push(const MTPDocument &data) {
data.match([&](const MTPDdocument &data) {
for (const auto &thumb : data.vthumbs.v) {
push(thumb);
}
result.data.emplace(
DocumentFileLocationId(data.vid.v),
DocumentFileLocationId{ data.vid.v },
data.vfile_reference.v);
}, [](const MTPDdocumentEmpty &data) {
});
@ -57,36 +38,6 @@ struct FileReferenceAccumulator {
push(data.vdocument);
});
}
void push(const MTPUserProfilePhoto &data) {
data.match([&](const MTPDuserProfilePhoto &data) {
push(data.vphoto_small);
push(data.vphoto_big);
}, [](const MTPDuserProfilePhotoEmpty &data) {
});
}
void push(const MTPChatPhoto &data) {
data.match([&](const MTPDchatPhoto &data) {
push(data.vphoto_small);
push(data.vphoto_big);
}, [](const MTPDchatPhotoEmpty &data) {
});
}
void push(const MTPUser &data) {
data.match([&](const MTPDuser &data) {
if (data.has_photo()) {
push(data.vphoto);
}
}, [](const MTPDuserEmpty &data) {
});
}
void push(const MTPChat &data) {
data.match([](const MTPDchatEmpty &data) {
}, [](const MTPDchannelForbidden &data) {
}, [](const MTPDchatForbidden &data) {
}, [&](const auto &data) {
push(data.vphoto);
});
}
void push(const MTPWebPage &data) {
data.match([&](const MTPDwebPage &data) {
if (data.has_document()) {
@ -139,22 +90,14 @@ struct FileReferenceAccumulator {
void push(const MTPmessages_Messages &data) {
data.match([](const MTPDmessages_messagesNotModified &) {
}, [&](const auto &data) {
push(data.vusers);
push(data.vchats);
push(data.vmessages);
});
}
void push(const MTPphotos_Photos &data) {
data.match([&](const auto &data) {
push(data.vusers);
push(data.vphotos);
});
}
void push(const MTPmessages_Chats &data) {
data.match([&](const auto &data) {
push(data.vchats);
});
}
void push(const MTPmessages_RecentStickers &data) {
data.match([&](const MTPDmessages_recentStickers &data) {
push(data.vstickers);
@ -191,22 +134,6 @@ UpdatedFileReferences GetFileReferencesHelper(const Type &data) {
} // namespace
SimpleFileLocationId::SimpleFileLocationId(
uint64 volumeId,
int32 dcId,
int32 localId)
: volumeId(volumeId)
, dcId(dcId)
, localId(localId) {
}
bool operator<(
const SimpleFileLocationId &a,
const SimpleFileLocationId &b) {
return std::tie(a.volumeId, a.dcId, a.localId)
< std::tie(b.volumeId, b.dcId, b.localId);
}
UpdatedFileReferences GetFileReferences(const MTPmessages_Messages &data) {
return GetFileReferencesHelper(data);
}
@ -215,14 +142,6 @@ UpdatedFileReferences GetFileReferences(const MTPphotos_Photos &data) {
return GetFileReferencesHelper(data);
}
UpdatedFileReferences GetFileReferences(const MTPVector<MTPUser> &data) {
return GetFileReferencesHelper(data);
}
UpdatedFileReferences GetFileReferences(const MTPmessages_Chats &data) {
return GetFileReferencesHelper(data);
}
UpdatedFileReferences GetFileReferences(
const MTPmessages_RecentStickers &data) {
return GetFileReferencesHelper(data);

View File

@ -107,31 +107,32 @@ struct FileOrigin {
Variant data;
};
// Volume_id, dc_id, local_id.
struct SimpleFileLocationId {
SimpleFileLocationId(uint64 volumeId, int32 dcId, int32 localId);
uint64 volumeId = 0;
int32 dcId = 0;
int32 localId = 0;
struct DocumentFileLocationId {
uint64 id = 0;
};
bool operator<(
const SimpleFileLocationId &a,
const SimpleFileLocationId &b);
inline bool operator<(DocumentFileLocationId a, DocumentFileLocationId b) {
return a.id < b.id;
}
struct PhotoFileLocationId {
uint64 id = 0;
};
inline bool operator<(PhotoFileLocationId a, PhotoFileLocationId b) {
return a.id < b.id;
}
using DocumentFileLocationId = uint64;
using FileLocationId = base::variant<
SimpleFileLocationId,
DocumentFileLocationId>;
DocumentFileLocationId,
PhotoFileLocationId>;
struct UpdatedFileReferences {
std::map<FileLocationId, QByteArray> data;
};
UpdatedFileReferences GetFileReferences(const MTPmessages_Messages &data);
UpdatedFileReferences GetFileReferences(const MTPphotos_Photos &data);
UpdatedFileReferences GetFileReferences(const MTPVector<MTPUser> &data);
UpdatedFileReferences GetFileReferences(const MTPmessages_Chats &data);
UpdatedFileReferences GetFileReferences(
const MTPmessages_RecentStickers &data);
UpdatedFileReferences GetFileReferences(

View File

@ -362,29 +362,36 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
"or with ttl_seconds in updateSentMedia()"));
return false;
}
const auto &photo = mediaPhoto.vphoto;
parent()->history()->owner().photoConvert(_photo, photo);
parent()->history()->owner().photoConvert(_photo, mediaPhoto.vphoto);
if (photo.type() != mtpc_photo) {
if (mediaPhoto.vphoto.type() != mtpc_photo) {
return false;
}
const auto &photo = mediaPhoto.vphoto.c_photo();
struct SizeData {
char letter = 0;
MTPstring type = MTP_string(QString());
int width = 0;
int height = 0;
const MTPFileLocation *location = nullptr;
QByteArray bytes;
};
const auto saveImageToCache = [&](
not_null<Image*> image,
SizeData size) {
Expects(size.location != nullptr);
Expects(!size.type.v.isEmpty());
const auto key = StorageImageLocation(
StorageFileLocation(
photo.vdc_id.v,
_photo->session().userId(),
MTP_inputPhotoFileLocation(
photo.vid,
photo.vaccess_hash,
photo.vfile_reference,
size.type)),
size.width,
size.height,
size.location->c_fileLocation());
if (key.isNull() || image->isNull() || !image->loaded()) {
size.height);
if (!key.valid() || image->isNull() || !image->loaded()) {
return;
}
if (size.bytes.isEmpty()) {
@ -403,24 +410,22 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
image->replaceSource(
std::make_unique<Images::StorageSource>(key, length));
};
auto &sizes = photo.c_photo().vsizes.v;
auto &sizes = photo.vsizes.v;
auto max = 0;
auto maxSize = SizeData();
for (const auto &data : sizes) {
const auto size = data.match([](const MTPDphotoSize &data) {
return SizeData{
data.vtype.v.isEmpty() ? char(0) : data.vtype.v[0],
data.vtype,
data.vw.v,
data.vh.v,
&data.vlocation,
QByteArray()
};
}, [](const MTPDphotoCachedSize &data) {
return SizeData{
data.vtype.v.isEmpty() ? char(0) : data.vtype.v[0],
data.vtype,
data.vw.v,
data.vh.v,
&data.vlocation,
qba(data.vbytes)
};
}, [](const MTPDphotoSizeEmpty &) {
@ -429,25 +434,26 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
// No need to save stripped images to local cache.
return SizeData();
});
if (!size.location || size.location->type() != mtpc_fileLocation) {
const auto letter = size.type.v.isEmpty() ? char(0) : size.type.v[0];
if (!letter) {
continue;
}
if (size.letter == 's') {
if (letter == 's') {
saveImageToCache(_photo->thumbnailSmall(), size);
} else if (size.letter == 'm') {
} else if (letter == 'm') {
saveImageToCache(_photo->thumbnail(), size);
} else if (size.letter == 'x' && max < 1) {
} else if (letter == 'x' && max < 1) {
max = 1;
maxSize = size;
} else if (size.letter == 'y' && max < 2) {
} else if (letter == 'y' && max < 2) {
max = 2;
maxSize = size;
//} else if (size.letter == 'w' && max < 3) {
//} else if (letter == 'w' && max < 3) {
// max = 3;
// maxSize = size;
}
}
if (maxSize.location) {
if (!maxSize.type.v.isEmpty()) {
saveImageToCache(_photo->large(), maxSize);
}
return true;

View File

@ -310,11 +310,24 @@ Data::FileOrigin PeerData::userpicPhotoOrigin() const {
void PeerData::updateUserpic(
PhotoId photoId,
MTP::DcId dcId,
const MTPFileLocation &location) {
const auto size = kUserpicSize;
const auto loc = StorageImageLocation::FromMTP(size, size, location);
const auto photo = loc.isNull() ? ImagePtr() : Images::Create(loc);
setUserpicChecked(photoId, loc, photo);
const auto loc = location.match([&](
const MTPDfileLocationToBeDeprecated &deprecated) {
return StorageImageLocation(
StorageFileLocation(
dcId,
session().userId(),
MTP_inputPeerPhotoFileLocation(
MTP_flags(0),
input,
deprecated.vvolume_id,
deprecated.vlocal_id)),
size,
size);
});
setUserpicChecked(photoId, loc, Images::Create(loc));
}
void PeerData::clearUserpic() {

View File

@ -303,7 +303,10 @@ protected:
const QString &newName,
const QString &newNameOrPhone,
const QString &newUsername);
void updateUserpic(PhotoId photoId, const MTPFileLocation &location);
void updateUserpic(
PhotoId photoId,
MTP::DcId dcId,
const MTPFileLocation &location);
void clearUserpic();
private:

View File

@ -135,11 +135,33 @@ Image *PhotoData::getReplyPreview(Data::FileOrigin origin) {
return _replyPreview.image();
}
void PhotoData::setRemoteLocation(
int32 dc,
uint64 access,
const QByteArray &fileReference) {
_fileReference = fileReference;
if (_dc != dc || _access != access) {
_dc = dc;
_access = access;
}
}
MTPInputPhoto PhotoData::mtpInput() const {
return MTP_inputPhoto(
MTP_long(id),
MTP_long(access),
MTP_bytes(fileReference));
MTP_long(_access),
MTP_bytes(_fileReference));
}
QByteArray PhotoData::fileReference() const {
return _fileReference;
}
void PhotoData::refreshFileReference(const QByteArray &value) {
_fileReference = value;
_thumbnailSmall->refreshFileReference(value);
_thumbnail->refreshFileReference(value);
_large->refreshFileReference(value);
}
void PhotoData::collectLocalData(not_null<PhotoData*> local) {
@ -206,7 +228,7 @@ void PhotoData::updateImages(
if (!was) {
was = now;
} else if (was->isDelayedStorageImage()) {
if (const auto location = now->location(); !location.isNull()) {
if (const auto location = now->location(); location.valid()) {
was->setDelayedStorageLocation(
Data::FileOrigin(),
location);

View File

@ -42,7 +42,13 @@ public:
void unload();
[[nodiscard]] Image *getReplyPreview(Data::FileOrigin origin);
void setRemoteLocation(
int32 dc,
uint64 access,
const QByteArray &fileReference);
[[nodiscard]] MTPInputPhoto mtpInput() const;
[[nodiscard]] QByteArray fileReference() const;
void refreshFileReference(const QByteArray &value);
// When we have some client-side generated photo
// (for example for displaying an external inline bot result)
@ -72,8 +78,6 @@ public:
ImagePtr large);
PhotoId id = 0;
uint64 access = 0;
QByteArray fileReference;
TimeId date = 0;
bool hasSticker = false;
@ -88,6 +92,9 @@ private:
ImagePtr _thumbnail;
ImagePtr _large;
int32 _dc = 0;
uint64 _access = 0;
QByteArray _fileReference;
Data::ReplyPreview _replyPreview;
not_null<Data::Session*> _owner;

View File

@ -1700,6 +1700,7 @@ not_null<PhotoData*> Session::processPhoto(
data.vaccess_hash.v,
data.vfile_reference.v,
data.vdate.v,
data.vdc_id.v,
data.is_has_stickers(),
thumbnailInline,
thumbnailSmall,
@ -1715,6 +1716,7 @@ not_null<PhotoData*> Session::photo(
const uint64 &access,
const QByteArray &fileReference,
TimeId date,
int32 dc,
bool hasSticker,
const ImagePtr &thumbnailInline,
const ImagePtr &thumbnailSmall,
@ -1726,6 +1728,7 @@ not_null<PhotoData*> Session::photo(
access,
fileReference,
date,
dc,
hasSticker,
thumbnailInline,
thumbnailSmall,
@ -1790,6 +1793,7 @@ PhotoData *Session::photoFromWeb(
uint64(0),
QByteArray(),
unixtime(),
0,
false,
thumbnailInline,
thumbnailSmall,
@ -1828,7 +1832,7 @@ void Session::photoApplyFields(
};
const auto image = [&](const QByteArray &levels) {
const auto i = find(levels);
return (i == sizes.end()) ? ImagePtr() : App::image(*i);
return (i == sizes.end()) ? ImagePtr() : Images::Create(data, *i);
};
const auto thumbnailInline = image(InlineLevels);
const auto thumbnailSmall = image(SmallLevels);
@ -1840,6 +1844,7 @@ void Session::photoApplyFields(
data.vaccess_hash.v,
data.vfile_reference.v,
data.vdate.v,
data.vdc_id.v,
data.is_has_stickers(),
thumbnailInline,
thumbnailSmall,
@ -1853,6 +1858,7 @@ void Session::photoApplyFields(
const uint64 &access,
const QByteArray &fileReference,
TimeId date,
int32 dc,
bool hasSticker,
const ImagePtr &thumbnailInline,
const ImagePtr &thumbnailSmall,
@ -1861,8 +1867,7 @@ void Session::photoApplyFields(
if (!date) {
return;
}
photo->access = access;
photo->fileReference = fileReference;
photo->setRemoteLocation(dc, access, fileReference);
photo->date = date;
photo->hasSticker = hasSticker;
photo->updateImages(
@ -2061,7 +2066,8 @@ void Session::documentApplyFields(
not_null<DocumentData*> document,
const MTPDdocument &data) {
const auto thumbnailInline = FindDocumentInlineThumbnail(data);
const auto thumbnail = FindDocumentThumbnail(data);
const auto thumbnailSize = FindDocumentThumbnail(data);
const auto thumbnail = Images::Create(data, thumbnailSize);
documentApplyFields(
document,
data.vaccess_hash.v,
@ -2069,11 +2075,11 @@ void Session::documentApplyFields(
data.vdate.v,
data.vattributes.v,
qs(data.vmime_type),
App::image(thumbnailInline),
App::image(thumbnail),
Images::Create(data, thumbnailInline),
thumbnail,
data.vdc_id.v,
data.vsize.v,
StorageImageLocation::FromMTP(thumbnail));
thumbnail->location());
}
void Session::documentApplyFields(
@ -2101,8 +2107,8 @@ void Session::documentApplyFields(
document->size = size;
document->recountIsImage();
if (document->sticker()
&& document->sticker()->loc.isNull()
&& !thumbLocation.isNull()) {
&& !document->sticker()->loc.valid()
&& thumbLocation.valid()) {
document->sticker()->loc = thumbLocation;
}
}

View File

@ -347,6 +347,7 @@ public:
const uint64 &access,
const QByteArray &fileReference,
TimeId date,
int32 dc,
bool hasSticker,
const ImagePtr &thumbnailInline,
const ImagePtr &thumbnailSmall,
@ -580,6 +581,7 @@ private:
const uint64 &access,
const QByteArray &fileReference,
TimeId date,
int32 dc,
bool hasSticker,
const ImagePtr &thumbnailInline,
const ImagePtr &thumbnailSmall,

View File

@ -62,11 +62,7 @@ Storage::Cache::Key DocumentThumbCacheKey(int32 dcId, uint64 id) {
}
Storage::Cache::Key StorageCacheKey(const StorageImageLocation &location) {
const auto dcId = uint64(location.dc()) & 0xFFULL;
return Storage::Cache::Key{
Data::kStorageCacheTag | (dcId << 32) | uint32(location.local()),
location.volume()
};
return location.file().cacheKey();
}
Storage::Cache::Key WebDocumentCacheKey(const WebFileLocation &location) {

View File

@ -79,7 +79,7 @@ void UserData::setContactStatus(ContactStatus status) {
void UserData::setPhoto(const MTPUserProfilePhoto &photo) {
if (photo.type() == mtpc_userProfilePhoto) {
const auto &data = photo.c_userProfilePhoto();
updateUserpic(data.vphoto_id.v, data.vphoto_small);
updateUserpic(data.vphoto_id.v, data.vdc_id.v, data.vphoto_small);
} else {
clearUserpic();
}

View File

@ -238,7 +238,7 @@ void WebPageData::replaceDocumentGoodThumbnail() {
return;
}
const auto &location = photo->large()->location();
if (!location.isNull()) {
if (location.valid()) {
document->replaceGoodThumbnail(
std::make_unique<Images::StorageSource>(
location,

View File

@ -203,36 +203,14 @@ Utf8String FillLeft(const Utf8String &data, int length, char filler) {
return result;
}
FileLocation ParseLocation(const MTPFileLocation &data) {
return data.match([](const MTPDfileLocation &data) {
return FileLocation{
data.vdc_id.v,
MTP_inputFileLocation(
data.vvolume_id,
data.vlocal_id,
data.vsecret,
data.vfile_reference)
};
}, [](const MTPDfileLocationUnavailable &data) {
return FileLocation{
0,
MTP_inputFileLocation(
data.vvolume_id,
data.vlocal_id,
data.vsecret,
MTP_bytes(QByteArray()))
};
});
}
Image ParseMaxImage(
const MTPVector<MTPPhotoSize> &data,
const MTPDphoto &photo,
const QString &suggestedPath) {
auto result = Image();
result.file.suggestedPath = suggestedPath;
auto maxArea = int64(0);
for (const auto &size : data.v) {
for (const auto &size : photo.vsizes.v) {
size.match([](const MTPDphotoSizeEmpty &) {
}, [](const MTPDphotoStrippedSize &) {
// Max image size should not be a stripped image.
@ -241,7 +219,13 @@ Image ParseMaxImage(
if (area > maxArea) {
result.width = data.vw.v;
result.height = data.vh.v;
result.file.location = ParseLocation(data.vlocation);
result.file.location = FileLocation{
photo.vdc_id.v,
MTP_inputPhotoFileLocation(
photo.vid,
photo.vaccess_hash,
photo.vfile_reference,
data.vtype) };
if constexpr (MTPDphotoCachedSize::Is<decltype(data)>()) {
result.file.content = data.vbytes.v;
result.file.size = result.file.content.size();
@ -261,7 +245,7 @@ Photo ParsePhoto(const MTPPhoto &data, const QString &suggestedPath) {
data.match([&](const MTPDphoto &data) {
result.id = data.vid.v;
result.date = data.vdate.v;
result.image = ParseMaxImage(data.vsizes, suggestedPath);
result.image = ParseMaxImage(data, suggestedPath);
}, [&](const MTPDphotoEmpty &data) {
result.id = data.vid.v;
});
@ -407,7 +391,7 @@ QString DocumentFolder(const Document &data) {
}
Image ParseDocumentThumb(
const QVector<MTPPhotoSize> &thumbs,
const MTPDdocument &document,
const QString &documentPath) {
const auto area = [](const MTPPhotoSize &size) {
return size.match([](const MTPDphotoSizeEmpty &) {
@ -418,6 +402,7 @@ Image ParseDocumentThumb(
return data.vw.v * data.vh.v;
});
};
const auto &thumbs = document.vthumbs.v;
const auto i = ranges::max_element(thumbs, ranges::less(), area);
if (i == thumbs.end()) {
return Image();
@ -430,7 +415,13 @@ Image ParseDocumentThumb(
auto result = Image();
result.width = data.vw.v;
result.height = data.vh.v;
result.file.location = ParseLocation(data.vlocation);
result.file.location = FileLocation{
document.vdc_id.v,
MTP_inputDocumentFileLocation(
document.vid,
document.vaccess_hash,
document.vfile_reference,
data.vtype) };
if constexpr (MTPDphotoCachedSize::Is<decltype(data)>()) {
result.file.content = data.vbytes.v;
result.file.size = result.file.content.size();
@ -460,13 +451,14 @@ Document ParseDocument(
result.file.location.data = MTP_inputDocumentFileLocation(
data.vid,
data.vaccess_hash,
data.vfile_reference);
data.vfile_reference,
MTP_string(QString()));
result.file.suggestedPath = suggestedFolder
+ DocumentFolder(result) + '/'
+ CleanDocumentName(ComputeDocumentName(context, result, date));
result.thumb = ParseDocumentThumb(
data.vthumbs.v,
data,
result.file.suggestedPath);
}, [&](const MTPDdocumentEmpty &data) {
result.id = data.vid.v;

View File

@ -103,40 +103,6 @@ TextWithEntities ExtractEditedText(const MTPMessage &message) {
return { text, entities };
}
PhotoData *GenerateChatPhoto(
ChannelId channelId,
uint64 logEntryId,
TimeId date,
const MTPDchatPhoto &photo) {
// We try to make a unique photoId that will stay the same for each pair (channelId, logEntryId).
static const auto RandomIdPart = rand_value<uint64>();
auto mixinIdPart = (static_cast<uint64>(static_cast<uint32>(channelId)) << 32) ^ logEntryId;
auto photoId = RandomIdPart ^ mixinIdPart;
const auto fileReference = [&]() -> const MTPbytes * {
const auto takeFrom = [](const MTPFileLocation &location) {
return (location.type() == mtpc_fileLocation)
? &location.c_fileLocation().vfile_reference
: nullptr;
};
if (const auto result = takeFrom(photo.vphoto_big)) {
return result;
}
return takeFrom(photo.vphoto_small);
}();
auto photoSizes = QVector<MTPPhotoSize>();
photoSizes.reserve(2);
photoSizes.push_back(MTP_photoSize(MTP_string("a"), photo.vphoto_small, MTP_int(160), MTP_int(160), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("c"), photo.vphoto_big, MTP_int(640), MTP_int(640), MTP_int(0)));
return Auth().data().processPhoto(MTP_photo(
MTP_flags(0),
MTP_long(photoId),
MTP_long(0),
fileReference ? (*fileReference) : MTP_bytes(QByteArray()),
MTP_int(date),
MTP_vector<MTPPhotoSize>(photoSizes)));
}
const auto CollectChanges = [](auto &phraseMap, auto plusFlags, auto minusFlags) {
auto withPrefix = [&phraseMap](auto flags, QChar prefix) {
auto result = QString();
@ -444,18 +410,14 @@ void GenerateItems(
};
auto createChangePhoto = [&](const MTPDchannelAdminLogEventActionChangePhoto &action) {
switch (action.vnew_photo.type()) {
case mtpc_chatPhoto: {
auto photo = GenerateChatPhoto(channel->bareId(), id, date, action.vnew_photo.c_chatPhoto());
action.vnew_photo.match([&](const MTPDphoto &data) {
auto photo = Auth().data().processPhoto(data);
auto text = (channel->isMegagroup() ? lng_admin_log_changed_photo_group : lng_admin_log_changed_photo_channel)(lt_from, fromLinkText);
addSimpleServiceMessage(text, photo);
} break;
case mtpc_chatPhotoEmpty: {
}, [&](const MTPDphotoEmpty &data) {
auto text = (channel->isMegagroup() ? lng_admin_log_removed_photo_group : lng_admin_log_removed_photo_channel)(lt_from, fromLinkText);
addSimpleServiceMessage(text);
} break;
default: Unexpected("ChatPhoto type in createChangePhoto()");
}
});
};
auto createToggleInvites = [&](const MTPDchannelAdminLogEventActionToggleInvites &action) {

View File

@ -1057,11 +1057,11 @@ void History::applyServiceChanges(
case mtpc_messageActionChatEditPhoto: {
auto &d = action.c_messageActionChatEditPhoto();
if (d.vphoto.type() == mtpc_photo) {
auto &sizes = d.vphoto.c_photo().vsizes.v;
d.vphoto.match([&](const MTPDphoto &data) {
const auto &sizes = data.vsizes.v;
if (!sizes.isEmpty()) {
auto photo = _owner->processPhoto(d.vphoto.c_photo());
if (photo) photo->peer = peer;
auto photo = _owner->processPhoto(data);
photo->peer = peer;
auto &smallSize = sizes.front();
auto &bigSize = sizes.back();
const MTPFileLocation *smallLoc = 0, *bigLoc = 0;
@ -1074,16 +1074,21 @@ void History::applyServiceChanges(
case mtpc_photoCachedSize: bigLoc = &bigSize.c_photoCachedSize().vlocation; break;
}
if (smallLoc && bigLoc) {
const auto newPhotoId = photo ? photo->id : PhotoId();
if (const auto chat = peer->asChat()) {
chat->setPhoto(newPhotoId, MTP_chatPhoto(*smallLoc, *bigLoc));
chat->setPhoto(photo->id, MTP_chatPhoto(*smallLoc, *bigLoc, data.vdc_id));
} else if (const auto channel = peer->asChannel()) {
channel->setPhoto(newPhotoId, MTP_chatPhoto(*smallLoc, *bigLoc));
channel->setPhoto(photo->id, MTP_chatPhoto(*smallLoc, *bigLoc, data.vdc_id));
}
peer->loadUserpic();
}
}
}
}, [&](const MTPDphotoEmpty &data) {
if (const auto chat = peer->asChat()) {
chat->setPhoto(MTP_chatPhotoEmpty());
} else if (const auto channel = peer->asChannel()) {
channel->setPhoto(MTP_chatPhotoEmpty());
}
});
} break;
case mtpc_messageActionChatEditTitle: {

View File

@ -240,16 +240,16 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, crl
}
p.setOpacity(radialOpacity);
auto icon = ([radial, this, selected]() -> const style::icon* {
auto icon = [&]() -> const style::icon* {
if (radial || _data->loading()) {
if (_data->uploading()
|| !_data->large()->location().isNull()) {
|| _data->large()->location().valid()) {
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
}
return nullptr;
}
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
})();
}();
if (icon) {
icon->paintInCenter(p, inner);
}
@ -311,7 +311,7 @@ TextState HistoryPhoto::textState(QPoint point, StateRequest request) const {
} else if (_data->loaded()) {
result.link = _openl;
} else if (_data->loading()) {
if (!_data->large()->location().isNull()) {
if (_data->large()->location().valid()) {
result.link = _cancell;
}
} else {
@ -414,7 +414,8 @@ void HistoryPhoto::drawGrouped(
if (_data->waitingForAlbum()) {
return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting);
} else if (radial || _data->loading()) {
if (_data->uploading() || !_data->large()->location().isNull()) {
if (_data->uploading()
|| _data->large()->location().valid()) {
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
}
return nullptr;
@ -459,9 +460,9 @@ TextState HistoryPhoto::getStateGrouped(
: _data->loaded()
? _openl
: _data->loading()
? (_data->large()->location().isNull()
? ClickHandlerPtr()
: _cancell)
? (_data->large()->location().valid()
? _cancell
: nullptr)
: _savel);
}

View File

@ -156,7 +156,8 @@ void LoaderMtproto::requestFailed(
}
const auto callback = [=](const Data::UpdatedFileReferences &updated) {
_location.match([&](const MTPDinputDocumentFileLocation &location) {
const auto i = updated.data.find(location.vid.v);
const auto i = updated.data.find(
Data::DocumentFileLocationId{ location.vid.v });
if (i == end(updated.data)) {
return fail();
}
@ -167,7 +168,8 @@ void LoaderMtproto::requestFailed(
_location = MTP_inputDocumentFileLocation(
MTP_long(location.vid.v),
MTP_long(location.vaccess_hash.v),
MTP_bytes(reference));
MTP_bytes(reference),
MTP_string(QString()));
}
if (!_requests.take(offset)) {
// Request with such offset was already cancelled.

View File

@ -72,7 +72,8 @@ std::optional<DedicatedLoader::File> ParseFile(
const auto location = MTP_inputDocumentFileLocation(
fields.vid,
fields.vaccess_hash,
fields.vfile_reference);
fields.vfile_reference,
MTP_string(QString()));
return DedicatedLoader::File{ name, size, fields.vdc_id.v, location };
}

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "core/application.h"
@ -540,7 +541,7 @@ mtpFileLoader::mtpFileLoader(
fromCloud,
autoLoading,
cacheTag)
, _dcId(location->dc())
, _dcId(location->file().dcId())
, _location(location)
, _origin(origin) {
auto shiftedDcId = MTP::downloadDcId(_dcId, 0);
@ -645,25 +646,22 @@ void mtpFileLoader::refreshFileReferenceFrom(
const Data::UpdatedFileReferences &updates,
int requestId,
const QByteArray &current) {
const auto updated = [&] {
if (_location) {
const auto i = updates.data.find(Data::SimpleFileLocationId(
_location->volume(),
_location->dc(),
_location->local()));
return (i == end(updates.data)) ? QByteArray() : i->second;
}
const auto i = updates.data.find(_id);
return (i == end(updates.data)) ? QByteArray() : i->second;
}();
if (updated.isEmpty() || updated == current) {
cancel(true);
return;
}
if (_location) {
_location->refreshFileReference(updated);
_location->refreshFileReference(updates);
if (_location->fileReference() == current) {
cancel(true);
return;
}
} else {
_fileReference = updated;
const auto i = updates.data.find(
Data::DocumentFileLocationId{ _id });
if (i != end(updates.data) && !i->second.isEmpty()) {
_fileReference = i->second;
}
if (_fileReference == current) {
cancel(true);
return;
}
}
const auto offset = finishSentRequestGetOffset(requestId);
makeRequest(offset);
@ -774,11 +772,7 @@ void mtpFileLoader::makeRequest(int offset) {
MTPInputFileLocation mtpFileLoader::computeLocation() const {
if (_location) {
return MTP_inputFileLocation(
MTP_long(_location->volume()),
MTP_int(_location->local()),
MTP_long(_location->secret()),
MTP_bytes(_location->fileReference()));
return _location->file().tl(Auth().userId());
} else if (_locationType == SecureFileLocation) {
return MTP_inputSecureFileLocation(
MTP_long(_id),
@ -787,7 +781,8 @@ MTPInputFileLocation mtpFileLoader::computeLocation() const {
return MTP_inputDocumentFileLocation(
MTP_long(_id),
MTP_long(_accessHash),
MTP_bytes(_fileReference));
MTP_bytes(_fileReference),
MTP_string(QString()));
}
void mtpFileLoader::requestMoreCdnFileHashes() {

View File

@ -65,7 +65,7 @@ PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) {
: std::move(original);
result.mtpSize = MTP_photoSize(
MTP_string(""),
MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(result.image.width()),
MTP_int(result.image.height()),
MTP_int(0));
@ -192,10 +192,7 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) {
const auto push = [&](const char *type, QImage &&image) {
photoSizes.push_back(MTP_photoSize(
MTP_string(type),
MTP_fileLocationUnavailable(
MTP_long(0),
MTP_int(0),
MTP_long(0)),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0)));
photoThumbs.emplace(type[0], std::move(image));
@ -211,7 +208,8 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) {
MTP_long(0),
MTP_bytes(QByteArray()),
MTP_int(unixtime()),
MTP_vector<MTPPhotoSize>(photoSizes));
MTP_vector<MTPPhotoSize>(photoSizes),
MTP_int(MTP::maindc()));
QString file, filename;
int32 filesize = 0;
@ -252,10 +250,7 @@ SendMediaReady PrepareWallPaper(const QImage &image) {
const auto push = [&](const char *type, QImage &&image) {
sizes.push_back(MTP_photoSize(
MTP_string(type),
MTP_fileLocationUnavailable(
MTP_long(0),
MTP_int(0),
MTP_long(0)),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0)));
thumbnails.emplace(type[0], std::move(image));
@ -861,15 +856,15 @@ void FileLoadTask::process() {
} else if (_type != SendMediaType::File) {
auto thumb = (w > 100 || h > 100) ? fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.emplace('s', thumb);
photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.emplace('m', medium);
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.emplace('y', full);
photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)));
{
QBuffer buffer(&filedata);
@ -882,7 +877,8 @@ void FileLoadTask::process() {
MTP_long(0),
MTP_bytes(QByteArray()),
MTP_int(unixtime()),
MTP_vector<MTPPhotoSize>(photoSizes));
MTP_vector<MTPPhotoSize>(photoSizes),
MTP_int(MTP::maindc()));
if (filesize < 0) {
filesize = _result->filesize = filedata.size();

View File

@ -3483,8 +3483,7 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr,
qint32 version = 0;
stickers.stream >> versionTag >> version;
if (versionTag != kStickersVersionTag
|| version <= 0
|| version > kStickersSerializeVersion) {
|| version != kStickersSerializeVersion) {
// Old data, without sticker set thumbnails.
return failed();
}
@ -3496,6 +3495,8 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr,
return failed();
}
for (auto i = 0; i != count; ++i) {
using LocationType = StorageFileLocation::Type;
quint64 setId = 0, setAccess = 0;
QString setTitle, setShortName;
qint32 scnt = 0;
@ -3514,11 +3515,19 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr,
>> setHash
>> setFlagsValue
>> setInstallDate;
setThumbnail = Serialize::readStorageImageLocation(
const auto thumbnail = Serialize::readStorageImageLocation(
stickers.version,
stickers.stream);
if (!_checkStreamStatus(stickers.stream)) {
if (!thumbnail || !_checkStreamStatus(stickers.stream)) {
return failed();
} else if (thumbnail->valid()
&& thumbnail->type() == LocationType::Legacy) {
setThumbnail = thumbnail->convertToModern(
LocationType::StickerSetThumb,
setId,
setAccess);
} else {
setThumbnail = *thumbnail;
}
setFlags = MTPDstickerSet::Flags::from_raw(setFlagsValue);
@ -3551,7 +3560,7 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr,
setHash,
MTPDstickerSet::Flags(setFlags),
setInstallDate,
setThumbnail.isNull() ? ImagePtr() : Images::Create(setThumbnail)));
Images::Create(setThumbnail)));
}
auto &set = it.value();
auto inputSet = MTP_inputStickerSetID(MTP_long(set.id), MTP_long(set.access));

View File

@ -15,49 +15,67 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
namespace Serialize {
namespace {
void writeStorageImageLocation(
QDataStream &stream,
const StorageImageLocation &location) {
stream
<< qint32(location.width())
<< qint32(location.height())
<< qint32(location.dc())
<< quint64(location.volume())
<< qint32(location.local())
<< quint64(location.secret());
stream << location.fileReference();
}
constexpr auto kModernImageLocationTag = std::numeric_limits<qint32>::min();
StorageImageLocation readStorageImageLocation(
} // namespace
std::optional<StorageImageLocation> readLegacyStorageImageLocationOrTag(
int streamAppVersion,
QDataStream &stream) {
qint32 width, height, dc, local;
quint64 volume, secret;
QByteArray fileReference;
stream >> width >> height >> dc >> volume >> local >> secret;
stream >> width;
if (width == kModernImageLocationTag) {
return std::nullopt;
}
stream >> height >> dc >> volume >> local >> secret;
if (streamAppVersion >= 1003013) {
stream >> fileReference;
}
if (stream.status() != QDataStream::Ok) {
return std::nullopt;
}
return StorageImageLocation(
StorageFileLocation(
dc,
UserId(0),
MTP_inputFileLocation(
MTP_long(volume),
MTP_int(local),
MTP_long(secret),
MTP_bytes(fileReference))),
width,
height,
dc,
volume,
local,
secret,
fileReference);
height);
}
int storageImageLocationSize(const StorageImageLocation &location) {
// width + height + dc + volume + local + secret + fileReference
return sizeof(qint32)
+ sizeof(qint32)
+ sizeof(qint32)
+ sizeof(quint64)
+ sizeof(qint32)
+ sizeof(quint64)
+ bytearraySize(location.fileReference());
// Modern image location tag + (size + content) of the serialization.
return sizeof(qint32) * 2 + location.serializeSize();
}
void writeStorageImageLocation(
QDataStream &stream,
const StorageImageLocation &location) {
stream << kModernImageLocationTag << location.serialize();
}
std::optional<StorageImageLocation> readStorageImageLocation(
int streamAppVersion,
QDataStream &stream) {
const auto legacy = readLegacyStorageImageLocationOrTag(
streamAppVersion,
stream);
if (legacy) {
return legacy;
}
auto serialized = QByteArray();
stream >> serialized;
return (stream.status() == QDataStream::Ok)
? StorageImageLocation::FromSerialized(serialized)
: std::nullopt;
}
uint32 peerSize(not_null<PeerData*> peer) {
@ -151,14 +169,15 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
return nullptr;
}
auto photoLoc = readStorageImageLocation(
streamAppVersion,
stream);
const auto userpic = readStorageImageLocation(streamAppVersion, stream);
auto userpicAccessHash = uint64(0);
if (!userpic) {
return nullptr;
}
PeerData *result = Auth().data().peerLoaded(peerId);
bool wasLoaded = (result != nullptr);
if (!wasLoaded) {
result = Auth().data().peer(peerId);
const auto loaded = Auth().data().peerLoaded(peerId);
const auto result = loaded ? loaded : Auth().data().peer(peerId).get();
if (!loaded) {
result->loadedStatus = PeerData::FullLoaded;
}
if (const auto user = result->asUser()) {
@ -174,6 +193,8 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
}
stream >> onlineTill >> contact >> botInfoVersion;
userpicAccessHash = access;
const auto showPhone = !user->isServiceUser()
&& (user->id != Auth().userPeerId())
&& (contact <= 0);
@ -181,7 +202,7 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
? App::formatPhone(phone)
: QString();
if (!wasLoaded) {
if (!loaded) {
user->setPhone(phone);
user->setName(first, last, pname, username);
@ -223,7 +244,7 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
if (oldForbidden) {
flags |= quint32(MTPDchat_ClientFlag::f_forbidden);
}
if (!wasLoaded) {
if (!loaded) {
chat->setName(name);
chat->count = count;
chat->date = date;
@ -245,10 +266,13 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
qint32 date, version, oldForbidden;
quint32 flags;
stream >> name >> access >> date >> version >> oldForbidden >> flags >> inviteLink;
userpicAccessHash = access;
if (oldForbidden) {
flags |= quint32(MTPDchannel_ClientFlag::f_forbidden);
}
if (!wasLoaded) {
if (!loaded) {
channel->setName(name, QString());
channel->access = access;
channel->date = date;
@ -264,11 +288,16 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) {
channel->inputChannel = MTP_inputChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access));
}
}
if (!wasLoaded) {
result->setUserpic(
photoId,
photoLoc,
photoLoc.isNull() ? ImagePtr() : Images::Create(photoLoc));
if (!loaded) {
using LocationType = StorageFileLocation::Type;
const auto location = (userpic->valid()
&& userpic->type() == LocationType::Legacy)
? userpic->convertToModern(
LocationType::PeerPhoto,
result->id,
userpicAccessHash)
: *userpic;
result->setUserpic(photoId, location, Images::Create(location));
}
return result;
}
@ -277,13 +306,12 @@ QString peekUserPhone(int streamAppVersion, QDataStream &stream) {
quint64 peerId = 0, photoId = 0;
stream >> peerId >> photoId;
DEBUG_LOG(("peekUserPhone.id: %1").arg(peerId));
if (!peerId || !peerIsUser(peerId)) {
if (!peerId
|| !peerIsUser(peerId)
|| !readStorageImageLocation(streamAppVersion, stream)) {
return QString();
}
const auto photoLoc = readStorageImageLocation(
streamAppVersion,
stream);
QString first, last, phone;
stream >> first >> last >> phone;
DEBUG_LOG(("peekUserPhone.data: %1 %2 %3"

View File

@ -84,13 +84,16 @@ inline int dateTimeSize() {
return (sizeof(qint64) + sizeof(quint32) + sizeof(qint8));
}
int storageImageLocationSize(const StorageImageLocation &location);
void writeStorageImageLocation(
QDataStream &stream,
const StorageImageLocation &location);
StorageImageLocation readStorageImageLocation(
// NB! This method can return StorageFileLocation with Type::Generic!
// The reader should discard it or convert to one of the valid modern types.
std::optional<StorageImageLocation> readStorageImageLocation(
int streamAppVersion,
QDataStream &stream);
int storageImageLocationSize(const StorageImageLocation &location);
template <typename T>
inline T read(QDataStream &stream) {

View File

@ -81,7 +81,7 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
}
qint32 duration = -1;
StorageImageLocation thumb;
std::optional<StorageImageLocation> thumb;
if (type == StickerDocument) {
QString alt;
qint32 typeOfSet;
@ -131,9 +131,14 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
}
}
if (!dc && !access) {
if ((!dc && !access) || !thumb) {
return nullptr;
}
using LocationType = StorageFileLocation::Type;
const auto location = (thumb->valid()
&& thumb->type() == LocationType::Legacy)
? thumb->convertToModern(LocationType::Document, id, access)
: *thumb;
return Auth().data().document(
id,
access,
@ -142,10 +147,10 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
attributes,
mime,
ImagePtr(),
thumb.isNull() ? ImagePtr() : Images::Create(thumb),
Images::Create(location),
dc,
size,
thumb);
location);
}
DocumentData *Document::readStickerFromStream(int streamAppVersion, QDataStream &stream, const StickerSetInfo &info) {

View File

@ -162,6 +162,9 @@ ImagePtr Create(int width, int height) {
}
ImagePtr Create(const StorageImageLocation &location, int size) {
if (!location.valid()) {
return ImagePtr();
}
const auto key = inMemoryKey(location);
const auto i = StorageImages.find(key);
const auto found = (i != end(StorageImages));
@ -198,6 +201,131 @@ ImagePtr Create(
return ImagePtr(image);
}
template <typename CreateLocation>
ImagePtr CreateFromPhotoSize(
CreateLocation &&createLocation,
const MTPPhotoSize &size) {
return size.match([&](const MTPDphotoSize &data) {
const auto &location = data.vlocation.c_fileLocationToBeDeprecated();
return Create(
StorageImageLocation(
createLocation(data.vtype, location),
data.vw.v,
data.vh.v),
data.vsize.v);
}, [&](const MTPDphotoCachedSize &data) {
const auto bytes = qba(data.vbytes);
const auto &location = data.vlocation.c_fileLocationToBeDeprecated();
return Create(
StorageImageLocation(
createLocation(data.vtype, location),
data.vw.v,
data.vh.v),
bytes);
}, [&](const MTPDphotoStrippedSize &data) {
const auto bytes = qba(data.vbytes);
if (bytes.size() < 3 || bytes[0] != '\x01') {
return ImagePtr();
}
const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
"\x3f\x00";
const char footer[] = "\xff\xd9";
auto real = QByteArray(header, sizeof(header) - 1);
real[164] = bytes[1];
real[166] = bytes[2];
const auto ready = real
+ bytes.mid(3)
+ QByteArray::fromRawData(footer, sizeof(footer) - 1);
auto image = App::readImage(ready);
return !image.isNull()
? Images::Create(std::move(image), "JPG")
: ImagePtr();
}, [&](const MTPDphotoSizeEmpty &) {
return ImagePtr();
});
}
ImagePtr Create(const MTPDstickerSet &set, const MTPPhotoSize &size) {
const auto create = [&](
const MTPstring &thumbSize,
const MTPDfileLocationToBeDeprecated &location) {
return StorageFileLocation(
set.vthumb_dc_id.v,
Auth().userId(),
MTP_inputStickerSetThumb(
MTP_inputStickerSetID(set.vid, set.vaccess_hash),
location.vvolume_id,
location.vlocal_id));
};
return CreateFromPhotoSize(create, size);
}
ImagePtr Create(const MTPDphoto &photo, const MTPPhotoSize &size) {
const auto create = [&](
const MTPstring &thumbSize,
const MTPDfileLocationToBeDeprecated &location) {
return StorageFileLocation(
photo.vdc_id.v,
Auth().userId(),
MTP_inputPhotoFileLocation(
photo.vid,
photo.vaccess_hash,
photo.vfile_reference,
thumbSize));
};
return CreateFromPhotoSize(create, size);
}
ImagePtr Create(const MTPDdocument &document, const MTPPhotoSize &size) {
const auto create = [&](
const MTPstring &thumbSize,
const MTPDfileLocationToBeDeprecated &location) {
return StorageFileLocation(
document.vdc_id.v,
Auth().userId(),
MTP_inputDocumentFileLocation(
document.vid,
document.vaccess_hash,
document.vfile_reference,
thumbSize));
};
return CreateFromPhotoSize(create, size);
}
QSize getImageSize(const QVector<MTPDocumentAttribute> &attributes) {
for (const auto &attribute : attributes) {
if (attribute.type() == mtpc_documentAttributeImageSize) {

View File

@ -30,6 +30,9 @@ ImagePtr Create(const StorageImageLocation &location, int size = 0);
ImagePtr Create( // photoCachedSize
const StorageImageLocation &location,
const QByteArray &bytes);
ImagePtr Create(const MTPDstickerSet &set, const MTPPhotoSize &size);
ImagePtr Create(const MTPDphoto &photo, const MTPPhotoSize &size);
ImagePtr Create(const MTPDdocument &document, const MTPPhotoSize &size);
ImagePtr Create(const MTPWebDocument &location);
ImagePtr Create(const MTPWebDocument &location, QSize box);
ImagePtr Create(

View File

@ -9,6 +9,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "platform/platform_specific.h"
#include "storage/cache/storage_cache_types.h"
#include "storage/serialize_common.h"
#include "data/data_file_origin.h"
#include "auth_session.h"
namespace {
MTPInputPeer GenerateInputPeer(uint64 id, uint64 accessHash, int32 self) {
const auto bareId = [&] {
return peerToBareMTPInt(id);
};
if (!id) {
return MTP_inputPeerEmpty();
} else if (id == peerFromUser(self)) {
return MTP_inputPeerSelf();
} else if (peerIsUser(id)) {
return MTP_inputPeerUser(bareId(), MTP_long(accessHash));
} else if (peerIsChat(id)) {
return MTP_inputPeerChat(bareId());
} else if (peerIsChannel(id)) {
return MTP_inputPeerChannel(bareId(), MTP_long(accessHash));
} else {
return MTP_inputPeerEmpty();
}
}
} // namespace
ImagePtr::ImagePtr() : _data(Image::Empty()) {
}
@ -29,9 +56,228 @@ ImagePtr::operator bool() const {
WebFileLocation WebFileLocation::Null;
StorageFileLocation::StorageFileLocation(
int32 dcId,
int32 self,
const MTPInputFileLocation &tl)
: _dcId(dcId) {
tl.match([&](const MTPDinputFileLocation &data) {
_type = Type::Legacy;
_volumeId = data.vvolume_id.v;
_localId = data.vlocal_id.v;
_accessHash = data.vsecret.v;
_fileReference = data.vfile_reference.v;
}, [&](const MTPDinputEncryptedFileLocation &data) {
_type = Type::Encrypted;
_id = data.vid.v;
_accessHash = data.vaccess_hash.v;
}, [&](const MTPDinputDocumentFileLocation &data) {
_type = Type::Document;
_id = data.vid.v;
_accessHash = data.vaccess_hash.v;
_fileReference = data.vfile_reference.v;
_sizeLetter = data.vthumb_size.v.isEmpty()
? uint8(0)
: uint8(data.vthumb_size.v[0]);
}, [&](const MTPDinputSecureFileLocation &data) {
_type = Type::Secure;
_id = data.vid.v;
_accessHash = data.vaccess_hash.v;
}, [&](const MTPDinputTakeoutFileLocation &data) {
_type = Type::Takeout;
}, [&](const MTPDinputPhotoFileLocation &data) {
_type = Type::Photo;
_id = data.vid.v;
_accessHash = data.vaccess_hash.v;
_fileReference = data.vfile_reference.v;
_sizeLetter = data.vthumb_size.v.isEmpty()
? char(0)
: data.vthumb_size.v[0];
}, [&](const MTPDinputPeerPhotoFileLocation &data) {
_type = Type::PeerPhoto;
data.vpeer.match([&](const MTPDinputPeerEmpty &data) {
_id = 0;
}, [&](const MTPDinputPeerSelf &data) {
_id = peerFromUser(self);
}, [&](const MTPDinputPeerChat &data) {
_id = peerFromChat(data.vchat_id);
}, [&](const MTPDinputPeerUser &data) {
_id = peerFromUser(data.vuser_id);
_accessHash = data.vaccess_hash.v;
}, [&](const MTPDinputPeerChannel &data) {
_id = peerFromChannel(data.vchannel_id);
_accessHash = data.vaccess_hash.v;
});
_volumeId = data.vvolume_id.v;
_localId = data.vlocal_id.v;
_sizeLetter = data.is_big() ? 'c' : 'a';
}, [&](const MTPDinputStickerSetThumb &data) {
_type = Type::StickerSetThumb;
data.vstickerset.match([&](const MTPDinputStickerSetEmpty &data) {
_id = 0;
}, [&](const MTPDinputStickerSetID &data) {
_id = data.vid.v;
_accessHash = data.vaccess_hash.v;
}, [&](const MTPDinputStickerSetShortName &data) {
Unexpected("inputStickerSetShortName in StorageFileLocation().");
});
_volumeId = data.vvolume_id.v;
_localId = data.vlocal_id.v;
});
}
StorageFileLocation StorageFileLocation::convertToModern(
Type type,
uint64 id,
uint64 accessHash) const {
Expects(_type == Type::Legacy);
Expects(type == Type::Document
|| type == Type::PeerPhoto
|| type == Type::StickerSetThumb);
auto result = *this;
result._type = type;
result._id = id;
result._accessHash = accessHash;
result._sizeLetter = (type == Type::PeerPhoto) ? uint8('a') : uint8(0);
return result;
}
int32 StorageFileLocation::dcId() const {
return _dcId;
}
MTPInputFileLocation StorageFileLocation::tl(int32 self) const {
switch (_type) {
case Type::Legacy:
return MTP_inputFileLocation(
MTP_long(_volumeId),
MTP_int(_localId),
MTP_long(_accessHash),
MTP_bytes(_fileReference));
case Type::Encrypted:
return MTP_inputSecureFileLocation(
MTP_long(_id),
MTP_long(_accessHash));
case Type::Document:
return MTP_inputDocumentFileLocation(
MTP_long(_id),
MTP_long(_accessHash),
MTP_bytes(_fileReference),
MTP_string(_sizeLetter
? std::string(1, char(_sizeLetter))
: std::string()));
case Type::Secure:
return MTP_inputSecureFileLocation(
MTP_long(_id),
MTP_long(_accessHash));
case Type::Takeout:
return MTP_inputTakeoutFileLocation();
case Type::Photo:
return MTP_inputPhotoFileLocation(
MTP_long(_id),
MTP_long(_accessHash),
MTP_bytes(_fileReference),
MTP_string(std::string(1, char(_sizeLetter))));
case Type::PeerPhoto:
return MTP_inputPeerPhotoFileLocation(
MTP_flags((_sizeLetter == 'c')
? MTPDinputPeerPhotoFileLocation::Flag::f_big
: MTPDinputPeerPhotoFileLocation::Flag(0)),
GenerateInputPeer(_id, _accessHash, self),
MTP_long(_volumeId),
MTP_int(_localId));
case Type::StickerSetThumb:
return MTP_inputStickerSetThumb(
MTP_inputStickerSetID(MTP_long(_id), MTP_long(_accessHash)),
MTP_long(_volumeId),
MTP_int(_localId));
}
Unexpected("Type in StorageFileLocation::tl.");
}
QByteArray StorageFileLocation::serialize() const {
auto result = QByteArray();
result.reserve(serializeSize());
if (valid()) {
auto buffer = QBuffer(&result);
buffer.open(QIODevice::WriteOnly);
auto stream = QDataStream(&buffer);
stream.setVersion(QDataStream::Qt_5_1);
stream
<< quint16(_dcId)
<< quint8(_type)
<< quint8(_sizeLetter)
<< qint32(_localId)
<< quint64(_id)
<< quint64(_accessHash)
<< quint64(_volumeId)
<< _fileReference;
}
return result;
}
int StorageFileLocation::serializeSize() const {
return valid()
? int(sizeof(uint64) * 4 + Serialize::bytearraySize(_fileReference))
: 0;
}
std::optional<StorageFileLocation> StorageFileLocation::FromSerialized(
const QByteArray &serialized) {
if (serialized.isEmpty()) {
return StorageFileLocation();
}
quint16 dcId = 0;
quint8 type = 0;
quint8 sizeLetter = 0;
qint32 localId = 0;
quint64 id = 0;
quint64 accessHash = 0;
quint64 volumeId = 0;
QByteArray fileReference;
auto stream = QDataStream(serialized);
stream.setVersion(QDataStream::Qt_5_1);
stream
>> dcId
>> type
>> sizeLetter
>> localId
>> id
>> accessHash
>> volumeId
>> fileReference;
auto result = StorageFileLocation();
result._dcId = dcId;
result._type = Type(type);
result._sizeLetter = sizeLetter;
result._localId = localId;
result._id = id;
result._accessHash = accessHash;
result._volumeId = volumeId;
result._fileReference = fileReference;
return (stream.status() == QDataStream::Ok && result.valid())
? std::make_optional(result)
: std::nullopt;
}
StorageFileLocation::Type StorageFileLocation::type() const {
return _type;
}
bool StorageFileLocation::valid() const {
switch (_type) {
case Type::General:
case Type::Legacy:
return (_dcId != 0) && (_volumeId != 0) && (_localId != 0);
case Type::Encrypted:
@ -52,33 +298,70 @@ bool StorageFileLocation::valid() const {
return false;
}
InMemoryKey StorageFileLocation::inMemoryKey() const {
Storage::Cache::Key StorageFileLocation::cacheKey() const {
using Key = Storage::Cache::Key;
// Skip '1' and '2' for legacy document cache keys.
const auto shifted = ((uint64(_type) + 3) << 8);
const auto sliced = uint64(_dcId) & 0xFFULL;
switch (_type) {
case Type::General:
case Type::Legacy:
case Type::PeerPhoto:
case Type::StickerSetThumb:
return InMemoryKey(
(uint64(_type) << 56) | (uint64(_dcId) << 40) | uint32(_localId),
_volumeId);
return Key{
shifted | sliced | (uint64(uint32(_localId)) << 16),
_volumeId };
case Type::Encrypted:
case Type::Secure:
return InMemoryKey(
(uint64(_type) << 56) | (uint64(_dcId) << 40),
_id);
return Key{ shifted | sliced, _id };
case Type::Document:
// Keep old cache keys for documents and document 'm' thumbnails.
if (_sizeLetter == 0) {
return Data::DocumentCacheKey(_dcId, _id);
//return Key{ 0x100ULL | sliced, _id };
} else if (_sizeLetter == uint8('m')) {
return Data::DocumentThumbCacheKey(_dcId, _id);
//return Key{ 0x200ULL | sliced, _id };
}
[[fallthrough]];
case Type::Photo:
return InMemoryKey(
(uint64(_type) << 56) | (uint64(_dcId) << 40) | _sizeLetter,
_id);
return Key{ shifted | sliced | (uint64(_sizeLetter) << 16), _id };
case Type::Takeout:
return InMemoryKey(
(uint64(_type) << 56),
0);
return Key{ shifted, 0 };
}
return InMemoryKey();
return Key();
}
QByteArray StorageFileLocation::fileReference() const {
return _fileReference;
}
bool StorageFileLocation::refreshFileReference(
const Data::UpdatedFileReferences &updates) {
const auto i = (_type == Type::Document)
? updates.data.find(Data::DocumentFileLocationId{ _id })
: (_type == Type::Photo)
? updates.data.find(Data::PhotoFileLocationId{ _id })
: end(updates.data);
return (i != end(updates.data))
? refreshFileReference(i->second)
: false;
}
bool StorageFileLocation::refreshFileReference(const QByteArray &data) {
if (data.isEmpty() || _fileReference == data) {
return false;
}
_fileReference = data;
return true;
}
const StorageFileLocation &StorageFileLocation::Invalid() {
static auto result = StorageFileLocation();
return result;
}
bool operator==(const StorageFileLocation &a, const StorageFileLocation &b) {
@ -95,7 +378,7 @@ bool operator==(const StorageFileLocation &a, const StorageFileLocation &b) {
using Type = StorageFileLocation::Type;
switch (type) {
case Type::General:
case Type::Legacy:
return (a._dcId == b._dcId)
&& (a._volumeId == b._volumeId)
&& (a._localId == b._localId);
@ -129,6 +412,11 @@ bool operator==(const StorageFileLocation &a, const StorageFileLocation &b) {
Unexpected("Type in StorageFileLocation::operator==.");
}
InMemoryKey inMemoryKey(const StorageFileLocation &location) {
const auto key = location.cacheKey();
return { key.high, key.low };
}
StorageImageLocation::StorageImageLocation(
const StorageFileLocation &file,
int width,
@ -138,6 +426,51 @@ StorageImageLocation::StorageImageLocation(
, _height(height) {
}
QByteArray StorageImageLocation::serialize() const {
auto result = _file.serialize();
if (!result.isEmpty() || (_width > 0) || (_height > 0)) {
result.reserve(result.size() + 2 * sizeof(qint32));
auto buffer = QBuffer(&result);
buffer.open(QIODevice::Append);
auto stream = QDataStream(&buffer);
stream.setVersion(QDataStream::Qt_5_1);
stream << qint32(_width) << qint32(_height);
}
return result;
}
int StorageImageLocation::serializeSize() const {
const auto partial = _file.serializeSize();
return (partial > 0 || _width > 0 || _height > 0)
? (partial + 2 * sizeof(qint32))
: 0;
}
std::optional<StorageImageLocation> StorageImageLocation::FromSerialized(
const QByteArray &serialized) {
if (const auto file = StorageFileLocation::FromSerialized(serialized)) {
const auto my = 2 * sizeof(qint32);
const auto full = serialized.size();
if (!full) {
return StorageImageLocation(*file, 0, 0);
} else if (full >= my) {
qint32 width = 0;
qint32 height = 0;
const auto dimensions = QByteArray::fromRawData(
serialized.data() + full - my, my);
auto stream = QDataStream(dimensions);
stream.setVersion(QDataStream::Qt_5_1);
stream >> width >> height;
return (stream.status() == QDataStream::Ok)
? StorageImageLocation(*file, width, height)
: std::optional<StorageImageLocation>();
}
}
return std::nullopt;
}
ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark)
: _bookmark(bookmark)
, _failed(_bookmark ? !_bookmark->enable() : false) {

View File

@ -9,6 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class FileLoader;
namespace Storage {
namespace Cache {
struct Key;
} // namespace Cache
} // namespace Storage
namespace Data {
struct UpdatedFileReferences;
} // namespace Data
enum LoadFromCloudSetting {
LoadFromCloudOrLocal,
LoadFromLocalOnly,
@ -21,11 +31,27 @@ enum LoadToCacheSetting {
using InMemoryKey = std::pair<uint64, uint64>;
namespace std {
template<>
struct hash<InMemoryKey> {
size_t operator()(InMemoryKey value) const {
auto seed = hash<uint64>()(value.first);
seed ^= hash<uint64>()(value.second)
+ std::size_t(0x9e3779b9)
+ (seed << 6) + (seed >> 2);
return seed;
}
};
} // namespace std
class StorageFileLocation {
public:
// Those are used in serialization, don't change.
enum class Type : uchar {
General = 0x00,
enum class Type : uint8 {
Legacy = 0x00,
Encrypted = 0x01,
Document = 0x02,
Secure = 0x03,
@ -36,26 +62,33 @@ public:
};
StorageFileLocation() = default;
StorageFileLocation(MTP::DcId dcId, const MTPInputFileLocation &tl);
StorageFileLocation(
int32 dcId,
int32 self,
const MTPInputFileLocation &tl);
[[nodiscard]] MTP::DcId dcId() const;
[[nodiscard]] MTPInputFileLocation tl() const;
[[nodiscard]] StorageFileLocation convertToModern(
Type type,
uint64 id,
uint64 accessHash) const;
[[nodiscard]] int32 dcId() const;
[[nodiscard]] MTPInputFileLocation tl(int32 self) const;
[[nodiscard]] QByteArray serialize() const;
[[nodiscard]] int serializeSize() const;
[[nodiscard]] static std::optional<StorageFileLocation> FromSerialized(
const QByteArray &serialized);
[[nodiscard]] Type type() const;
[[nodiscard]] bool valid() const;
[[nodiscard]] InMemoryKey inMemoryKey() const;
[[nodiscard]] Storage::Cache::Key cacheKey() const;
[[nodiscard]] QByteArray fileReference() const;
bool refreshFileReference(const QByteArray &data) {
if (data.isEmpty() || _fileReference == data) {
return false;
}
_fileReference = data;
return true;
}
bool refreshFileReference(const Data::UpdatedFileReferences &updates);
bool refreshFileReference(const QByteArray &data);
[[nodiscard]] static const StorageFileLocation &Invalid();
private:
friend bool operator==(
@ -63,7 +96,7 @@ private:
const StorageFileLocation &b);
uint16 _dcId = 0;
Type _type = Type::General;
Type _type = Type::Legacy;
uint8 _sizeLetter = 0;
int32 _localId = 0;
uint64 _id = 0;
@ -88,9 +121,20 @@ public:
int height);
[[nodiscard]] QByteArray serialize() const;
[[nodiscard]] int serializeSize() const;
[[nodiscard]] static std::optional<StorageImageLocation> FromSerialized(
const QByteArray &serialized);
[[nodiscard]] StorageImageLocation convertToModern(
StorageFileLocation::Type type,
uint64 id,
uint64 accessHash) const {
return StorageImageLocation(
_file.convertToModern(type, id, accessHash),
_width,
_height);
}
[[nodiscard]] const StorageFileLocation &file() const {
return _file;
}
@ -106,18 +150,26 @@ public:
_height = height;
}
[[nodiscard]] StorageFileLocation::Type type() const {
return _file.type();
}
[[nodiscard]] bool valid() const {
return _file.valid();
}
[[nodiscard]] InMemoryKey inMemoryKey() const {
return _file.inMemoryKey();
}
[[nodiscard]] QByteArray fileReference() const {
return _file.fileReference();
}
bool refreshFileReference(const QByteArray &data) {
return _file.refreshFileReference(data);
}
bool refreshFileReference(const Data::UpdatedFileReferences &updates) {
return _file.refreshFileReference(updates);
}
[[nodiscard]] static const StorageImageLocation &Invalid() {
static auto result = StorageImageLocation();
return result;
}
private:
friend inline bool operator==(
@ -224,12 +276,10 @@ private:
};
inline InMemoryKey inMemoryKey(const StorageFileLocation &location) {
return location.inMemoryKey();
}
InMemoryKey inMemoryKey(const StorageFileLocation &location);
inline InMemoryKey inMemoryKey(const StorageImageLocation &location) {
return location.inMemoryKey();
return inMemoryKey(location.file());
}
inline InMemoryKey inMemoryKey(const WebFileLocation &location) {

View File

@ -87,7 +87,7 @@ int ImageSource::loadOffset() {
}
const StorageImageLocation &ImageSource::location() {
return StorageImageLocation::Null;
return StorageImageLocation::Invalid();
}
void ImageSource::refreshFileReference(const QByteArray &data) {
@ -222,7 +222,7 @@ int LocalFileSource::loadOffset() {
}
const StorageImageLocation &LocalFileSource::location() {
return StorageImageLocation::Null;
return StorageImageLocation::Invalid();
}
void LocalFileSource::refreshFileReference(const QByteArray &data) {
@ -334,7 +334,7 @@ void RemoteSource::setImageBytes(const QByteArray &bytes) {
_loader->finishWithBytes(bytes);
const auto location = this->location();
if (!location.isNull()
if (location.valid()
&& !bytes.isEmpty()
&& bytes.size() <= Storage::kMaxFileInMemory) {
Auth().data().cache().putIfEmpty(
@ -437,7 +437,7 @@ RemoteSource::~RemoteSource() {
}
const StorageImageLocation &RemoteSource::location() {
return StorageImageLocation::Null;
return StorageImageLocation::Invalid();
}
void RemoteSource::refreshFileReference(const QByteArray &data) {
@ -472,9 +472,9 @@ const StorageImageLocation &StorageSource::location() {
}
std::optional<Storage::Cache::Key> StorageSource::cacheKey() {
return _location.isNull()
? std::nullopt
: base::make_optional(Data::StorageCacheKey(_location));
return _location.valid()
? base::make_optional(Data::StorageCacheKey(_location))
: std::nullopt;
}
int StorageSource::width() {
@ -506,16 +506,15 @@ FileLoader *StorageSource::createLoader(
Data::FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading) {
if (_location.isNull()) {
return nullptr;
}
return new mtpFileLoader(
&_location,
origin,
_size,
fromCloud,
autoLoading,
Data::kImageCacheTag);
return _location.valid()
? new mtpFileLoader(
&_location,
origin,
_size,
fromCloud,
autoLoading,
Data::kImageCacheTag)
: nullptr;
}
WebCachedSource::WebCachedSource(
@ -637,7 +636,7 @@ DelayedStorageSource::DelayedStorageSource()
}
DelayedStorageSource::DelayedStorageSource(int w, int h)
: StorageSource(StorageImageLocation(w, h, 0, 0, 0, 0, {}), 0) {
: StorageSource(StorageImageLocation(StorageFileLocation(), w, h), 0) {
}
void DelayedStorageSource::setDelayedStorageLocation(
@ -663,22 +662,22 @@ void DelayedStorageSource::performDelayedLoad(Data::FileOrigin origin) {
void DelayedStorageSource::automaticLoad(
Data::FileOrigin origin,
const HistoryItem *item) {
if (_location.isNull()) {
if (!_loadCancelled && item) {
const auto loadFromCloud = Data::AutoDownload::Should(
Auth().settings().autoDownload(),
item->history()->peer,
this);
if (_loadRequested) {
if (loadFromCloud) _loadFromCloud = loadFromCloud;
} else {
_loadFromCloud = loadFromCloud;
_loadRequested = true;
}
}
} else {
if (_location.valid()) {
StorageSource::automaticLoad(origin, item);
return;
} else if (_loadCancelled || !item) {
return;
}
const auto loadFromCloud = Data::AutoDownload::Should(
Auth().settings().autoDownload(),
item->history()->peer,
this);
if (_loadRequested) {
if (loadFromCloud) _loadFromCloud = loadFromCloud;
} else {
_loadFromCloud = loadFromCloud;
_loadRequested = true;
}
}
@ -691,10 +690,10 @@ void DelayedStorageSource::load(
Data::FileOrigin origin,
bool loadFirst,
bool prior) {
if (_location.isNull()) {
_loadRequested = _loadFromCloud = true;
} else {
if (_location.valid()) {
StorageSource::load(origin, loadFirst, prior);
} else {
_loadRequested = _loadFromCloud = true;
}
}
@ -707,7 +706,7 @@ void DelayedStorageSource::loadEvenCancelled(
}
bool DelayedStorageSource::displayLoading() {
return _location.isNull() ? true : StorageSource::displayLoading();
return _location.valid() ? StorageSource::displayLoading() : true;
}
void DelayedStorageSource::cancel() {

View File

@ -283,7 +283,9 @@ public:
void automaticLoadSettingsChanged() override;
bool loading() override {
return _location.isNull() ? _loadRequested : StorageSource::loading();
return _location.valid()
? StorageSource::loading()
: _loadRequested;
}
bool displayLoading() override;
void cancel() override;