From c27456277e4cdddc3841214a9e34f00c6d3ef198 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 4 Mar 2019 16:26:29 +0400
Subject: [PATCH] Support streaming of local files.

---
 Telegram/SourceFiles/data/data_document.cpp   | 19 +++--
 .../media_streaming_loader_local.cpp          | 83 +++++++++++++++++++
 .../streaming/media_streaming_loader_local.h  | 47 +++++++++++
 .../media/view/media_view_overlay_widget.cpp  |  5 ++
 Telegram/gyp/telegram_sources.txt             |  2 +
 5 files changed, 147 insertions(+), 9 deletions(-)
 create mode 100644 Telegram/SourceFiles/media/streaming/media_streaming_loader_local.cpp
 create mode 100644 Telegram/SourceFiles/media/streaming/media_streaming_loader_local.h

diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp
index 83e1f4fbef..7f439980df 100644
--- a/Telegram/SourceFiles/data/data_document.cpp
+++ b/Telegram/SourceFiles/data/data_document.cpp
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "mainwindow.h"
 #include "core/application.h"
 #include "media/streaming/media_streaming_loader_mtproto.h"
+#include "media/streaming/media_streaming_loader_local.h"
 
 namespace {
 
@@ -1214,14 +1215,14 @@ bool DocumentData::inappPlaybackFailed() const {
 
 auto DocumentData::createStreamingLoader(Data::FileOrigin origin) const
 -> std::unique_ptr<Media::Streaming::Loader> {
-	// #TODO streaming create local file loader
-	//auto &location = this->location(true);
-	//if (!_doc->data().isEmpty()) {
-	//	initStreaming();
-	//} else if (location.accessEnable()) {
-	//	initStreaming();
-	//	location.accessDisable();
-	//}
+	const auto &location = this->location(true);
+	if (!data().isEmpty()) {
+		return Media::Streaming::MakeBytesLoader(data());
+	} else if (!location.isEmpty() && location.accessEnable()) {
+		auto result = Media::Streaming::MakeFileLoader(location.name());
+		location.accessDisable();
+		return result;
+	}
 	return hasRemoteLocation()
 		? std::make_unique<Media::Streaming::LoaderMtproto>(
 			&session().api(),
@@ -1590,7 +1591,7 @@ void HandleUnsupportedMedia(
 		not_null<DocumentData*> document,
 		FullMsgId contextId) {
 	document->setInappPlaybackFailed();
-	auto filepath = document->filepath(
+	const auto filepath = document->filepath(
 		DocumentData::FilePathResolveSaveFromData);
 	if (filepath.isEmpty()) {
 		const auto save = [=] {
diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_loader_local.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_loader_local.cpp
new file mode 100644
index 0000000000..47c7274714
--- /dev/null
+++ b/Telegram/SourceFiles/media/streaming/media_streaming_loader_local.cpp
@@ -0,0 +1,83 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "media/streaming/media_streaming_loader_local.h"
+
+#include "storage/cache/storage_cache_types.h"
+
+namespace Media {
+namespace Streaming {
+
+LoaderLocal::LoaderLocal(std::unique_ptr<QIODevice> device)
+: _device(std::move(device)) {
+	Expects(_device != nullptr);
+
+	if (!_device->open(QIODevice::ReadOnly)) {
+		fail();
+	}
+}
+
+std::optional<Storage::Cache::Key> LoaderLocal::baseCacheKey() const {
+	return std::nullopt;
+}
+
+int LoaderLocal::size() const {
+	return _device->size();
+}
+
+void LoaderLocal::load(int offset) {
+	if (_device->pos() != offset && !_device->seek(offset)) {
+		fail();
+		return;
+	}
+	auto result = _device->read(kPartSize);
+	if (result.isEmpty()
+		|| ((result.size() != kPartSize)
+			&& (offset + result.size() != size()))) {
+		fail();
+		return;
+	}
+	crl::on_main(this, [=, result = std::move(result)]() mutable {
+		_parts.fire({ offset, std::move(result) });
+	});
+}
+
+void LoaderLocal::fail() {
+	crl::on_main(this, [=] {
+		_parts.fire({ LoadedPart::kFailedOffset });
+	});
+}
+
+void LoaderLocal::cancel(int offset) {
+}
+
+void LoaderLocal::increasePriority() {
+}
+
+void LoaderLocal::stop() {
+}
+
+rpl::producer<LoadedPart> LoaderLocal::parts() const {
+	return _parts.events();
+}
+
+std::unique_ptr<LoaderLocal> MakeFileLoader(const QString &path) {
+	return std::make_unique<LoaderLocal>(std::make_unique<QFile>(path));
+}
+
+std::unique_ptr<LoaderLocal> MakeBytesLoader(const QByteArray &bytes) {
+	auto device = std::make_unique<QBuffer>();
+	auto copy = new QByteArray(bytes);
+	QObject::connect(device.get(), &QBuffer::destroyed, [=] {
+		delete copy;
+	});
+	device->setBuffer(copy);
+	return std::make_unique<LoaderLocal>(std::move(device));
+}
+
+} // namespace Streaming
+} // namespace Media
diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_loader_local.h b/Telegram/SourceFiles/media/streaming/media_streaming_loader_local.h
new file mode 100644
index 0000000000..6a856d468b
--- /dev/null
+++ b/Telegram/SourceFiles/media/streaming/media_streaming_loader_local.h
@@ -0,0 +1,47 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "media/streaming/media_streaming_loader.h"
+#include "mtproto/sender.h"
+#include "data/data_file_origin.h"
+
+class ApiWrap;
+
+namespace Media {
+namespace Streaming {
+
+class LoaderLocal : public Loader, public base::has_weak_ptr {
+public:
+	LoaderLocal(std::unique_ptr<QIODevice> device);
+
+	[[nodiscard]] auto baseCacheKey() const
+		->std::optional<Storage::Cache::Key> override;
+	[[nodiscard]] int size() const override;
+
+	void load(int offset) override;
+	void cancel(int offset) override;
+	void increasePriority() override;
+	void stop() override;
+
+	// Parts will be sent from the main thread.
+	[[nodiscard]] rpl::producer<LoadedPart> parts() const override;
+
+private:
+	void fail();
+
+	std::unique_ptr<QIODevice> _device;
+	rpl::event_stream<LoadedPart> _parts;
+
+};
+
+std::unique_ptr<LoaderLocal> MakeFileLoader(const QString &path);
+std::unique_ptr<LoaderLocal> MakeBytesLoader(const QByteArray &bytes);
+
+} // namespace Streaming
+} // namespace Media
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
index bcb8a479b9..c6dfd745f4 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
@@ -1895,6 +1895,11 @@ void OverlayWidget::initStreamingThumbnail() {
 	const auto thumb = _doc->thumbnail();
 	const auto useThumb = (thumb && thumb->loaded());
 	const auto blurred = _doc->thumbnailInline();
+	if (good && !useGood) {
+		good->load({});
+	} else if (thumb && !useThumb) {
+		thumb->load(fileOrigin());
+	}
 	if (!useGood && !thumb && !blurred) {
 		return;
 	} else if (_doc->dimensions.isEmpty()) {
diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt
index d3452c8a8e..6063ba3136 100644
--- a/Telegram/gyp/telegram_sources.txt
+++ b/Telegram/gyp/telegram_sources.txt
@@ -460,6 +460,8 @@
 <(src_loc)/media/streaming/media_streaming_file_delegate.h
 <(src_loc)/media/streaming/media_streaming_loader.cpp
 <(src_loc)/media/streaming/media_streaming_loader.h
+<(src_loc)/media/streaming/media_streaming_loader_local.cpp
+<(src_loc)/media/streaming/media_streaming_loader_local.h
 <(src_loc)/media/streaming/media_streaming_loader_mtproto.cpp
 <(src_loc)/media/streaming/media_streaming_loader_mtproto.h
 <(src_loc)/media/streaming/media_streaming_player.cpp