api_ringtones.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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 "api/api_ringtones.h"
  8. #include "api/api_toggling_media.h"
  9. #include "apiwrap.h"
  10. #include "base/random.h"
  11. #include "base/unixtime.h"
  12. #include "data/data_document.h"
  13. #include "data/data_document_media.h"
  14. #include "data/data_file_origin.h"
  15. #include "data/data_session.h"
  16. #include "data/notify/data_notify_settings.h"
  17. #include "main/main_app_config.h"
  18. #include "main/main_session.h"
  19. #include "storage/file_upload.h"
  20. #include "storage/localimageloader.h"
  21. namespace Api {
  22. namespace {
  23. std::shared_ptr<FilePrepareResult> PrepareRingtoneDocument(
  24. MTP::DcId dcId,
  25. const QString &filename,
  26. const QString &filemime,
  27. const QByteArray &content) {
  28. const auto id = base::RandomValue<DocumentId>();
  29. auto attributes = QVector<MTPDocumentAttribute>(
  30. 1,
  31. MTP_documentAttributeFilename(MTP_string(filename)));
  32. auto result = MakePreparedFile({
  33. .id = id,
  34. .type = SendMediaType::File,
  35. });
  36. result->filename = filename;
  37. result->content = content;
  38. result->filesize = content.size();
  39. result->setFileData(content);
  40. result->document = MTP_document(
  41. MTP_flags(0),
  42. MTP_long(id),
  43. MTP_long(0),
  44. MTP_bytes(),
  45. MTP_int(base::unixtime::now()),
  46. MTP_string(filemime),
  47. MTP_long(content.size()),
  48. MTP_vector<MTPPhotoSize>(),
  49. MTPVector<MTPVideoSize>(),
  50. MTP_int(dcId),
  51. MTP_vector<MTPDocumentAttribute>(std::move(attributes)));
  52. return result;
  53. }
  54. } // namespace
  55. Ringtones::Ringtones(not_null<ApiWrap*> api)
  56. : _session(&api->session())
  57. , _api(&api->instance()) {
  58. crl::on_main(_session, [=] {
  59. // You can't use _session->lifetime() in the constructor,
  60. // only queued, because it is not constructed yet.
  61. _session->uploader().documentReady(
  62. ) | rpl::start_with_next([=](const Storage::UploadedMedia &data) {
  63. ready(data.fullId, data.info.file);
  64. }, _session->lifetime());
  65. });
  66. }
  67. void Ringtones::upload(
  68. const QString &filename,
  69. const QString &filemime,
  70. const QByteArray &content) {
  71. const auto ready = PrepareRingtoneDocument(
  72. _api.instance().mainDcId(),
  73. filename,
  74. filemime,
  75. content);
  76. const auto uploadedData = UploadedData{ filename, filemime, content };
  77. const auto fakeId = FullMsgId(
  78. _session->userPeerId(),
  79. _session->data().nextLocalMessageId());
  80. const auto already = ranges::find_if(
  81. _uploads,
  82. [&](const auto &d) {
  83. return uploadedData.filemime == d.second.filemime
  84. && uploadedData.filename == d.second.filename;
  85. });
  86. if (already != end(_uploads)) {
  87. _session->uploader().cancel(already->first);
  88. _uploads.erase(already);
  89. }
  90. _uploads.emplace(fakeId, uploadedData);
  91. _session->uploader().upload(fakeId, ready);
  92. }
  93. void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) {
  94. const auto maybeUploadedData = _uploads.take(msgId);
  95. if (!maybeUploadedData) {
  96. return;
  97. }
  98. const auto uploadedData = *maybeUploadedData;
  99. _api.request(MTPaccount_UploadRingtone(
  100. file,
  101. MTP_string(uploadedData.filename),
  102. MTP_string(uploadedData.filemime)
  103. )).done([=, content = uploadedData.content](const MTPDocument &result) {
  104. const auto document = _session->data().processDocument(result);
  105. _list.documents.insert(_list.documents.begin(), document->id);
  106. const auto media = document->createMediaView();
  107. media->setBytes(content);
  108. document->owner().notifySettings().cacheSound(document);
  109. _uploadDones.fire_copy(document->id);
  110. }).fail([=](const MTP::Error &error) {
  111. _uploadFails.fire_copy(error.type());
  112. }).send();
  113. }
  114. void Ringtones::requestList() {
  115. if (_list.requestId) {
  116. return;
  117. }
  118. _list.requestId = _api.request(
  119. MTPaccount_GetSavedRingtones(MTP_long(_list.hash))
  120. ).done([=](const MTPaccount_SavedRingtones &result) {
  121. _list.requestId = 0;
  122. result.match([&](const MTPDaccount_savedRingtones &data) {
  123. _list.hash = data.vhash().v;
  124. _list.documents.clear();
  125. _list.documents.reserve(data.vringtones().v.size());
  126. for (const auto &d : data.vringtones().v) {
  127. const auto document = _session->data().processDocument(d);
  128. document->forceToCache(true);
  129. _list.documents.emplace_back(document->id);
  130. }
  131. _list.updates.fire({});
  132. }, [&](const MTPDaccount_savedRingtonesNotModified &) {
  133. });
  134. }).fail([=] {
  135. _list.requestId = 0;
  136. }).send();
  137. }
  138. const Ringtones::Ids &Ringtones::list() const {
  139. return _list.documents;
  140. }
  141. rpl::producer<> Ringtones::listUpdates() const {
  142. return _list.updates.events();
  143. }
  144. rpl::producer<QString> Ringtones::uploadFails() const {
  145. return _uploadFails.events();
  146. }
  147. rpl::producer<DocumentId> Ringtones::uploadDones() const {
  148. return _uploadDones.events();
  149. }
  150. void Ringtones::applyUpdate() {
  151. _list.hash = 0;
  152. _list.documents.clear();
  153. requestList();
  154. }
  155. void Ringtones::remove(DocumentId id) {
  156. if (const auto document = _session->data().document(id)) {
  157. ToggleSavedRingtone(
  158. document,
  159. Data::FileOriginRingtones(),
  160. crl::guard(&document->session(), [=] {
  161. const auto it = ranges::find(_list.documents, id);
  162. if (it != end(_list.documents)) {
  163. _list.documents.erase(it);
  164. }
  165. }),
  166. false);
  167. }
  168. }
  169. int64 Ringtones::maxSize() const {
  170. return _session->appConfig().get<int>(
  171. u"ringtone_size_max"_q,
  172. 100 * 1024);
  173. }
  174. int Ringtones::maxSavedCount() const {
  175. return _session->appConfig().get<int>(
  176. u"ringtone_saved_count_max"_q,
  177. 100);
  178. }
  179. crl::time Ringtones::maxDuration() const {
  180. return crl::time(1000) * _session->appConfig().get<int>(
  181. u"ringtone_duration_max"_q,
  182. 5);
  183. }
  184. } // namespace Api