diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp
index 6a46c2679..cf55e70fc 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.cpp
+++ b/Telegram/SourceFiles/export/data/export_data_types.cpp
@@ -142,6 +142,7 @@ void ParseAttributes(
 		}, [&](const MTPDdocumentAttributeAnimated &data) {
 			result.isAnimated = true;
 		}, [&](const MTPDdocumentAttributeSticker &data) {
+			result.isSticker = true;
 			result.stickerEmoji = ParseString(data.valt);
 		}, [&](const MTPDdocumentAttributeVideo &data) {
 			if (data.is_round_message()) {
diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h
index 78664ef3b..8e7a07a54 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.h
+++ b/Telegram/SourceFiles/export/data/export_data_types.h
@@ -51,6 +51,12 @@ struct FileLocation {
 };
 
 struct File {
+	enum class SkipReason {
+		None,
+		Unavailable,
+		FileType,
+		FileSize,
+	};
 	FileLocation location;
 	int size = 0;
 	QByteArray content;
@@ -58,6 +64,7 @@ struct File {
 	QString suggestedPath;
 
 	QString relativePath;
+	SkipReason skipReason = SkipReason::None;
 };
 
 struct Image {
@@ -89,6 +96,7 @@ struct Document {
 	Utf8String songTitle;
 	int duration = 0;
 
+	bool isSticker = false;
 	bool isAnimated = false;
 	bool isVideoMessage = false;
 	bool isVoiceMessage = false;
diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp
index d21d31272..1580e4f28 100644
--- a/Telegram/SourceFiles/export/export_api_wrap.cpp
+++ b/Telegram/SourceFiles/export/export_api_wrap.cpp
@@ -26,9 +26,8 @@ constexpr auto kChatsSliceLimit = 100;
 constexpr auto kMessagesSliceLimit = 100;
 constexpr auto kFileMaxSize = 1500 * 1024 * 1024;
 
-bool WillLoadFile(const Data::File &file) {
-	return file.relativePath.isEmpty()
-		&& (!file.content.isEmpty() || file.location.dcId != 0);
+bool FileIsAvailable(const Data::File &file) {
+	return file.relativePath.isEmpty() && (file.location.dcId != 0);
 }
 
 } // namespace
@@ -258,17 +257,16 @@ void ApiWrap::loadNextUserpic() {
 	Expects(_userpicsProcess != nullptr);
 	Expects(_userpicsProcess->slice.has_value());
 
-	const auto &list = _userpicsProcess->slice->list;
+	auto &list = _userpicsProcess->slice->list;
 	while (true) {
 		const auto index = ++_userpicsProcess->fileIndex;
 		if (index >= list.size()) {
 			break;
 		}
-		const auto &file = list[index].image.file;
-		if (WillLoadFile(file)) {
-			loadFile(
-				file,
-				[=](const QString &path) { loadUserpicDone(path); });
+		const auto ready = processFileLoad(
+			list[index].image.file,
+			[=](const QString &path) { loadUserpicDone(path); });
+		if (!ready) {
 			return;
 		}
 	}
@@ -510,17 +508,16 @@ void ApiWrap::loadNextMessageFile() {
 	Expects(_dialogsProcess->single->slice.has_value());
 
 	const auto process = _dialogsProcess->single.get();
-	const auto &list = process->slice->list;
+	auto &list = process->slice->list;
 	while (true) {
 		const auto index = ++process->fileIndex;
 		if (index >= list.size()) {
 			break;
 		}
-		const auto &file = list[index].file();
-		if (WillLoadFile(file)) {
-			loadFile(
-				file,
-				[=](const QString &path) { loadMessageFileDone(path); });
+		const auto ready = processFileLoad(
+			list[index].file(),
+			[=](const QString &path) { loadMessageFileDone(path); });
+		if (!ready) {
 			return;
 		}
 	}
@@ -569,32 +566,92 @@ void ApiWrap::finishDialogs() {
 	base::take(_dialogsProcess)->finish();
 }
 
-void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) {
-	Expects(_fileProcess == nullptr);
+bool ApiWrap::processFileLoad(
+		Data::File &file,
+		FnMut<void(QString)> done,
+		Data::Message *message) {
+	using SkipReason = Data::File::SkipReason;
+
+	if (!file.relativePath.isEmpty()) {
+		return true;
+	} else if (writePreloadedFile(file)) {
+		return true;
+	} else if (!FileIsAvailable(file)) {
+		file.skipReason = SkipReason::Unavailable;
+		return true;
+	}
+
+	using Type = MediaSettings::Type;
+	const auto type = message ? message->media.content.match(
+	[&](const Data::Document &data) {
+		if (data.isSticker) {
+			return Type::Sticker;
+		} else if (data.isVideoMessage) {
+			return Type::VideoMessage;
+		} else if (data.isVoiceMessage) {
+			return Type::VoiceMessage;
+		} else if (data.isAnimated) {
+			return Type::GIF;
+		} else if (data.isVideoFile) {
+			return Type::Video;
+		} else {
+			return Type::File;
+		}
+	}, [](const auto &data) {
+		return Type::Photo;
+	}) : Type::Photo;
+
+	if ((_settings->media.types & type) != type) {
+		file.skipReason = SkipReason::FileType;
+		return true;
+	} else if (file.size >= _settings->media.sizeLimit) {
+		file.skipReason = SkipReason::FileSize;
+		return true;
+	}
+	loadFile(file, std::move(done));
+	return false;
+}
+
+bool ApiWrap::writePreloadedFile(Data::File &file) {
 	Expects(_settings != nullptr);
-	Expects(WillLoadFile(file));
 
 	using namespace Output;
-	const auto relativePath = File::PrepareRelativePath(
-		_settings->path,
-		file.suggestedPath);
-	_fileProcess = std::make_unique<FileProcess>(
-		_settings->path + relativePath);
-	_fileProcess->relativePath = relativePath;
-	_fileProcess->location = file.location;
-	_fileProcess->size = file.size;
-	_fileProcess->done = std::move(done);
 
 	if (!file.content.isEmpty()) {
+		const auto process = prepareFileProcess(file);
 		auto &output = _fileProcess->file;
 		if (output.writeBlock(file.content) == File::Result::Success) {
-			_fileProcess->done(relativePath);
-		} else {
-			error(QString("Could not open '%1'.").arg(relativePath));
+			file.relativePath = process->relativePath;
+			return true;
 		}
-	} else {
-		loadFilePart();
+		error(QString("Could not write '%1'.").arg(process->relativePath));
 	}
+	return false;
+}
+
+void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) {
+	Expects(_fileProcess == nullptr);
+	Expects(FileIsAvailable(file));
+
+	_fileProcess = prepareFileProcess(file);
+	_fileProcess->done = std::move(done);
+
+	loadFilePart();
+}
+
+auto ApiWrap::prepareFileProcess(const Data::File &file) const
+-> std::unique_ptr<FileProcess> {
+	Expects(_settings != nullptr);
+
+	const auto relativePath = Output::File::PrepareRelativePath(
+		_settings->path,
+		file.suggestedPath);
+	auto result = std::make_unique<FileProcess>(
+		_settings->path + relativePath);
+	result->relativePath = relativePath;
+	result->location = file.location;
+	result->size = file.size;
+	return result;
 }
 
 void ApiWrap::loadFilePart() {
diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h
index 6db408376..b0b1b4028 100644
--- a/Telegram/SourceFiles/export/export_api_wrap.h
+++ b/Telegram/SourceFiles/export/export_api_wrap.h
@@ -21,6 +21,7 @@ struct SessionsList;
 struct DialogsInfo;
 struct DialogInfo;
 struct MessagesSlice;
+struct Message;
 } // namespace Data
 
 struct Settings;
@@ -56,6 +57,10 @@ public:
 	~ApiWrap();
 
 private:
+	struct UserpicsProcess;
+	struct FileProcess;
+	struct DialogsProcess;
+
 	void startMainSession(FnMut<void()> done);
 
 	void handleUserpicsSlice(const MTPphotos_Photos &result);
@@ -64,8 +69,6 @@ private:
 	void loadUserpicDone(const QString &relativePath);
 	void finishUserpics();
 
-	void requestSavedContacts();
-
 	void requestDialogsSlice();
 	void appendDialogsSlice(Data::DialogsInfo &&info);
 	void finishDialogsList();
@@ -75,10 +78,18 @@ private:
 	void requestMessagesSlice();
 	void loadMessagesFiles(Data::MessagesSlice &&slice);
 	void loadNextMessageFile();
+
 	void loadMessageFileDone(const QString &relativePath);
 	void finishMessages();
 	void finishDialogs();
 
+	bool processFileLoad(
+		Data::File &file,
+		FnMut<void(QString)> done,
+		Data::Message *message = nullptr);
+	std::unique_ptr<FileProcess> prepareFileProcess(
+		const Data::File &file) const;
+	bool writePreloadedFile(Data::File &file);
 	void loadFile(const Data::File &file, FnMut<void(QString)> done);
 	void loadFilePart();
 	void filePartDone(int offset, const MTPupload_File &result);
@@ -99,13 +110,8 @@ private:
 	std::unique_ptr<Settings> _settings;
 	MTPInputUser _user = MTP_inputUserSelf();
 
-	struct UserpicsProcess;
 	std::unique_ptr<UserpicsProcess> _userpicsProcess;
-
-	struct FileProcess;
 	std::unique_ptr<FileProcess> _fileProcess;
-
-	struct DialogsProcess;
 	std::unique_ptr<DialogsProcess> _dialogsProcess;
 
 	rpl::event_stream<RPCError> _errors;
diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp
index 73c4e64d0..2bb294866 100644
--- a/Telegram/SourceFiles/export/output/export_output_text.cpp
+++ b/Telegram/SourceFiles/export/output/export_output_text.cpp
@@ -171,6 +171,13 @@ QByteArray SerializeMessage(
 	const auto pushAction = [&](const QByteArray &action) {
 		push("Action", action);
 	};
+	const auto pushTTL = [&](
+		const QByteArray &label = "Self destruct period") {
+		if (const auto ttl = message.media.ttl) {
+			push(label, NumberToString(ttl) + " sec.");
+		}
+	};
+
 	message.action.content.match([&](const ActionChatCreate &data) {
 		pushActor();
 		pushAction("Create group");
@@ -299,11 +306,12 @@ QByteArray SerializeMessage(
 	}
 
 	message.media.content.match([&](const Photo &photo) {
+		pushTTL();
 	}, [&](const Document &data) {
 		const auto pushPath = [&](const QByteArray &label) {
 			push(label, FormatFilePath(data.file));
 		};
-		if (!data.stickerEmoji.isEmpty()) {
+		if (data.isSticker) {
 			pushPath("Sticker");
 			push("Emoji", data.stickerEmoji);
 		} else if (data.isVideoMessage) {
@@ -321,7 +329,7 @@ QByteArray SerializeMessage(
 		} else {
 			pushPath("File");
 		}
-		if (data.stickerEmoji.isEmpty()) {
+		if (!data.isSticker) {
 			push("Mime type", data.mime);
 		}
 		if (data.duration) {
@@ -331,6 +339,7 @@ QByteArray SerializeMessage(
 			push("Width", NumberToString(data.width));
 			push("Height", NumberToString(data.height));
 		}
+		pushTTL();
 	}, [&](const ContactInfo &data) {
 		push("Contact information", SerializeKeyValue({
 			{ "First name", data.firstName },
@@ -342,6 +351,7 @@ QByteArray SerializeMessage(
 			{ "Latitude", NumberToString(data.latitude) },
 			{ "Longitude", NumberToString(data.longitude) },
 		}) : QByteArray("(empty value)"));
+		pushTTL("Live location period");
 	}, [&](const Venue &data) {
 		push("Place name", data.title);
 		push("Address", data.address);