data_cloud_file.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "data/data_cloud_file.h"
  8. #include "data/data_file_origin.h"
  9. #include "data/data_session.h"
  10. #include "storage/cache/storage_cache_database.h"
  11. #include "storage/file_download.h"
  12. #include "ui/image/image.h"
  13. #include "main/main_session.h"
  14. namespace Data {
  15. CloudFile::~CloudFile() {
  16. // Destroy loader with still alive CloudFile with already zero '.loader'.
  17. // Otherwise in ~FileLoader it tries to clear file.loader and crashes.
  18. base::take(loader);
  19. }
  20. void CloudFile::clear() {
  21. location = {};
  22. base::take(loader);
  23. byteSize = 0;
  24. progressivePartSize = 0;
  25. flags = {};
  26. }
  27. CloudImage::CloudImage() = default;
  28. CloudImage::CloudImage(
  29. not_null<Main::Session*> session,
  30. const ImageWithLocation &data) {
  31. update(session, data);
  32. }
  33. void CloudImage::set(
  34. not_null<Main::Session*> session,
  35. const ImageWithLocation &data) {
  36. const auto &was = _file.location.file().data;
  37. const auto &now = data.location.file().data;
  38. if (!data.location.valid()) {
  39. _file.flags |= CloudFile::Flag::Cancelled;
  40. _file.loader = nullptr;
  41. _file.location = ImageLocation();
  42. _file.byteSize = 0;
  43. _file.flags = CloudFile::Flag();
  44. _view = std::weak_ptr<QImage>();
  45. } else if (was != now
  46. && (!v::is<InMemoryLocation>(was) || v::is<InMemoryLocation>(now))) {
  47. _file.location = ImageLocation();
  48. _view = std::weak_ptr<QImage>();
  49. }
  50. UpdateCloudFile(
  51. _file,
  52. data,
  53. session->data().cache(),
  54. kImageCacheTag,
  55. [=](FileOrigin origin) { load(session, origin); },
  56. [=](QImage preloaded, QByteArray) {
  57. setToActive(session, std::move(preloaded));
  58. });
  59. }
  60. void CloudImage::update(
  61. not_null<Main::Session*> session,
  62. const ImageWithLocation &data) {
  63. UpdateCloudFile(
  64. _file,
  65. data,
  66. session->data().cache(),
  67. kImageCacheTag,
  68. [=](FileOrigin origin) { load(session, origin); },
  69. [=](QImage preloaded, QByteArray) {
  70. setToActive(session, std::move(preloaded));
  71. });
  72. }
  73. bool CloudImage::empty() const {
  74. return !_file.location.valid();
  75. }
  76. bool CloudImage::loading() const {
  77. return (_file.loader != nullptr);
  78. }
  79. bool CloudImage::failed() const {
  80. return (_file.flags & CloudFile::Flag::Failed);
  81. }
  82. bool CloudImage::loadedOnce() const {
  83. return (_file.flags & CloudFile::Flag::Loaded);
  84. }
  85. void CloudImage::load(not_null<Main::Session*> session, FileOrigin origin) {
  86. const auto autoLoading = false;
  87. const auto finalCheck = [=] {
  88. if (const auto active = activeView()) {
  89. return active->isNull();
  90. } else if (_file.flags & CloudFile::Flag::Loaded) {
  91. return false;
  92. }
  93. return !(_file.flags & CloudFile::Flag::Loaded);
  94. };
  95. const auto done = [=](QImage result, QByteArray) {
  96. setToActive(session, std::move(result));
  97. };
  98. LoadCloudFile(
  99. session,
  100. _file,
  101. origin,
  102. LoadFromCloudOrLocal,
  103. autoLoading,
  104. kImageCacheTag,
  105. finalCheck,
  106. done);
  107. }
  108. const ImageLocation &CloudImage::location() const {
  109. return _file.location;
  110. }
  111. int CloudImage::byteSize() const {
  112. return _file.byteSize;
  113. }
  114. std::shared_ptr<QImage> CloudImage::createView() {
  115. if (auto active = activeView()) {
  116. return active;
  117. }
  118. auto view = std::make_shared<QImage>();
  119. _view = view;
  120. return view;
  121. }
  122. std::shared_ptr<QImage> CloudImage::activeView() const {
  123. return _view.lock();
  124. }
  125. bool CloudImage::isCurrentView(const std::shared_ptr<QImage> &view) const {
  126. if (!view) {
  127. return empty();
  128. }
  129. return !view.owner_before(_view) && !_view.owner_before(view);
  130. }
  131. void CloudImage::setToActive(
  132. not_null<Main::Session*> session,
  133. QImage image) {
  134. if (const auto view = activeView()) {
  135. *view = image.isNull()
  136. ? Image::Empty()->original()
  137. : std::move(image);
  138. session->notifyDownloaderTaskFinished();
  139. }
  140. }
  141. void UpdateCloudFile(
  142. CloudFile &file,
  143. const ImageWithLocation &data,
  144. Storage::Cache::Database &cache,
  145. uint8 cacheTag,
  146. Fn<void(FileOrigin)> restartLoader,
  147. Fn<void(QImage, QByteArray)> usePreloaded) {
  148. if (!data.location.valid()) {
  149. if (data.progressivePartSize && !file.location.valid()) {
  150. file.progressivePartSize = data.progressivePartSize;
  151. }
  152. if (data.location.width()
  153. && data.location.height()
  154. && !file.location.valid()
  155. && !file.location.width()) {
  156. file.location = data.location;
  157. }
  158. return;
  159. }
  160. const auto needStickerThumbnailUpdate = [&] {
  161. const auto was = std::get_if<StorageFileLocation>(
  162. &file.location.file().data);
  163. const auto now = std::get_if<StorageFileLocation>(
  164. &data.location.file().data);
  165. using Type = StorageFileLocation::Type;
  166. if (!was || !now || was->type() != Type::StickerSetThumb) {
  167. return false;
  168. }
  169. return now->valid()
  170. && (now->type() != Type::StickerSetThumb
  171. || now->cacheKey() != was->cacheKey());
  172. };
  173. const auto update = !file.location.valid()
  174. || (data.location.file().cacheKey()
  175. && (!file.location.file().cacheKey()
  176. || (file.location.width() < data.location.width())
  177. || (file.location.height() < data.location.height())
  178. || needStickerThumbnailUpdate()));
  179. if (!update) {
  180. return;
  181. }
  182. auto cacheBytes = !data.bytes.isEmpty()
  183. ? data.bytes
  184. : v::is<InMemoryLocation>(file.location.file().data)
  185. ? v::get<InMemoryLocation>(file.location.file().data).bytes
  186. : QByteArray();
  187. if (!cacheBytes.isEmpty()) {
  188. if (const auto cacheKey = data.location.file().cacheKey()) {
  189. cache.putIfEmpty(
  190. cacheKey,
  191. Storage::Cache::Database::TaggedValue(
  192. std::move(cacheBytes),
  193. cacheTag));
  194. }
  195. }
  196. file.location = data.location;
  197. file.byteSize = data.bytesCount;
  198. if (!data.preloaded.isNull()) {
  199. file.loader = nullptr;
  200. if (usePreloaded) {
  201. usePreloaded(data.preloaded, data.bytes);
  202. }
  203. } else if (file.loader) {
  204. const auto origin = base::take(file.loader)->fileOrigin();
  205. restartLoader(origin);
  206. } else if (file.flags & CloudFile::Flag::Failed) {
  207. file.flags &= ~CloudFile::Flag::Failed;
  208. }
  209. }
  210. void LoadCloudFile(
  211. not_null<Main::Session*> session,
  212. CloudFile &file,
  213. FileOrigin origin,
  214. LoadFromCloudSetting fromCloud,
  215. bool autoLoading,
  216. uint8 cacheTag,
  217. Fn<bool()> finalCheck,
  218. Fn<void(CloudFile&)> done,
  219. Fn<void(bool)> fail,
  220. Fn<void()> progress,
  221. int downloadFrontPartSize = 0) {
  222. const auto loadSize = downloadFrontPartSize
  223. ? std::min(downloadFrontPartSize, file.byteSize)
  224. : file.byteSize;
  225. if (file.loader) {
  226. if (fromCloud == LoadFromCloudOrLocal) {
  227. file.loader->permitLoadFromCloud();
  228. }
  229. if (file.loader->loadSize() < loadSize) {
  230. file.loader->increaseLoadSize(loadSize, autoLoading);
  231. }
  232. return;
  233. } else if ((file.flags & CloudFile::Flag::Failed)
  234. || !file.location.valid()
  235. || (finalCheck && !finalCheck())) {
  236. return;
  237. }
  238. file.flags &= ~CloudFile::Flag::Cancelled;
  239. file.loader = CreateFileLoader(
  240. session,
  241. file.location.file(),
  242. origin,
  243. QString(),
  244. loadSize,
  245. file.byteSize,
  246. UnknownFileLocation,
  247. LoadToCacheAsWell,
  248. fromCloud,
  249. autoLoading,
  250. cacheTag);
  251. const auto finish = [done](CloudFile &file) {
  252. if (!file.loader || file.loader->cancelled()) {
  253. file.flags |= CloudFile::Flag::Cancelled;
  254. } else {
  255. file.flags |= CloudFile::Flag::Loaded;
  256. done(file);
  257. }
  258. // NB! file.loader may be in ~FileLoader() already.
  259. if (const auto loader = base::take(file.loader)) {
  260. if ((file.flags & CloudFile::Flag::Cancelled)
  261. && !loader->cancelled()) {
  262. loader->cancel();
  263. }
  264. }
  265. };
  266. file.loader->updates(
  267. ) | rpl::start_with_next_error_done([=] {
  268. if (const auto onstack = progress) {
  269. onstack();
  270. }
  271. }, [=, &file](FileLoader::Error error) {
  272. finish(file);
  273. file.flags |= CloudFile::Flag::Failed;
  274. if (const auto onstack = fail) {
  275. onstack(error.started);
  276. }
  277. }, [=, &file] {
  278. finish(file);
  279. }, file.loader->lifetime());
  280. file.loader->start();
  281. }
  282. void LoadCloudFile(
  283. not_null<Main::Session*> session,
  284. CloudFile &file,
  285. FileOrigin origin,
  286. LoadFromCloudSetting fromCloud,
  287. bool autoLoading,
  288. uint8 cacheTag,
  289. Fn<bool()> finalCheck,
  290. Fn<void(QImage, QByteArray)> done,
  291. Fn<void(bool)> fail,
  292. Fn<void()> progress,
  293. int downloadFrontPartSize) {
  294. const auto callback = [=](CloudFile &file) {
  295. if (auto read = file.loader->imageData(); read.isNull()) {
  296. file.flags |= CloudFile::Flag::Failed;
  297. if (const auto onstack = fail) {
  298. onstack(true);
  299. }
  300. } else if (const auto onstack = done) {
  301. onstack(std::move(read), file.loader->bytes());
  302. }
  303. };
  304. LoadCloudFile(
  305. session,
  306. file,
  307. origin,
  308. fromCloud,
  309. autoLoading,
  310. cacheTag,
  311. finalCheck,
  312. callback,
  313. std::move(fail),
  314. std::move(progress),
  315. downloadFrontPartSize);
  316. }
  317. void LoadCloudFile(
  318. not_null<Main::Session*> session,
  319. CloudFile &file,
  320. FileOrigin origin,
  321. LoadFromCloudSetting fromCloud,
  322. bool autoLoading,
  323. uint8 cacheTag,
  324. Fn<bool()> finalCheck,
  325. Fn<void(QByteArray)> done,
  326. Fn<void(bool)> fail,
  327. Fn<void()> progress) {
  328. const auto callback = [=](CloudFile &file) {
  329. if (auto bytes = file.loader->bytes(); bytes.isEmpty()) {
  330. file.flags |= CloudFile::Flag::Failed;
  331. if (const auto onstack = fail) {
  332. onstack(true);
  333. }
  334. } else if (const auto onstack = done) {
  335. onstack(std::move(bytes));
  336. }
  337. };
  338. LoadCloudFile(
  339. session,
  340. file,
  341. origin,
  342. fromCloud,
  343. autoLoading,
  344. cacheTag,
  345. finalCheck,
  346. callback,
  347. std::move(fail),
  348. std::move(progress));
  349. }
  350. } // namespace Data