file_upload.cpp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  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 "storage/file_upload.h"
  8. #include "api/api_editing.h"
  9. #include "api/api_send_progress.h"
  10. #include "storage/localimageloader.h"
  11. #include "storage/file_download.h"
  12. #include "data/data_document.h"
  13. #include "data/data_document_media.h"
  14. #include "data/data_photo.h"
  15. #include "data/data_session.h"
  16. #include "ui/image/image_location_factory.h"
  17. #include "history/history_item.h"
  18. #include "history/history.h"
  19. #include "core/file_location.h"
  20. #include "core/mime_type.h"
  21. #include "main/main_session.h"
  22. #include "apiwrap.h"
  23. namespace Storage {
  24. namespace {
  25. // max 1mb uploaded at the same time in each session
  26. constexpr auto kMaxUploadPerSession = 1024 * 1024;
  27. constexpr auto kDocumentMaxPartsCountDefault = 4000;
  28. // 32kb for tiny document ( < 1mb )
  29. constexpr auto kDocumentUploadPartSize0 = 32 * 1024;
  30. // 64kb for little document ( <= 32mb )
  31. constexpr auto kDocumentUploadPartSize1 = 64 * 1024;
  32. // 128kb for small document ( <= 375mb )
  33. constexpr auto kDocumentUploadPartSize2 = 128 * 1024;
  34. // 256kb for medium document ( <= 750mb )
  35. constexpr auto kDocumentUploadPartSize3 = 256 * 1024;
  36. // 512kb for large document ( <= 1500mb )
  37. constexpr auto kDocumentUploadPartSize4 = 512 * 1024;
  38. // One part each half second, if not uploaded faster.
  39. constexpr auto kUploadRequestInterval = crl::time(250);
  40. // How much time without upload causes additional session kill.
  41. constexpr auto kKillSessionTimeout = 15 * crl::time(1000);
  42. // How much wait after session kill before killing another one.
  43. constexpr auto kWaitForNormalizeTimeout = 8 * crl::time(1000);
  44. constexpr auto kMaxSessionsCount = 8;
  45. constexpr auto kFastRequestThreshold = 1 * crl::time(1000);
  46. constexpr auto kSlowRequestThreshold = 8 * crl::time(1000);
  47. // Request is 'fast' if it was done in less than 1s and
  48. // (it-s size + queued before size) >= 512kb.
  49. constexpr auto kAcceptAsFastIfTotalAtLeast = 512 * 1024;
  50. [[nodiscard]] const char *ThumbnailFormat(const QString &mime) {
  51. return Core::IsMimeSticker(mime) ? "WEBP" : "JPG";
  52. }
  53. } // namespace
  54. struct Uploader::Entry {
  55. Entry(FullMsgId itemId, const std::shared_ptr<FilePrepareResult> &file);
  56. void setDocSize(int64 size);
  57. bool setPartSize(int partSize);
  58. // const, but non-const for the move-assignment in the
  59. FullMsgId itemId;
  60. std::shared_ptr<FilePrepareResult> file;
  61. not_null<std::vector<QByteArray>*> parts;
  62. uint64 partsOfId = 0;
  63. int64 sentSize = 0;
  64. ushort partsSent = 0;
  65. ushort partsWaiting = 0;
  66. HashMd5 md5Hash;
  67. std::unique_ptr<QFile> docFile;
  68. int64 docSize = 0;
  69. int64 docSentSize = 0;
  70. int docPartSize = 0;
  71. ushort docPartsSent = 0;
  72. ushort docPartsCount = 0;
  73. ushort docPartsWaiting = 0;
  74. };
  75. struct Uploader::Request {
  76. FullMsgId itemId;
  77. crl::time sent = 0;
  78. QByteArray bytes;
  79. int queued = 0;
  80. ushort part = 0;
  81. uchar dcIndex = 0;
  82. bool docPart = false;
  83. bool bigPart = false;
  84. bool nonPremiumDelayed = false;
  85. };
  86. Uploader::Entry::Entry(
  87. FullMsgId itemId,
  88. const std::shared_ptr<FilePrepareResult> &file)
  89. : itemId(itemId)
  90. , file(file)
  91. , parts((file->type == SendMediaType::Photo
  92. || file->type == SendMediaType::Secure)
  93. ? &file->fileparts
  94. : &file->thumbparts)
  95. , partsOfId((file->type == SendMediaType::Photo
  96. || file->type == SendMediaType::Secure)
  97. ? file->id
  98. : file->thumbId) {
  99. if (file->type == SendMediaType::File
  100. || file->type == SendMediaType::ThemeFile
  101. || file->type == SendMediaType::Audio
  102. || file->type == SendMediaType::Round) {
  103. setDocSize(file->filesize);
  104. }
  105. }
  106. void Uploader::Entry::setDocSize(int64 size) {
  107. docSize = size;
  108. constexpr auto limit0 = 1024 * 1024;
  109. constexpr auto limit1 = 32 * limit0;
  110. if (docSize >= limit0 || !setPartSize(kDocumentUploadPartSize0)) {
  111. if (docSize > limit1 || !setPartSize(kDocumentUploadPartSize1)) {
  112. if (!setPartSize(kDocumentUploadPartSize2)) {
  113. if (!setPartSize(kDocumentUploadPartSize3)) {
  114. setPartSize(kDocumentUploadPartSize4);
  115. }
  116. }
  117. }
  118. }
  119. }
  120. bool Uploader::Entry::setPartSize(int partSize) {
  121. docPartSize = partSize;
  122. docPartsCount = (docSize + docPartSize - 1) / docPartSize;
  123. return (docPartsCount <= kDocumentMaxPartsCountDefault);
  124. }
  125. Uploader::Uploader(not_null<ApiWrap*> api)
  126. : _api(api)
  127. , _nextTimer([=] { maybeSend(); })
  128. , _stopSessionsTimer([=] { stopSessions(); }) {
  129. const auto session = &_api->session();
  130. photoReady(
  131. ) | rpl::start_with_next([=](UploadedMedia &&data) {
  132. if (data.edit) {
  133. const auto item = session->data().message(data.fullId);
  134. Api::EditMessageWithUploadedPhoto(
  135. item,
  136. std::move(data.info),
  137. data.options);
  138. } else {
  139. _api->sendUploadedPhoto(
  140. data.fullId,
  141. std::move(data.info),
  142. data.options);
  143. }
  144. }, _lifetime);
  145. documentReady(
  146. ) | rpl::start_with_next([=](UploadedMedia &&data) {
  147. if (data.edit) {
  148. const auto item = session->data().message(data.fullId);
  149. Api::EditMessageWithUploadedDocument(
  150. item,
  151. std::move(data.info),
  152. data.options);
  153. } else {
  154. _api->sendUploadedDocument(
  155. data.fullId,
  156. std::move(data.info),
  157. data.options);
  158. }
  159. }, _lifetime);
  160. photoProgress(
  161. ) | rpl::start_with_next([=](const FullMsgId &fullId) {
  162. processPhotoProgress(fullId);
  163. }, _lifetime);
  164. photoFailed(
  165. ) | rpl::start_with_next([=](const FullMsgId &fullId) {
  166. processPhotoFailed(fullId);
  167. }, _lifetime);
  168. documentProgress(
  169. ) | rpl::start_with_next([=](const FullMsgId &fullId) {
  170. processDocumentProgress(fullId);
  171. }, _lifetime);
  172. documentFailed(
  173. ) | rpl::start_with_next([=](const FullMsgId &fullId) {
  174. processDocumentFailed(fullId);
  175. }, _lifetime);
  176. _api->instance().nonPremiumDelayedRequests(
  177. ) | rpl::start_with_next([=](mtpRequestId id) {
  178. const auto i = _requests.find(id);
  179. if (i != end(_requests)) {
  180. i->second.nonPremiumDelayed = true;
  181. }
  182. }, _lifetime);
  183. }
  184. void Uploader::processPhotoProgress(FullMsgId itemId) {
  185. if (const auto item = session().data().message(itemId)) {
  186. sendProgressUpdate(item, Api::SendProgressType::UploadPhoto);
  187. }
  188. }
  189. void Uploader::processDocumentProgress(FullMsgId itemId) {
  190. if (const auto item = session().data().message(itemId)) {
  191. const auto media = item->media();
  192. const auto document = media ? media->document() : nullptr;
  193. const auto sendAction = (document && document->isVoiceMessage())
  194. ? Api::SendProgressType::UploadVoice
  195. : (document && document->isVideoMessage())
  196. ? Api::SendProgressType::UploadRound
  197. : Api::SendProgressType::UploadFile;
  198. const auto progress = (document && document->uploading())
  199. ? ((document->uploadingData->offset * 100)
  200. / document->uploadingData->size)
  201. : 0;
  202. sendProgressUpdate(item, sendAction, progress);
  203. }
  204. }
  205. void Uploader::processPhotoFailed(FullMsgId itemId) {
  206. if (const auto item = session().data().message(itemId)) {
  207. sendProgressUpdate(item, Api::SendProgressType::UploadPhoto, -1);
  208. }
  209. }
  210. void Uploader::processDocumentFailed(FullMsgId itemId) {
  211. if (const auto item = session().data().message(itemId)) {
  212. const auto media = item->media();
  213. const auto document = media ? media->document() : nullptr;
  214. const auto sendAction = (document && document->isVoiceMessage())
  215. ? Api::SendProgressType::UploadVoice
  216. : (document && document->isVideoMessage())
  217. ? Api::SendProgressType::UploadRound
  218. : Api::SendProgressType::UploadFile;
  219. sendProgressUpdate(item, sendAction, -1);
  220. }
  221. }
  222. void Uploader::sendProgressUpdate(
  223. not_null<HistoryItem*> item,
  224. Api::SendProgressType type,
  225. int progress) {
  226. const auto history = item->history();
  227. auto &manager = _api->session().sendProgressManager();
  228. manager.update(history, type, progress);
  229. if (const auto replyTo = item->replyToTop()) {
  230. if (history->peer->isMegagroup()) {
  231. manager.update(history, replyTo, type, progress);
  232. }
  233. } else if (history->isForum()) {
  234. manager.update(history, item->topicRootId(), type, progress);
  235. }
  236. _api->session().data().requestItemRepaint(item);
  237. }
  238. Uploader::~Uploader() {
  239. clear();
  240. }
  241. Main::Session &Uploader::session() const {
  242. return _api->session();
  243. }
  244. FullMsgId Uploader::currentUploadId() const {
  245. return _queue.empty() ? FullMsgId() : _queue.front().itemId;
  246. }
  247. void Uploader::upload(
  248. FullMsgId itemId,
  249. const std::shared_ptr<FilePrepareResult> &file) {
  250. if (file->type == SendMediaType::Photo) {
  251. const auto photo = session().data().processPhoto(
  252. file->photo,
  253. file->photoThumbs);
  254. photo->uploadingData = std::make_unique<Data::UploadState>(
  255. file->partssize);
  256. } else if (file->type == SendMediaType::File
  257. || file->type == SendMediaType::ThemeFile
  258. || file->type == SendMediaType::Audio
  259. || file->type == SendMediaType::Round) {
  260. const auto document = file->thumb.isNull()
  261. ? session().data().processDocument(file->document)
  262. : session().data().processDocument(
  263. file->document,
  264. Images::FromImageInMemory(
  265. file->thumb,
  266. ThumbnailFormat(file->filemime),
  267. file->thumbbytes));
  268. document->uploadingData = std::make_unique<Data::UploadState>(
  269. document->size);
  270. if (const auto active = document->activeMediaView()) {
  271. if (!file->goodThumbnail.isNull()) {
  272. active->setGoodThumbnail(std::move(file->goodThumbnail));
  273. }
  274. if (!file->thumb.isNull()) {
  275. active->setThumbnail(file->thumb);
  276. }
  277. }
  278. if (!file->goodThumbnailBytes.isEmpty()) {
  279. document->owner().cache().putIfEmpty(
  280. document->goodThumbnailCacheKey(),
  281. Storage::Cache::Database::TaggedValue(
  282. std::move(file->goodThumbnailBytes),
  283. Data::kImageCacheTag));
  284. }
  285. if (!file->content.isEmpty()) {
  286. document->setDataAndCache(file->content);
  287. }
  288. if (!file->filepath.isEmpty()) {
  289. document->setLocation(Core::FileLocation(file->filepath));
  290. }
  291. if (file->type == SendMediaType::ThemeFile) {
  292. document->checkWallPaperProperties();
  293. }
  294. if (file->videoCover) {
  295. session().data().processPhoto(
  296. file->videoCover->photo,
  297. file->videoCover->photoThumbs);
  298. }
  299. }
  300. _queue.push_back({ itemId, file });
  301. if (!_nextTimer.isActive()) {
  302. maybeSend();
  303. }
  304. }
  305. void Uploader::failed(FullMsgId itemId) {
  306. const auto i = ranges::find(_queue, itemId, &Entry::itemId);
  307. if (i != end(_queue)) {
  308. const auto entry = std::move(*i);
  309. _queue.erase(i);
  310. notifyFailed(entry);
  311. } else if (const auto coverId = _videoIdToCoverId.take(itemId)) {
  312. if (const auto video = _videoWaitingCover.take(*coverId)) {
  313. const auto document = session().data().document(video->id);
  314. if (document->uploading()) {
  315. document->status = FileUploadFailed;
  316. }
  317. _documentFailed.fire_copy(video->fullId);
  318. }
  319. failed(*coverId);
  320. } else if (const auto video = _videoWaitingCover.take(itemId)) {
  321. _videoIdToCoverId.remove(video->fullId);
  322. const auto document = session().data().document(video->id);
  323. if (document->uploading()) {
  324. document->status = FileUploadFailed;
  325. }
  326. _documentFailed.fire_copy(video->fullId);
  327. }
  328. cancelRequests(itemId);
  329. maybeFinishFront();
  330. crl::on_main(this, [=] {
  331. maybeSend();
  332. });
  333. }
  334. void Uploader::notifyFailed(const Entry &entry) {
  335. const auto type = entry.file->type;
  336. if (type == SendMediaType::Photo) {
  337. _photoFailed.fire_copy(entry.itemId);
  338. } else if (type == SendMediaType::File
  339. || type == SendMediaType::ThemeFile
  340. || type == SendMediaType::Audio
  341. || type == SendMediaType::Round) {
  342. const auto document = session().data().document(entry.file->id);
  343. if (document->uploading()) {
  344. document->status = FileUploadFailed;
  345. }
  346. _documentFailed.fire_copy(entry.itemId);
  347. } else if (type == SendMediaType::Secure) {
  348. _secureFailed.fire_copy(entry.itemId);
  349. } else {
  350. Unexpected("Type in Uploader::failed.");
  351. }
  352. }
  353. void Uploader::stopSessions() {
  354. if (ranges::any_of(_sentPerDcIndex, rpl::mappers::_1 != 0)) {
  355. _stopSessionsTimer.callOnce(kKillSessionTimeout);
  356. } else {
  357. for (auto i = 0; i != int(_sentPerDcIndex.size()); ++i) {
  358. _api->instance().stopSession(MTP::uploadDcId(i));
  359. }
  360. _sentPerDcIndex.clear();
  361. _dcIndicesWithFastRequests.clear();
  362. }
  363. }
  364. QByteArray Uploader::readDocPart(not_null<Entry*> entry) {
  365. const auto checked = [&](QByteArray result) {
  366. if ((entry->file->type == SendMediaType::File
  367. || entry->file->type == SendMediaType::ThemeFile
  368. || entry->file->type == SendMediaType::Audio
  369. || entry->file->type == SendMediaType::Round)
  370. && entry->docSize <= kUseBigFilesFrom) {
  371. entry->md5Hash.feed(result.data(), result.size());
  372. }
  373. if (result.isEmpty()
  374. || (result.size() > entry->docPartSize)
  375. || ((result.size() < entry->docPartSize
  376. && entry->docPartsSent + 1 != entry->docPartsCount))) {
  377. return QByteArray();
  378. }
  379. return result;
  380. };
  381. auto &content = entry->file->content;
  382. if (!content.isEmpty()) {
  383. const auto offset = entry->docPartsSent * entry->docPartSize;
  384. return checked(content.mid(offset, entry->docPartSize));
  385. } else if (!entry->docFile) {
  386. const auto filepath = entry->file->filepath;
  387. entry->docFile = std::make_unique<QFile>(filepath);
  388. if (!entry->docFile->open(QIODevice::ReadOnly)) {
  389. return QByteArray();
  390. }
  391. }
  392. return checked(entry->docFile->read(entry->docPartSize));
  393. }
  394. bool Uploader::canAddDcIndex() const {
  395. const auto count = int(_sentPerDcIndex.size());
  396. return (count < kMaxSessionsCount)
  397. && (count == int(_dcIndicesWithFastRequests.size()));
  398. }
  399. std::optional<uchar> Uploader::chooseDcIndexForNextRequest(
  400. const base::flat_set<uchar> &used) {
  401. for (auto i = 0, count = int(_sentPerDcIndex.size()); i != count; ++i) {
  402. if (!_sentPerDcIndex[i] && !used.contains(i)) {
  403. return i;
  404. }
  405. }
  406. if (canAddDcIndex()) {
  407. const auto result = int(_sentPerDcIndex.size());
  408. _sentPerDcIndex.push_back(0);
  409. _dcIndicesWithFastRequests.clear();
  410. _latestDcIndexAdded = crl::now();
  411. DEBUG_LOG(("Uploader: Added dc index %1.").arg(result));
  412. return result;
  413. }
  414. auto result = std::optional<int>();
  415. for (auto i = 0, count = int(_sentPerDcIndex.size()); i != count; ++i) {
  416. if (!used.contains(i)
  417. && (!result.has_value()
  418. || _sentPerDcIndex[i] < _sentPerDcIndex[*result])) {
  419. result = i;
  420. }
  421. }
  422. return result;
  423. }
  424. Uploader::Entry *Uploader::chooseEntryForNextRequest() {
  425. if (!_pendingFromRemovedDcIndices.empty()) {
  426. const auto itemId = _pendingFromRemovedDcIndices.front().itemId;
  427. const auto i = ranges::find(_queue, itemId, &Entry::itemId);
  428. Assert(i != end(_queue));
  429. return &*i;
  430. }
  431. for (auto i = begin(_queue); i != end(_queue); ++i) {
  432. if (i->partsSent < i->parts->size()
  433. || i->docPartsSent < i->docPartsCount) {
  434. return &*i;
  435. }
  436. }
  437. return nullptr;
  438. }
  439. auto Uploader::sendPart(not_null<Entry*> entry, uchar dcIndex)
  440. -> SendResult {
  441. return !_pendingFromRemovedDcIndices.empty()
  442. ? sendPendingPart(entry, dcIndex)
  443. : (entry->partsSent < entry->parts->size())
  444. ? sendSlicedPart(entry, dcIndex)
  445. : sendDocPart(entry, dcIndex);
  446. }
  447. template <typename Prepared>
  448. void Uploader::sendPreparedRequest(Prepared &&prepared, Request &&request) {
  449. auto &sentInSession = _sentPerDcIndex[request.dcIndex];
  450. const auto queued = sentInSession;
  451. sentInSession += int(request.bytes.size());
  452. const auto requestId = _api->request(
  453. std::move(prepared)
  454. ).done([=](const MTPBool &result, mtpRequestId requestId) {
  455. partLoaded(result, requestId);
  456. }).fail([=](const MTP::Error &error, mtpRequestId requestId) {
  457. partFailed(error, requestId);
  458. }).toDC(MTP::uploadDcId(request.dcIndex)).send();
  459. request.sent = crl::now();
  460. request.queued = queued;
  461. _requests.emplace(requestId, std::move(request));
  462. }
  463. auto Uploader::sendPendingPart(not_null<Entry*> entry, uchar dcIndex)
  464. -> SendResult {
  465. Expects(!_pendingFromRemovedDcIndices.empty());
  466. Expects(_pendingFromRemovedDcIndices.front().itemId == entry->itemId);
  467. auto request = std::move(_pendingFromRemovedDcIndices.front());
  468. _pendingFromRemovedDcIndices.erase(begin(_pendingFromRemovedDcIndices));
  469. const auto part = request.part;
  470. const auto bytes = request.bytes;
  471. request.dcIndex = dcIndex;
  472. if (request.bigPart) {
  473. sendPreparedRequest(MTPupload_SaveBigFilePart(
  474. MTP_long(entry->file->id),
  475. MTP_int(part),
  476. MTP_int(entry->docPartsCount),
  477. MTP_bytes(bytes)
  478. ), std::move(request));
  479. } else {
  480. const auto id = request.docPart ? entry->file->id : entry->partsOfId;
  481. sendPreparedRequest(MTPupload_SaveFilePart(
  482. MTP_long(id),
  483. MTP_int(part),
  484. MTP_bytes(bytes)
  485. ), std::move(request));
  486. }
  487. return SendResult::Success;
  488. }
  489. auto Uploader::sendDocPart(not_null<Entry*> entry, uchar dcIndex)
  490. -> SendResult {
  491. const auto itemId = entry->itemId;
  492. const auto alreadySent = _sentPerDcIndex[dcIndex];
  493. const auto willProbablyBeSent = entry->docPartSize;
  494. if (alreadySent + willProbablyBeSent > kMaxUploadPerSession) {
  495. return SendResult::DcIndexFull;
  496. }
  497. Assert(entry->docPartsSent < entry->docPartsCount);
  498. const auto partBytes = readDocPart(entry);
  499. if (partBytes.isEmpty()) {
  500. failed(itemId);
  501. return SendResult::Failed;
  502. }
  503. const auto part = entry->docPartsSent++;
  504. ++entry->docPartsWaiting;
  505. const auto send = [&](auto &&request, bool big) {
  506. sendPreparedRequest(std::move(request), {
  507. .itemId = itemId,
  508. .bytes = partBytes,
  509. .part = part,
  510. .dcIndex = dcIndex,
  511. .docPart = true,
  512. .bigPart = big,
  513. });
  514. };
  515. if (entry->docSize > kUseBigFilesFrom) {
  516. send(MTPupload_SaveBigFilePart(
  517. MTP_long(entry->file->id),
  518. MTP_int(part),
  519. MTP_int(entry->docPartsCount),
  520. MTP_bytes(partBytes)
  521. ), true);
  522. } else {
  523. send(MTPupload_SaveFilePart(
  524. MTP_long(entry->file->id),
  525. MTP_int(part),
  526. MTP_bytes(partBytes)
  527. ), false);
  528. }
  529. return SendResult::Success;
  530. }
  531. auto Uploader::sendSlicedPart(not_null<Entry*> entry, uchar dcIndex)
  532. -> SendResult {
  533. const auto itemId = entry->itemId;
  534. const auto alreadySent = _sentPerDcIndex[dcIndex];
  535. const auto willBeSent = entry->parts->at(entry->partsSent).size();
  536. if (alreadySent + willBeSent >= kMaxUploadPerSession) {
  537. return SendResult::DcIndexFull;
  538. }
  539. ++entry->partsWaiting;
  540. const auto index = entry->partsSent++;
  541. const auto partBytes = entry->parts->at(index);
  542. sendPreparedRequest(MTPupload_SaveFilePart(
  543. MTP_long(entry->partsOfId),
  544. MTP_int(index),
  545. MTP_bytes(partBytes)
  546. ), {
  547. .itemId = itemId,
  548. .bytes = partBytes,
  549. .dcIndex = dcIndex,
  550. });
  551. return SendResult::Success;
  552. }
  553. void Uploader::maybeSend() {
  554. const auto stopping = _stopSessionsTimer.isActive();
  555. if (_queue.empty()) {
  556. if (!stopping) {
  557. _stopSessionsTimer.callOnce(kKillSessionTimeout);
  558. }
  559. _pausedId = FullMsgId();
  560. return;
  561. } else if (_pausedId) {
  562. return;
  563. } else if (stopping) {
  564. _stopSessionsTimer.cancel();
  565. }
  566. auto usedDcIndices = base::flat_set<uchar>();
  567. while (true) {
  568. const auto maybeDcIndex = chooseDcIndexForNextRequest(usedDcIndices);
  569. if (!maybeDcIndex.has_value()) {
  570. break;
  571. }
  572. const auto dcIndex = *maybeDcIndex;
  573. while (true) {
  574. const auto entry = chooseEntryForNextRequest();
  575. if (!entry) {
  576. return;
  577. }
  578. const auto result = sendPart(entry, dcIndex);
  579. if (result == SendResult::DcIndexFull) {
  580. return;
  581. } else if (result == SendResult::Success) {
  582. break;
  583. }
  584. // If this entry failed, we try the next one.
  585. }
  586. if (_sentPerDcIndex[dcIndex] >= kAcceptAsFastIfTotalAtLeast) {
  587. usedDcIndices.emplace(dcIndex);
  588. }
  589. }
  590. if (usedDcIndices.empty()) {
  591. _nextTimer.cancel();
  592. } else {
  593. _nextTimer.callOnce(kUploadRequestInterval);
  594. }
  595. }
  596. void Uploader::cancel(FullMsgId itemId) {
  597. failed(itemId);
  598. }
  599. void Uploader::cancelAll() {
  600. while (!_queue.empty()) {
  601. failed(_queue.front().itemId);
  602. }
  603. clear();
  604. unpause();
  605. }
  606. void Uploader::pause(FullMsgId itemId) {
  607. _pausedId = itemId;
  608. }
  609. void Uploader::unpause() {
  610. _pausedId = FullMsgId();
  611. maybeSend();
  612. }
  613. void Uploader::cancelRequests(FullMsgId itemId) {
  614. for (auto i = begin(_requests); i != end(_requests);) {
  615. if (i->second.itemId == itemId) {
  616. const auto bytes = int(i->second.bytes.size());
  617. _sentPerDcIndex[i->second.dcIndex] -= bytes;
  618. _api->request(i->first).cancel();
  619. i = _requests.erase(i);
  620. } else {
  621. ++i;
  622. }
  623. }
  624. _pendingFromRemovedDcIndices.erase(ranges::remove(
  625. _pendingFromRemovedDcIndices,
  626. itemId,
  627. &Request::itemId
  628. ), end(_pendingFromRemovedDcIndices));
  629. }
  630. void Uploader::cancelAllRequests() {
  631. for (const auto &[requestId, request] : base::take(_requests)) {
  632. _api->request(requestId).cancel();
  633. }
  634. ranges::fill(_sentPerDcIndex, 0);
  635. }
  636. void Uploader::clear() {
  637. _queue.clear();
  638. cancelAllRequests();
  639. stopSessions();
  640. _stopSessionsTimer.cancel();
  641. }
  642. Uploader::Request Uploader::finishRequest(mtpRequestId requestId) {
  643. const auto taken = _requests.take(requestId);
  644. Assert(taken.has_value());
  645. _sentPerDcIndex[taken->dcIndex] -= int(taken->bytes.size());
  646. return *taken;
  647. }
  648. void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
  649. const auto request = finishRequest(requestId);
  650. const auto bytes = int(request.bytes.size());
  651. const auto itemId = request.itemId;
  652. if (mtpIsFalse(result)) { // failed to upload current file
  653. failed(itemId);
  654. return;
  655. }
  656. const auto i = ranges::find(_queue, itemId, &Entry::itemId);
  657. Assert(i != end(_queue));
  658. auto &entry = *i;
  659. const auto now = crl::now();
  660. const auto duration = now - request.sent;
  661. const auto fast = (duration < kFastRequestThreshold);
  662. const auto slowish = !fast;
  663. const auto slow = (duration >= kSlowRequestThreshold);
  664. if (slowish) {
  665. _dcIndicesWithFastRequests.clear();
  666. if (slow) {
  667. const auto elapsed = (now - _latestDcIndexRemoved);
  668. const auto remove = (elapsed >= kWaitForNormalizeTimeout);
  669. if (remove && _sentPerDcIndex.size() > 1) {
  670. DEBUG_LOG(("Uploader: Slow request, removing dc index."));
  671. removeDcIndex();
  672. _latestDcIndexRemoved = now;
  673. } else {
  674. DEBUG_LOG(("Uploader: Slow request, clear fast records."));
  675. }
  676. } else {
  677. DEBUG_LOG(("Uploader: Slow-ish request, clear fast records."));
  678. }
  679. } else if (request.sent > _latestDcIndexAdded
  680. && (request.queued + bytes >= kAcceptAsFastIfTotalAtLeast)) {
  681. if (_dcIndicesWithFastRequests.emplace(request.dcIndex).second) {
  682. DEBUG_LOG(("Uploader: Mark %1 of %2 as fast."
  683. ).arg(request.dcIndex
  684. ).arg(_sentPerDcIndex.size()));
  685. }
  686. }
  687. if (request.docPart) {
  688. --entry.docPartsWaiting;
  689. entry.docSentSize += bytes;
  690. } else {
  691. --entry.partsWaiting;
  692. entry.sentSize += bytes;
  693. }
  694. if (entry.file->type == SendMediaType::Photo) {
  695. const auto photo = session().data().photo(entry.file->id);
  696. if (photo->uploading()) {
  697. photo->uploadingData->size = entry.file->partssize;
  698. photo->uploadingData->offset = entry.sentSize;
  699. }
  700. _photoProgress.fire_copy(itemId);
  701. } else if (entry.file->type == SendMediaType::File
  702. || entry.file->type == SendMediaType::ThemeFile
  703. || entry.file->type == SendMediaType::Audio
  704. || entry.file->type == SendMediaType::Round) {
  705. const auto document = session().data().document(entry.file->id);
  706. if (document->uploading()) {
  707. document->uploadingData->offset = std::min(
  708. document->uploadingData->size,
  709. entry.docSentSize);
  710. }
  711. _documentProgress.fire_copy(itemId);
  712. } else if (entry.file->type == SendMediaType::Secure) {
  713. _secureProgress.fire_copy({
  714. .fullId = itemId,
  715. .offset = entry.sentSize,
  716. .size = entry.file->partssize,
  717. });
  718. }
  719. if (request.nonPremiumDelayed) {
  720. _nonPremiumDelays.fire_copy(itemId);
  721. }
  722. if (!_queue.empty() && itemId == _queue.front().itemId) {
  723. maybeFinishFront();
  724. }
  725. maybeSend();
  726. }
  727. void Uploader::removeDcIndex() {
  728. Expects(_sentPerDcIndex.size() > 1);
  729. const auto dcIndex = int(_sentPerDcIndex.size()) - 1;
  730. for (auto i = begin(_requests); i != end(_requests);) {
  731. if (i->second.dcIndex == dcIndex) {
  732. const auto bytes = int(i->second.bytes.size());
  733. _sentPerDcIndex[dcIndex] -= bytes;
  734. _api->request(i->first).cancel();
  735. _pendingFromRemovedDcIndices.push_back(std::move(i->second));
  736. i = _requests.erase(i);
  737. } else {
  738. ++i;
  739. }
  740. }
  741. Assert(_sentPerDcIndex.back() == 0);
  742. _sentPerDcIndex.pop_back();
  743. _dcIndicesWithFastRequests.remove(dcIndex);
  744. _api->instance().stopSession(MTP::uploadDcId(dcIndex));
  745. DEBUG_LOG(("Uploader: Removed dc index %1.").arg(dcIndex));
  746. }
  747. void Uploader::maybeFinishFront() {
  748. while (!_queue.empty()) {
  749. const auto &entry = _queue.front();
  750. if (entry.partsSent >= entry.parts->size()
  751. && entry.docPartsSent >= entry.docPartsCount
  752. && !entry.partsWaiting
  753. && !entry.docPartsWaiting) {
  754. finishFront();
  755. } else {
  756. break;
  757. }
  758. }
  759. }
  760. void Uploader::finishFront() {
  761. Expects(!_queue.empty());
  762. auto entry = std::move(_queue.front());
  763. _queue.erase(_queue.begin());
  764. const auto options = entry.file
  765. ? entry.file->to.options
  766. : Api::SendOptions();
  767. const auto edit = entry.file &&
  768. entry.file->to.replaceMediaOf;
  769. const auto attachedStickers = entry.file
  770. ? entry.file->attachedStickers
  771. : std::vector<MTPInputDocument>();
  772. if (entry.file->type == SendMediaType::Photo) {
  773. auto photoFilename = entry.file->filename;
  774. if (!photoFilename.endsWith(u".jpg"_q, Qt::CaseInsensitive)) {
  775. // Server has some extensions checking for inputMediaUploadedPhoto,
  776. // so force the extension to be .jpg anyway. It doesn't matter,
  777. // because the filename from inputFile is not used anywhere.
  778. photoFilename += u".jpg"_q;
  779. }
  780. const auto md5 = entry.file->filemd5;
  781. const auto file = MTP_inputFile(
  782. MTP_long(entry.file->id),
  783. MTP_int(entry.parts->size()),
  784. MTP_string(photoFilename),
  785. MTP_bytes(md5));
  786. auto ready = UploadedMedia{
  787. .id = entry.file->id,
  788. .fullId = entry.itemId,
  789. .info = {
  790. .file = file,
  791. .attachedStickers = attachedStickers,
  792. },
  793. .options = options,
  794. .edit = edit,
  795. };
  796. const auto i = _videoWaitingCover.find(entry.itemId);
  797. if (i != end(_videoWaitingCover)) {
  798. uploadCoverAsPhoto(i->second.fullId, std::move(ready));
  799. } else {
  800. _photoReady.fire(std::move(ready));
  801. }
  802. } else if (entry.file->type == SendMediaType::File
  803. || entry.file->type == SendMediaType::ThemeFile
  804. || entry.file->type == SendMediaType::Audio
  805. || entry.file->type == SendMediaType::Round) {
  806. QByteArray docMd5(32, Qt::Uninitialized);
  807. hashMd5Hex(entry.md5Hash.result(), docMd5.data());
  808. const auto file = (entry.docSize > kUseBigFilesFrom)
  809. ? MTP_inputFileBig(
  810. MTP_long(entry.file->id),
  811. MTP_int(entry.docPartsCount),
  812. MTP_string(entry.file->filename))
  813. : MTP_inputFile(
  814. MTP_long(entry.file->id),
  815. MTP_int(entry.docPartsCount),
  816. MTP_string(entry.file->filename),
  817. MTP_bytes(docMd5));
  818. const auto thumb = [&]() -> std::optional<MTPInputFile> {
  819. if (entry.parts->empty()) {
  820. return std::nullopt;
  821. }
  822. const auto thumbFilename = entry.file->thumbname;
  823. const auto thumbMd5 = entry.file->thumbmd5;
  824. return MTP_inputFile(
  825. MTP_long(entry.file->thumbId),
  826. MTP_int(entry.parts->size()),
  827. MTP_string(thumbFilename),
  828. MTP_bytes(thumbMd5));
  829. }();
  830. auto ready = UploadedMedia{
  831. .id = entry.file->id,
  832. .fullId = entry.itemId,
  833. .info = {
  834. .file = file,
  835. .thumb = thumb,
  836. .attachedStickers = attachedStickers,
  837. },
  838. .options = options,
  839. .edit = edit,
  840. };
  841. if (entry.file->videoCover) {
  842. uploadVideoCover(std::move(ready), entry.file->videoCover);
  843. } else {
  844. _documentReady.fire(std::move(ready));
  845. }
  846. } else if (entry.file->type == SendMediaType::Secure) {
  847. _secureReady.fire({
  848. entry.itemId,
  849. entry.file->id,
  850. int(entry.parts->size()),
  851. });
  852. }
  853. }
  854. void Uploader::partFailed(const MTP::Error &error, mtpRequestId requestId) {
  855. const auto request = finishRequest(requestId);
  856. failed(request.itemId);
  857. }
  858. void Uploader::uploadVideoCover(
  859. UploadedMedia &&video,
  860. std::shared_ptr<FilePrepareResult> videoCover) {
  861. const auto coverId = FullMsgId(
  862. videoCover->to.peer,
  863. session().data().nextLocalMessageId());
  864. _videoIdToCoverId.emplace(video.fullId, coverId);
  865. _videoWaitingCover.emplace(coverId, std::move(video));
  866. upload(coverId, videoCover);
  867. }
  868. void Uploader::uploadCoverAsPhoto(
  869. FullMsgId videoId,
  870. UploadedMedia &&cover) {
  871. const auto coverId = cover.fullId;
  872. _api->request(MTPmessages_UploadMedia(
  873. MTP_flags(0),
  874. MTPstring(), // business_connection_id
  875. session().data().peer(videoId.peer)->input,
  876. MTP_inputMediaUploadedPhoto(
  877. MTP_flags(0),
  878. cover.info.file,
  879. MTP_vector<MTPInputDocument>(0),
  880. MTP_int(0))
  881. )).done([=](const MTPMessageMedia &result) {
  882. result.match([&](const MTPDmessageMediaPhoto &data) {
  883. const auto photo = data.vphoto();
  884. if (!photo || photo->type() != mtpc_photo) {
  885. failed(coverId);
  886. return;
  887. }
  888. const auto &fields = photo->c_photo();
  889. if (const auto coverId = _videoIdToCoverId.take(videoId)) {
  890. if (auto video = _videoWaitingCover.take(*coverId)) {
  891. video->info.videoCover = MTP_inputPhoto(
  892. fields.vid(),
  893. fields.vaccess_hash(),
  894. fields.vfile_reference());
  895. _documentReady.fire(std::move(*video));
  896. }
  897. }
  898. }, [&](const auto &) {
  899. failed(coverId);
  900. });
  901. }).fail([=] {
  902. failed(coverId);
  903. }).send();
  904. }
  905. } // namespace Storage