media_player_instance.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386
  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 "media/player/media_player_instance.h"
  8. #include "data/data_document.h"
  9. #include "data/data_session.h"
  10. #include "data/data_changes.h"
  11. #include "data/data_streaming.h"
  12. #include "data/data_file_click_handler.h"
  13. #include "base/options.h"
  14. #include "base/random.h"
  15. #include "base/power_save_blocker.h"
  16. #include "media/audio/media_audio.h"
  17. #include "media/audio/media_audio_capture.h"
  18. #include "media/streaming/media_streaming_instance.h"
  19. #include "media/streaming/media_streaming_player.h"
  20. #include "media/view/media_view_playback_progress.h"
  21. #include "calls/calls_instance.h"
  22. #include "history/history.h"
  23. #include "history/history_item.h"
  24. #include "data/data_media_types.h"
  25. #include "data/data_file_origin.h"
  26. #include "core/shortcuts.h"
  27. #include "core/application.h"
  28. #include "core/core_settings.h"
  29. #include "window/window_controller.h"
  30. #include "mainwindow.h"
  31. #include "main/main_domain.h" // Domain::activeSessionValue.
  32. #include "main/main_session.h"
  33. #include "main/main_account.h" // session->account().sessionChanges().
  34. #include "main/main_session_settings.h"
  35. #include "storage/storage_account.h"
  36. namespace Media {
  37. namespace Player {
  38. namespace {
  39. Instance *SingleInstance = nullptr;
  40. // Preload X message ids before and after current.
  41. constexpr auto kIdsLimit = 32;
  42. // Preload next messages if we went further from current than that.
  43. constexpr auto kIdsPreloadAfter = 28;
  44. constexpr auto kShufflePlaylistLimit = 10'000;
  45. constexpr auto kRememberShuffledOrderItems = 16;
  46. constexpr auto kMinLengthForSavePositionVideo = TimeId(60); // 1 minute.
  47. constexpr auto kMinLengthForSavePositionMusic = 20 * TimeId(60); // 20.
  48. base::options::toggle OptionDisableAutoplayNext({
  49. .id = kOptionDisableAutoplayNext,
  50. .name = "Disable auto-play of the next track",
  51. .description = "Disable auto-play of the next "
  52. "Audio file / Voice Message / Video message.",
  53. });
  54. } // namespace
  55. const char kOptionDisableAutoplayNext[] = "disable-autoplay-next";
  56. struct Instance::Streamed {
  57. Streamed(
  58. AudioMsgId id,
  59. std::shared_ptr<Streaming::Document> document);
  60. AudioMsgId id;
  61. Streaming::Instance instance;
  62. View::PlaybackProgress progress;
  63. bool clearing = false;
  64. rpl::lifetime lifetime;
  65. };
  66. struct Instance::ShuffleData {
  67. using UniversalMsgId = MsgId;
  68. std::vector<UniversalMsgId> playlist;
  69. std::vector<UniversalMsgId> nonPlayedIds;
  70. std::vector<UniversalMsgId> playedIds;
  71. History *history = nullptr;
  72. MsgId topicRootId = 0;
  73. History *migrated = nullptr;
  74. bool scheduled = false;
  75. int indexInPlayedIds = 0;
  76. bool allLoaded = false;
  77. rpl::lifetime nextSliceLifetime;
  78. rpl::lifetime lifetime;
  79. };
  80. void start(not_null<Audio::Instance*> instance) {
  81. Audio::Start(instance);
  82. Capture::Start();
  83. SingleInstance = new Instance();
  84. }
  85. void finish(not_null<Audio::Instance*> instance) {
  86. delete base::take(SingleInstance);
  87. Capture::Finish();
  88. Audio::Finish(instance);
  89. }
  90. void SaveLastPlaybackPosition(
  91. not_null<DocumentData*> document,
  92. const TrackState &state) {
  93. const auto limit = document->isVideoFile()
  94. ? kMinLengthForSavePositionVideo
  95. : kMinLengthForSavePositionMusic;
  96. const auto time = (state.position == kTimeUnknown
  97. || state.length == kTimeUnknown
  98. || state.state == State::PausedAtEnd
  99. || IsStopped(state.state))
  100. ? TimeId(0)
  101. : (state.length >= limit * state.frequency)
  102. ? (state.position / state.frequency) * crl::time(1000)
  103. : TimeId(0);
  104. auto &session = document->session();
  105. if (session.local().mediaLastPlaybackPosition(document->id) != time) {
  106. session.local().setMediaLastPlaybackPosition(document->id, time);
  107. }
  108. }
  109. Instance::Streamed::Streamed(
  110. AudioMsgId id,
  111. std::shared_ptr<Streaming::Document> document)
  112. : id(id)
  113. , instance(std::move(document), nullptr) {
  114. }
  115. Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview)
  116. : type(type)
  117. , overview(overview) {
  118. }
  119. Instance::Data::Data(Data &&other) = default;
  120. Instance::Data &Instance::Data::operator=(Data &&other) = default;
  121. Instance::Data::~Data() = default;
  122. Instance::Instance()
  123. : _songData(AudioMsgId::Type::Song, SharedMediaType::MusicFile)
  124. , _voiceData(AudioMsgId::Type::Voice, SharedMediaType::RoundVoiceFile) {
  125. Media::Player::Updated(
  126. ) | rpl::start_with_next([=](const AudioMsgId &audioId) {
  127. handleSongUpdate(audioId);
  128. }, _lifetime);
  129. repeatChanges(
  130. &_songData
  131. ) | rpl::start_with_next([=](RepeatMode mode) {
  132. if (mode == RepeatMode::All) {
  133. refreshPlaylist(&_songData);
  134. }
  135. }, _lifetime);
  136. orderChanges(
  137. &_songData
  138. ) | rpl::start_with_next([=](OrderMode mode) {
  139. if (mode == OrderMode::Shuffle) {
  140. validateShuffleData(&_songData);
  141. } else {
  142. _songData.shuffleData = nullptr;
  143. }
  144. }, _lifetime);
  145. using namespace rpl::mappers;
  146. rpl::combine(
  147. Core::App().calls().currentCallValue(),
  148. Core::App().calls().currentGroupCallValue(),
  149. _1 || _2
  150. ) | rpl::start_with_next([=](bool call) {
  151. if (call) {
  152. pauseOnCall(AudioMsgId::Type::Voice);
  153. pauseOnCall(AudioMsgId::Type::Song);
  154. } else {
  155. resumeOnCall(AudioMsgId::Type::Voice);
  156. resumeOnCall(AudioMsgId::Type::Song);
  157. }
  158. }, _lifetime);
  159. setupShortcuts();
  160. }
  161. Instance::~Instance() = default;
  162. AudioMsgId::Type Instance::getActiveType() const {
  163. if (const auto data = getData(AudioMsgId::Type::Voice)) {
  164. if (data->current) {
  165. const auto state = getState(data->type);
  166. if (!IsStoppedOrStopping(state.state)) {
  167. return data->type;
  168. }
  169. }
  170. }
  171. return AudioMsgId::Type::Song;
  172. }
  173. void Instance::handleSongUpdate(const AudioMsgId &audioId) {
  174. emitUpdate(audioId.type(), [&](const AudioMsgId &playing) {
  175. return (audioId == playing);
  176. });
  177. }
  178. void Instance::setCurrent(const AudioMsgId &audioId) {
  179. if (const auto data = getData(audioId.type())) {
  180. if (data->current == audioId) {
  181. return;
  182. }
  183. const auto changed = [&](const AudioMsgId & check) {
  184. return (check.audio() != audioId.audio())
  185. || (check.contextId() != audioId.contextId());
  186. };
  187. if (changed(data->current)
  188. && data->streamed
  189. && changed(data->streamed->id)) {
  190. clearStreamed(data);
  191. }
  192. data->current = audioId;
  193. data->isPlaying = false;
  194. const auto item = (audioId.audio() && audioId.contextId())
  195. ? audioId.audio()->owner().message(audioId.contextId())
  196. : nullptr;
  197. if (item) {
  198. setHistory(data, item->history());
  199. } else {
  200. setHistory(
  201. data,
  202. nullptr,
  203. audioId.audio() ? &audioId.audio()->session() : nullptr);
  204. }
  205. _trackChanged.fire_copy(data->type);
  206. refreshPlaylist(data);
  207. }
  208. }
  209. void Instance::setHistory(
  210. not_null<Data*> data,
  211. History *history,
  212. Main::Session *sessionFallback) {
  213. if (history) {
  214. data->history = history->migrateToOrMe();
  215. data->topicRootId = 0;
  216. data->migrated = data->history->migrateFrom();
  217. setSession(data, &history->session());
  218. } else {
  219. data->history = data->migrated = nullptr;
  220. setSession(data, sessionFallback);
  221. }
  222. }
  223. void Instance::setSession(not_null<Data*> data, Main::Session *session) {
  224. if (data->session == session) {
  225. return;
  226. }
  227. data->playlistLifetime.destroy();
  228. data->playlistOtherLifetime.destroy();
  229. data->sessionLifetime.destroy();
  230. data->session = session;
  231. if (session) {
  232. session->account().sessionChanges(
  233. ) | rpl::start_with_next([=] {
  234. setSession(data, nullptr);
  235. }, data->sessionLifetime);
  236. session->data().documentLoadProgress(
  237. ) | rpl::filter([=](not_null<DocumentData*> document) {
  238. // Before refactoring it was called only for audio files.
  239. return document->isAudioFile();
  240. }) | rpl::start_with_next([=](not_null<DocumentData*> document) {
  241. const auto type = AudioMsgId::Type::Song;
  242. emitUpdate(type, [&](const AudioMsgId &audioId) {
  243. return (audioId.audio() == document);
  244. });
  245. }, data->sessionLifetime);
  246. session->data().itemRemoved(
  247. ) | rpl::filter([=](not_null<const HistoryItem*> item) {
  248. return (data->current.contextId() == item->fullId());
  249. }) | rpl::start_with_next([=] {
  250. stopAndClear(data);
  251. }, data->sessionLifetime);
  252. } else {
  253. stopAndClear(data);
  254. }
  255. }
  256. void Instance::clearStreamed(not_null<Data*> data, bool savePosition) {
  257. if (!data->streamed || data->streamed->clearing) {
  258. return;
  259. }
  260. data->streamed->clearing = true;
  261. if (savePosition) {
  262. SaveLastPlaybackPosition(
  263. data->current.audio(),
  264. data->streamed->instance.player().prepareLegacyState());
  265. }
  266. data->streamed->instance.stop();
  267. data->isPlaying = false;
  268. requestRoundVideoResize();
  269. emitUpdate(data->type);
  270. data->streamed = nullptr;
  271. _roundPlaying = false;
  272. Core::App().floatPlayerToggleGifsPaused(false);
  273. }
  274. void Instance::refreshPlaylist(not_null<Data*> data) {
  275. if (!validPlaylist(data)) {
  276. validatePlaylist(data);
  277. } else {
  278. refreshOtherPlaylist(data);
  279. }
  280. }
  281. void Instance::refreshOtherPlaylist(not_null<Data*> data) {
  282. if (!validOtherPlaylist(data)) {
  283. validateOtherPlaylist(data);
  284. }
  285. playlistUpdated(data);
  286. }
  287. void Instance::playlistUpdated(not_null<Data*> data) {
  288. if (data->playlistSlice) {
  289. const auto fullId = data->current.contextId();
  290. data->playlistIndex = data->playlistSlice->indexOf(fullId);
  291. if (order(data) == OrderMode::Shuffle) {
  292. validateShuffleData(data);
  293. }
  294. } else {
  295. data->playlistIndex = std::nullopt;
  296. data->shuffleData = nullptr;
  297. }
  298. data->playlistChanges.fire({});
  299. }
  300. bool Instance::validPlaylist(not_null<const Data*> data) const {
  301. if (const auto key = playlistKey(data)) {
  302. if (!data->playlistSlice) {
  303. return false;
  304. }
  305. using Key = SliceKey;
  306. const auto inSameDomain = [](const Key &a, const Key &b) {
  307. return (a.peerId == b.peerId)
  308. && (a.topicRootId == b.topicRootId)
  309. && (a.migratedPeerId == b.migratedPeerId);
  310. };
  311. const auto countDistanceInData = [&](const Key &a, const Key &b) {
  312. return [&](const SparseIdsMergedSlice &data) {
  313. return inSameDomain(a, b)
  314. ? data.distance(a, b)
  315. : std::optional<int>();
  316. };
  317. };
  318. if (key == data->playlistRequestedKey) {
  319. return true;
  320. } else if (!data->playlistSliceKey
  321. || !data->playlistRequestedKey
  322. || *data->playlistRequestedKey != *data->playlistSliceKey) {
  323. return false;
  324. }
  325. auto distance = data->playlistSlice
  326. | countDistanceInData(*key, *data->playlistRequestedKey)
  327. | func::abs;
  328. if (distance) {
  329. return (*distance < kIdsPreloadAfter);
  330. }
  331. }
  332. return !data->playlistSlice;
  333. }
  334. void Instance::validatePlaylist(not_null<Data*> data) {
  335. data->playlistLifetime.destroy();
  336. if (const auto key = playlistKey(data)) {
  337. data->playlistRequestedKey = key;
  338. const auto sharedMediaViewer = (key->topicRootId
  339. == SparseIdsMergedSlice::kScheduledTopicId)
  340. ? SharedScheduledMediaViewer
  341. : SharedMediaMergedViewer;
  342. sharedMediaViewer(
  343. &data->history->session(),
  344. SharedMediaMergedKey(*key, data->overview),
  345. kIdsLimit,
  346. kIdsLimit
  347. ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
  348. data->playlistSlice = std::move(update);
  349. data->playlistSliceKey = key;
  350. refreshOtherPlaylist(data);
  351. }, data->playlistLifetime);
  352. } else {
  353. data->playlistSlice = std::nullopt;
  354. data->playlistSliceKey = data->playlistRequestedKey = std::nullopt;
  355. refreshOtherPlaylist(data);
  356. }
  357. }
  358. auto Instance::playlistKey(not_null<const Data*> data) const
  359. -> std::optional<SliceKey> {
  360. const auto contextId = data->current.contextId();
  361. const auto history = data->history;
  362. if (!contextId || !history) {
  363. return {};
  364. }
  365. const auto item = data->history->owner().message(contextId);
  366. if (!item || (!item->isRegular() && !item->isScheduled())) {
  367. return {};
  368. }
  369. const auto universalId = (contextId.peer == history->peer->id)
  370. ? contextId.msg
  371. : (contextId.msg - ServerMaxMsgId);
  372. return SliceKey(
  373. data->history->peer->id,
  374. (item->isScheduled()
  375. ? SparseIdsMergedSlice::kScheduledTopicId
  376. : data->topicRootId),
  377. data->migrated ? data->migrated->peer->id : 0,
  378. universalId);
  379. }
  380. bool Instance::validOtherPlaylist(not_null<const Data*> data) const {
  381. if (const auto key = playlistOtherKey(data)) {
  382. return data->playlistOtherSlice
  383. && (key == data->playlistOtherRequestedKey);
  384. }
  385. return !data->playlistOtherSlice;
  386. }
  387. void Instance::validateOtherPlaylist(not_null<Data*> data) {
  388. data->playlistOtherLifetime.destroy();
  389. if (const auto key = playlistOtherKey(data)) {
  390. data->playlistOtherRequestedKey = key;
  391. SharedMediaMergedViewer(
  392. &data->history->session(),
  393. SharedMediaMergedKey(*key, data->overview),
  394. kIdsLimit,
  395. kIdsLimit
  396. ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
  397. data->playlistOtherSlice = std::move(update);
  398. playlistUpdated(data);
  399. }, data->playlistOtherLifetime);
  400. } else {
  401. data->playlistOtherSlice = std::nullopt;
  402. data->playlistOtherRequestedKey = std::nullopt;
  403. playlistUpdated(data);
  404. }
  405. }
  406. auto Instance::playlistOtherKey(not_null<const Data*> data) const
  407. -> std::optional<SliceKey> {
  408. if (repeat(data) != RepeatMode::All
  409. || order(data) == OrderMode::Shuffle
  410. || !data->playlistSlice
  411. || (data->playlistSlice->skippedBefore() != 0
  412. && data->playlistSlice->skippedAfter() != 0)
  413. || (data->playlistSlice->skippedBefore() == 0
  414. && data->playlistSlice->skippedAfter() == 0)) {
  415. return {};
  416. }
  417. const auto contextId = data->current.contextId();
  418. const auto history = data->history;
  419. if (!contextId || !history) {
  420. return {};
  421. }
  422. const auto item = data->history->owner().message(contextId);
  423. if (!item || !item->isRegular()) {
  424. return {};
  425. }
  426. return SliceKey(
  427. data->history->peer->id,
  428. data->topicRootId,
  429. data->migrated ? data->migrated->peer->id : 0,
  430. (data->playlistSlice->skippedBefore() == 0
  431. ? ServerMaxMsgId - 1
  432. : data->migrated
  433. ? (1 - ServerMaxMsgId)
  434. : 1));
  435. }
  436. HistoryItem *Instance::itemByIndex(not_null<Data*> data, int index) {
  437. if (!data->playlistSlice
  438. || index < 0
  439. || index >= data->playlistSlice->size()) {
  440. return nullptr;
  441. }
  442. Assert(data->history != nullptr);
  443. const auto fullId = (*data->playlistSlice)[index];
  444. return data->history->owner().message(fullId);
  445. }
  446. bool Instance::moveInPlaylist(
  447. not_null<Data*> data,
  448. int delta,
  449. bool autonext) {
  450. if (!data->playlistIndex) {
  451. return false;
  452. }
  453. const auto jumpByItem = [&](not_null<HistoryItem*> item) {
  454. if (const auto media = item->media()) {
  455. if (media->ttlSeconds()) {
  456. return false;
  457. }
  458. if (const auto document = media->document()) {
  459. if (autonext) {
  460. _switchToNext.fire({
  461. data->current,
  462. item->fullId()
  463. });
  464. }
  465. if (document->isAudioFile()
  466. || document->isVoiceMessage()
  467. || document->isVideoMessage()) {
  468. play(AudioMsgId(document, item->fullId()));
  469. }
  470. return true;
  471. }
  472. }
  473. return false;
  474. };
  475. const auto jumpById = [&](FullMsgId id) {
  476. return jumpByItem(data->history->owner().message(id));
  477. };
  478. const auto repeatAll = (repeat(data) == RepeatMode::All);
  479. if (order(data) == OrderMode::Shuffle) {
  480. const auto raw = data->shuffleData.get();
  481. if (!raw || !raw->history) {
  482. return false;
  483. }
  484. const auto universal = computeCurrentUniversalId(data);
  485. const auto byUniversal = [&](ShuffleData::UniversalMsgId id) {
  486. return (id < 0 && raw->migrated)
  487. ? jumpById({ raw->migrated->peer->id, id + ServerMaxMsgId })
  488. : jumpById({ raw->history->peer->id, id });
  489. };
  490. if (universal && raw->indexInPlayedIds == raw->playedIds.size()) {
  491. raw->playedIds.push_back(universal);
  492. const auto i = ranges::find(raw->nonPlayedIds, universal);
  493. if (i != end(raw->nonPlayedIds)) {
  494. raw->nonPlayedIds.erase(i);
  495. }
  496. }
  497. if (repeatAll) {
  498. ensureShuffleMove(data, delta);
  499. }
  500. if (raw->nonPlayedIds.empty()
  501. && raw->indexInPlayedIds + 1 == raw->playedIds.size()) {
  502. raw->nonPlayedIds.push_back(raw->playedIds.back());
  503. raw->playedIds.pop_back();
  504. }
  505. const auto shuffleCompleted = raw->nonPlayedIds.empty()
  506. || (raw->nonPlayedIds.size() == 1
  507. && raw->nonPlayedIds.front() == universal);
  508. if (delta < 0) {
  509. return (raw->indexInPlayedIds > 0)
  510. && byUniversal(raw->playedIds[--raw->indexInPlayedIds]);
  511. } else if (raw->indexInPlayedIds + 1 < raw->playedIds.size()) {
  512. return byUniversal(raw->playedIds[++raw->indexInPlayedIds]);
  513. }
  514. if (shuffleCompleted) {
  515. return false;
  516. } else if (raw->indexInPlayedIds < raw->playedIds.size()) {
  517. ++raw->indexInPlayedIds;
  518. }
  519. const auto index = base::RandomIndex(raw->nonPlayedIds.size());
  520. return byUniversal(raw->nonPlayedIds[index]);
  521. }
  522. const auto newIndex = *data->playlistIndex
  523. + (order(data) == OrderMode::Reverse ? -delta : delta);
  524. const auto useIndex = (!repeatAll
  525. || !data->playlistSlice
  526. || data->playlistSlice->skippedAfter() != 0
  527. || data->playlistSlice->skippedBefore() != 0
  528. || !data->playlistSlice->size())
  529. ? newIndex
  530. : ((newIndex + int(data->playlistSlice->size()))
  531. % int(data->playlistSlice->size()));
  532. if (const auto item = itemByIndex(data, useIndex)) {
  533. return jumpByItem(item);
  534. } else if (repeatAll
  535. && data->playlistOtherSlice
  536. && data->playlistOtherSlice->size() > 0) {
  537. const auto &other = *data->playlistOtherSlice;
  538. if (newIndex < 0 && other.skippedAfter() == 0) {
  539. return jumpById(other[other.size() - 1]);
  540. } else if (newIndex > 0 && other.skippedBefore() == 0) {
  541. return jumpById(other[0]);
  542. }
  543. }
  544. return false;
  545. }
  546. void Instance::updatePowerSaveBlocker(
  547. not_null<Data*> data,
  548. const TrackState &state) {
  549. const auto block = !IsPausedOrPausing(state.state)
  550. && !IsStoppedOrStopping(state.state);
  551. const auto blockVideo = block
  552. && data->current.audio()
  553. && data->current.audio()->isVideoMessage();
  554. const auto windowResolver = [] {
  555. const auto window = Core::App().activeWindow();
  556. return window ? window->widget()->windowHandle() : nullptr;
  557. };
  558. base::UpdatePowerSaveBlocker(
  559. data->powerSaveBlocker,
  560. block,
  561. base::PowerSaveBlockType::PreventAppSuspension,
  562. [] { return u"Audio playback is active"_q; },
  563. windowResolver);
  564. base::UpdatePowerSaveBlocker(
  565. data->powerSaveBlockerVideo,
  566. blockVideo,
  567. base::PowerSaveBlockType::PreventDisplaySleep,
  568. [] { return u"Video playback is active"_q; },
  569. windowResolver);
  570. }
  571. void Instance::ensureShuffleMove(not_null<Data*> data, int delta) {
  572. const auto raw = data->shuffleData.get();
  573. if (delta < 0) {
  574. if (raw->indexInPlayedIds > 0) {
  575. return;
  576. } else if (raw->nonPlayedIds.size() < 2) {
  577. const auto freeUp = std::max(
  578. int(raw->playedIds.size() / 2),
  579. int(raw->playlist.size()) - kRememberShuffledOrderItems);
  580. const auto till = end(raw->playedIds);
  581. const auto from = end(raw->playedIds) - freeUp;
  582. raw->nonPlayedIds.insert(end(raw->nonPlayedIds), from, till);
  583. raw->playedIds.erase(from, till);
  584. }
  585. if (raw->nonPlayedIds.empty()) {
  586. return;
  587. }
  588. const auto index = base::RandomIndex(raw->nonPlayedIds.size());
  589. raw->playedIds.insert(
  590. begin(raw->playedIds),
  591. raw->nonPlayedIds[index]);
  592. raw->nonPlayedIds.erase(begin(raw->nonPlayedIds) + index);
  593. ++raw->indexInPlayedIds;
  594. if (raw->nonPlayedIds.empty() && raw->playedIds.size() > 1) {
  595. raw->nonPlayedIds.push_back(raw->playedIds.back());
  596. raw->playedIds.pop_back();
  597. }
  598. return;
  599. } else if (raw->indexInPlayedIds + 1 < raw->playedIds.size()) {
  600. return;
  601. } else if (raw->nonPlayedIds.size() < 2) {
  602. const auto freeUp = std::max(
  603. int(raw->playedIds.size() / 2),
  604. int(raw->playlist.size()) - kRememberShuffledOrderItems);
  605. const auto from = begin(raw->playedIds);
  606. const auto till = begin(raw->playedIds) + freeUp;
  607. raw->nonPlayedIds.insert(end(raw->nonPlayedIds), from, till);
  608. raw->playedIds.erase(from, till);
  609. raw->indexInPlayedIds -= freeUp;
  610. }
  611. }
  612. MsgId Instance::computeCurrentUniversalId(not_null<const Data*> data) const {
  613. const auto raw = data->shuffleData.get();
  614. if (!raw) {
  615. return MsgId(0);
  616. }
  617. const auto current = data->current.contextId();
  618. const auto item = raw->history->owner().message(current);
  619. return !item
  620. ? MsgId(0)
  621. : (item->history() == raw->history)
  622. ? item->id
  623. : (item->history() == raw->migrated)
  624. ? (item->id - ServerMaxMsgId)
  625. : MsgId(0);
  626. }
  627. bool Instance::previousAvailable(AudioMsgId::Type type) const {
  628. const auto data = getData(type);
  629. Assert(data != nullptr);
  630. if (!data->playlistIndex || !data->playlistSlice) {
  631. return false;
  632. } else if (repeat(data) == RepeatMode::All) {
  633. return true;
  634. } else if (order(data) == OrderMode::Shuffle) {
  635. const auto raw = data->shuffleData.get();
  636. return raw && (raw->indexInPlayedIds > 0);
  637. }
  638. return (order(data) == OrderMode::Reverse)
  639. ? (*data->playlistIndex + 1 < data->playlistSlice->size())
  640. : (*data->playlistIndex > 0);
  641. }
  642. bool Instance::nextAvailable(AudioMsgId::Type type) const {
  643. const auto data = getData(type);
  644. Assert(data != nullptr);
  645. if (!data->playlistIndex || !data->playlistSlice) {
  646. return false;
  647. } else if (repeat(data) == RepeatMode::All) {
  648. return true;
  649. } else if (order(data) == OrderMode::Shuffle) {
  650. const auto raw = data->shuffleData.get();
  651. const auto universal = computeCurrentUniversalId(data);
  652. return raw
  653. && ((raw->indexInPlayedIds + 1 < raw->playedIds.size())
  654. || (raw->nonPlayedIds.size() > 1)
  655. || (!raw->nonPlayedIds.empty()
  656. && raw->nonPlayedIds.front() != universal));
  657. }
  658. return (order(data) == OrderMode::Reverse)
  659. ? (*data->playlistIndex > 0)
  660. : (*data->playlistIndex + 1 < data->playlistSlice->size());
  661. }
  662. rpl::producer<> Media::Player::Instance::playlistChanges(
  663. AudioMsgId::Type type) const {
  664. const auto data = getData(type);
  665. Assert(data != nullptr);
  666. return rpl::merge(
  667. data->playlistChanges.events(),
  668. orderChanges(data) | rpl::to_empty,
  669. repeatChanges(data) | rpl::to_empty);
  670. }
  671. rpl::producer<> Media::Player::Instance::stops(AudioMsgId::Type type) const {
  672. return _playerStopped.events(
  673. ) | rpl::filter([=](auto t) {
  674. return t == type;
  675. }) | rpl::to_empty;
  676. }
  677. rpl::producer<> Media::Player::Instance::startsPlay(
  678. AudioMsgId::Type type) const {
  679. return _playerStartedPlay.events(
  680. ) | rpl::filter([=](auto t) {
  681. return t == type;
  682. }) | rpl::to_empty;
  683. }
  684. auto Media::Player::Instance::seekingChanges(AudioMsgId::Type type) const
  685. -> rpl::producer<Media::Player::Instance::Seeking> {
  686. return _seekingChanges.events(
  687. ) | rpl::filter([=](SeekingChanges data) {
  688. return data.type == type;
  689. }) | rpl::map([](SeekingChanges data) {
  690. return data.seeking;
  691. });
  692. }
  693. not_null<Instance*> instance() {
  694. Expects(SingleInstance != nullptr);
  695. return SingleInstance;
  696. }
  697. void Instance::play(AudioMsgId::Type type) {
  698. if (const auto data = getData(type)) {
  699. if (!data->streamed || IsStopped(getState(type).state)) {
  700. play(data->current);
  701. } else {
  702. if (data->streamed->instance.active()) {
  703. data->streamed->instance.resume();
  704. }
  705. emitUpdate(type);
  706. }
  707. data->resumeOnCallEnd = false;
  708. }
  709. }
  710. void Instance::play(const AudioMsgId &audioId) {
  711. const auto document = audioId.audio();
  712. if (!document) {
  713. return;
  714. }
  715. if (document->isAudioFile()
  716. || document->isVoiceMessage()
  717. || document->isVideoMessage()) {
  718. auto shared = document->owner().streaming().sharedDocument(
  719. document,
  720. audioId.contextId());
  721. if (!shared) {
  722. return;
  723. }
  724. playStreamed(audioId, std::move(shared));
  725. }
  726. if (document->isVoiceMessage() || document->isVideoMessage()) {
  727. document->owner().markMediaRead(document);
  728. }
  729. _playerStartedPlay.fire_copy({audioId.type()});
  730. }
  731. void Instance::playPause(const AudioMsgId &audioId) {
  732. const auto now = current(audioId.type());
  733. if (now.audio() == audioId.audio()
  734. && now.contextId() == audioId.contextId()) {
  735. playPause(audioId.type());
  736. } else {
  737. play(audioId);
  738. }
  739. }
  740. void Instance::playStreamed(
  741. const AudioMsgId &audioId,
  742. std::shared_ptr<Streaming::Document> shared) {
  743. Expects(audioId.audio() != nullptr);
  744. const auto data = getData(audioId.type());
  745. Assert(data != nullptr);
  746. clearStreamed(data, data->current.audio() != audioId.audio());
  747. data->streamed = std::make_unique<Streamed>(
  748. audioId,
  749. std::move(shared));
  750. data->streamed->instance.lockPlayer();
  751. data->streamed->instance.player().updates(
  752. ) | rpl::start_with_next_error([=](Streaming::Update &&update) {
  753. handleStreamingUpdate(data, std::move(update));
  754. }, [=](Streaming::Error &&error) {
  755. handleStreamingError(data, std::move(error));
  756. }, data->streamed->lifetime);
  757. data->streamed->instance.play(streamingOptions(audioId));
  758. emitUpdate(audioId.type());
  759. }
  760. Streaming::PlaybackOptions Instance::streamingOptions(
  761. const AudioMsgId &audioId,
  762. crl::time position) {
  763. const auto document = audioId.audio();
  764. auto result = Streaming::PlaybackOptions();
  765. result.mode = (document && document->isVideoMessage())
  766. ? Streaming::Mode::Both
  767. : Streaming::Mode::Audio;
  768. result.speed = audioId.changeablePlaybackSpeed()
  769. ? Core::App().settings().voicePlaybackSpeed()
  770. : 1.;
  771. result.audioId = audioId;
  772. if (position >= 0) {
  773. result.position = position;
  774. } else if (document) {
  775. auto &local = document->session().local();
  776. result.position = local.mediaLastPlaybackPosition(document->id);
  777. local.setMediaLastPlaybackPosition(document->id, 0);
  778. } else {
  779. result.position = 0;
  780. }
  781. return result;
  782. }
  783. void Instance::pause(AudioMsgId::Type type) {
  784. if (const auto data = getData(type)) {
  785. if (data->streamed) {
  786. if (data->streamed->instance.active()) {
  787. data->streamed->instance.pause();
  788. }
  789. emitUpdate(type);
  790. }
  791. }
  792. }
  793. void Instance::stop(AudioMsgId::Type type, bool asFinished) {
  794. if (const auto data = getData(type)) {
  795. if (data->streamed) {
  796. clearStreamed(data);
  797. }
  798. data->resumeOnCallEnd = false;
  799. _playerStopped.fire_copy({type});
  800. }
  801. if (asFinished) {
  802. _tracksFinished.fire_copy(type);
  803. }
  804. }
  805. void Instance::stopAndClear(not_null<Data*> data) {
  806. stop(data->type);
  807. *data = Data(data->type, data->overview);
  808. _tracksFinished.fire_copy(data->type);
  809. }
  810. void Instance::validateShuffleData(not_null<Data*> data) {
  811. if (!data->history) {
  812. data->shuffleData = nullptr;
  813. return;
  814. } else if (!data->shuffleData) {
  815. setupShuffleData(data);
  816. }
  817. const auto raw = data->shuffleData.get();
  818. const auto key = playlistKey(data);
  819. const auto scheduled = key
  820. && (key->topicRootId == SparseIdsMergedSlice::kScheduledTopicId);
  821. if (raw->history != data->history
  822. || raw->topicRootId != data->topicRootId
  823. || raw->migrated != data->migrated
  824. || raw->scheduled != scheduled) {
  825. raw->history = data->history;
  826. raw->migrated = data->migrated;
  827. raw->scheduled = scheduled;
  828. raw->nextSliceLifetime.destroy();
  829. raw->allLoaded = false;
  830. raw->playlist.clear();
  831. raw->nonPlayedIds.clear();
  832. raw->playedIds.clear();
  833. raw->indexInPlayedIds = 0;
  834. } else if (raw->nextSliceLifetime) {
  835. return;
  836. } else if (raw->allLoaded) {
  837. const auto universal = computeCurrentUniversalId(data);
  838. if (!universal
  839. || (raw->indexInPlayedIds < raw->playedIds.size()
  840. ? (raw->playedIds[raw->indexInPlayedIds] == universal)
  841. : ranges::contains(raw->nonPlayedIds, universal))) {
  842. return;
  843. }
  844. // We started playing some track not from the tracks that are left.
  845. // Start the whole playlist thing once again.
  846. raw->playedIds.clear();
  847. raw->indexInPlayedIds = 0;
  848. if (ranges::contains(raw->playlist, universal)) {
  849. raw->nonPlayedIds = raw->playlist;
  850. } else {
  851. raw->allLoaded = false;
  852. raw->playlist.clear();
  853. raw->nonPlayedIds.clear();
  854. }
  855. }
  856. if (raw->scheduled) {
  857. const auto count = data->playlistSlice
  858. ? int(data->playlistSlice->size())
  859. : 0;
  860. if (raw->playlist.empty() && count > 0) {
  861. raw->playlist.reserve(count);
  862. for (auto i = 0; i != count; ++i) {
  863. raw->playlist.push_back((*data->playlistSlice)[i].msg);
  864. }
  865. raw->nonPlayedIds = raw->playlist;
  866. raw->allLoaded = true;
  867. data->playlistChanges.fire({});
  868. }
  869. return;
  870. }
  871. const auto last = raw->playlist.empty()
  872. ? MsgId(ServerMaxMsgId - 1)
  873. : raw->playlist.back();
  874. SharedMediaMergedViewer(
  875. &raw->history->session(),
  876. SharedMediaMergedKey(
  877. SliceKey(
  878. raw->history->peer->id,
  879. raw->topicRootId,
  880. raw->migrated ? raw->migrated->peer->id : 0,
  881. last),
  882. data->overview),
  883. kIdsLimit,
  884. kIdsLimit
  885. ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
  886. raw->nextSliceLifetime.destroy();
  887. const auto size = update.size();
  888. const auto peer = raw->history->peer->id;
  889. raw->playlist.reserve(raw->playlist.size() + size);
  890. raw->nonPlayedIds.reserve(raw->nonPlayedIds.size() + size);
  891. for (auto i = size; i != 0;) {
  892. const auto fullId = update[--i];
  893. const auto universal = (fullId.peer == peer)
  894. ? fullId.msg
  895. : (fullId.msg - ServerMaxMsgId);
  896. if (raw->playlist.empty() || raw->playlist.back() > universal) {
  897. raw->playlist.push_back(universal);
  898. raw->nonPlayedIds.push_back(universal);
  899. }
  900. }
  901. if (update.skippedBefore() == 0
  902. || raw->playlist.size() >= kShufflePlaylistLimit) {
  903. raw->allLoaded = true;
  904. }
  905. data->playlistChanges.fire({});
  906. }, raw->nextSliceLifetime);
  907. }
  908. void Instance::setupShuffleData(not_null<Data*> data) {
  909. data->shuffleData = std::make_unique<ShuffleData>();
  910. const auto raw = data->shuffleData.get();
  911. data->history->session().changes().messageUpdates(
  912. ::Data::MessageUpdate::Flag::Destroyed
  913. ) | rpl::map([=](const ::Data::MessageUpdate &update) {
  914. const auto item = update.item;
  915. const auto history = item->history().get();
  916. return (history == raw->history)
  917. ? item->id
  918. : (history == raw->migrated)
  919. ? (item->id - ServerMaxMsgId)
  920. : MsgId(0);
  921. }) | rpl::filter(
  922. rpl::mappers::_1 != MsgId(0)
  923. ) | rpl::start_with_next([=](MsgId id) {
  924. const auto i = ranges::find(raw->playlist, id);
  925. if (i != end(raw->playlist)) {
  926. raw->playlist.erase(i);
  927. }
  928. const auto j = ranges::find(raw->nonPlayedIds, id);
  929. if (j != end(raw->nonPlayedIds)) {
  930. raw->nonPlayedIds.erase(j);
  931. }
  932. const auto k = ranges::find(raw->playedIds, id);
  933. if (k != end(raw->playedIds)) {
  934. const auto index = (k - begin(raw->playedIds));
  935. raw->playedIds.erase(k);
  936. if (raw->indexInPlayedIds > index) {
  937. --raw->indexInPlayedIds;
  938. }
  939. }
  940. }, data->shuffleData->lifetime);
  941. }
  942. void Instance::playPause(AudioMsgId::Type type) {
  943. if (const auto data = getData(type)) {
  944. if (!data->streamed) {
  945. play(data->current);
  946. } else {
  947. auto &streamed = data->streamed->instance;
  948. if (!streamed.active()) {
  949. streamed.play(streamingOptions(data->streamed->id));
  950. } else if (streamed.paused()) {
  951. streamed.resume();
  952. } else {
  953. streamed.pause();
  954. }
  955. emitUpdate(type);
  956. }
  957. data->resumeOnCallEnd = false;
  958. }
  959. }
  960. void Instance::pauseOnCall(AudioMsgId::Type type) {
  961. const auto state = getState(type);
  962. if (!state.id
  963. || IsStopped(state.state)
  964. || IsPaused(state.state)
  965. || state.state == State::Pausing) {
  966. return;
  967. }
  968. pause(type);
  969. if (const auto data = getData(type)) {
  970. data->resumeOnCallEnd = true;
  971. }
  972. }
  973. void Instance::resumeOnCall(AudioMsgId::Type type) {
  974. if (const auto data = getData(type)) {
  975. if (data->resumeOnCallEnd) {
  976. data->resumeOnCallEnd = false;
  977. play(type);
  978. }
  979. }
  980. }
  981. bool Instance::next(AudioMsgId::Type type) {
  982. if (const auto data = getData(type)) {
  983. return moveInPlaylist(data, 1, false);
  984. }
  985. return false;
  986. }
  987. bool Instance::previous(AudioMsgId::Type type) {
  988. if (const auto data = getData(type)) {
  989. return moveInPlaylist(data, -1, false);
  990. }
  991. return false;
  992. }
  993. void Instance::playPauseCancelClicked(AudioMsgId::Type type) {
  994. if (isSeeking(type)) {
  995. return;
  996. }
  997. const auto data = getData(type);
  998. if (!data) {
  999. return;
  1000. }
  1001. const auto state = getState(type);
  1002. const auto showPause = ShowPauseIcon(state.state);
  1003. const auto audio = state.id.audio();
  1004. if (audio && audio->loading() && !data->streamed) {
  1005. audio->cancel();
  1006. } else if (showPause) {
  1007. pause(type);
  1008. } else {
  1009. play(type);
  1010. }
  1011. }
  1012. void Instance::startSeeking(AudioMsgId::Type type) {
  1013. if (auto data = getData(type)) {
  1014. data->seeking = data->current;
  1015. }
  1016. pause(type);
  1017. emitUpdate(type);
  1018. _seekingChanges.fire({ .seeking = Seeking::Start, .type = type });
  1019. }
  1020. void Instance::finishSeeking(AudioMsgId::Type type, float64 progress) {
  1021. if (const auto data = getData(type)) {
  1022. if (const auto streamed = data->streamed.get()) {
  1023. const auto &info = streamed->instance.info();
  1024. const auto duration = info.audio.state.duration;
  1025. if (duration != kTimeUnknown) {
  1026. const auto position = crl::time(base::SafeRound(
  1027. std::clamp(progress, 0., 1.) * duration));
  1028. streamed->instance.play(streamingOptions(
  1029. streamed->id,
  1030. position));
  1031. emitUpdate(type);
  1032. }
  1033. }
  1034. }
  1035. cancelSeeking(type);
  1036. _seekingChanges.fire({ .seeking = Seeking::Finish, .type = type });
  1037. }
  1038. void Instance::cancelSeeking(AudioMsgId::Type type) {
  1039. if (const auto data = getData(type)) {
  1040. data->seeking = AudioMsgId();
  1041. }
  1042. emitUpdate(type);
  1043. _seekingChanges.fire({ .seeking = Seeking::Cancel, .type = type });
  1044. }
  1045. void Instance::updateVoicePlaybackSpeed() {
  1046. if (const auto data = getData(getActiveType())) {
  1047. if (!data->current.changeablePlaybackSpeed()) {
  1048. return;
  1049. }
  1050. if (const auto streamed = data->streamed.get()) {
  1051. streamed->instance.setSpeed(
  1052. Core::App().settings().voicePlaybackSpeed());
  1053. }
  1054. }
  1055. }
  1056. void Instance::emitUpdate(AudioMsgId::Type type) {
  1057. emitUpdate(type, [](const AudioMsgId &playing) { return true; });
  1058. }
  1059. RepeatMode Instance::repeat(not_null<const Data*> data) const {
  1060. return (data->type == AudioMsgId::Type::Song)
  1061. ? Core::App().settings().playerRepeatMode()
  1062. : RepeatMode::None;
  1063. }
  1064. rpl::producer<RepeatMode> Instance::repeatChanges(
  1065. not_null<const Data*> data) const {
  1066. return (data->type == AudioMsgId::Type::Song)
  1067. ? Core::App().settings().playerRepeatModeChanges()
  1068. : rpl::never<RepeatMode>();
  1069. }
  1070. OrderMode Instance::order(not_null<const Data*> data) const {
  1071. return (data->type == AudioMsgId::Type::Song)
  1072. ? Core::App().settings().playerOrderMode()
  1073. : OrderMode::Default;
  1074. }
  1075. rpl::producer<OrderMode> Instance::orderChanges(
  1076. not_null<const Data*> data) const {
  1077. return (data->type == AudioMsgId::Type::Song)
  1078. ? Core::App().settings().playerOrderModeChanges()
  1079. : rpl::never<OrderMode>();
  1080. }
  1081. TrackState Instance::getState(AudioMsgId::Type type) const {
  1082. if (const auto data = getData(type)) {
  1083. if (data->streamed) {
  1084. return data->streamed->instance.player().prepareLegacyState();
  1085. }
  1086. }
  1087. return TrackState();
  1088. }
  1089. Streaming::Instance *Instance::roundVideoStreamed(HistoryItem *item) const {
  1090. if (!item) {
  1091. return nullptr;
  1092. } else if (const auto data = getData(AudioMsgId::Type::Voice)) {
  1093. if (const auto streamed = data->streamed.get()) {
  1094. if (streamed->id.contextId() == item->fullId()) {
  1095. const auto player = &streamed->instance.player();
  1096. if (player->ready() && !player->videoSize().isEmpty()) {
  1097. return &streamed->instance;
  1098. }
  1099. }
  1100. }
  1101. }
  1102. return nullptr;
  1103. }
  1104. Streaming::Instance *Instance::roundVideoPreview(
  1105. not_null<DocumentData*> document) const {
  1106. if (const auto data = getData(AudioMsgId::Type::Voice)) {
  1107. if (const auto streamed = data->streamed.get()) {
  1108. if (streamed->id.audio() == document) {
  1109. const auto player = &streamed->instance.player();
  1110. if (player->ready() && !player->videoSize().isEmpty()) {
  1111. return &streamed->instance;
  1112. }
  1113. }
  1114. }
  1115. }
  1116. return nullptr;
  1117. }
  1118. View::PlaybackProgress *Instance::roundVideoPlayback(
  1119. HistoryItem *item) const {
  1120. return roundVideoStreamed(item)
  1121. ? &getData(AudioMsgId::Type::Voice)->streamed->progress
  1122. : nullptr;
  1123. }
  1124. template <typename CheckCallback>
  1125. void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) {
  1126. if (const auto data = getData(type)) {
  1127. const auto state = getState(type);
  1128. if (!state.id || !check(state.id)) {
  1129. return;
  1130. }
  1131. setCurrent(state.id);
  1132. if (const auto streamed = data->streamed.get()) {
  1133. if (!streamed->instance.info().video.size.isEmpty()) {
  1134. streamed->progress.updateState(state);
  1135. }
  1136. }
  1137. updatePowerSaveBlocker(data, state);
  1138. auto finished = false;
  1139. _updatedNotifier.fire_copy({state});
  1140. if (data->isPlaying && state.state == State::StoppedAtEnd) {
  1141. if (repeat(data) == RepeatMode::One) {
  1142. play(data->current);
  1143. } else if (OptionDisableAutoplayNext.value()) {
  1144. finished = true;
  1145. } else if (!moveInPlaylist(data, 1, true)) {
  1146. finished = true;
  1147. }
  1148. }
  1149. data->isPlaying = !IsStopped(state.state);
  1150. if (finished) {
  1151. _tracksFinished.fire_copy(type);
  1152. }
  1153. }
  1154. }
  1155. void Instance::setupShortcuts() {
  1156. Shortcuts::Requests(
  1157. ) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
  1158. using Command = Shortcuts::Command;
  1159. request->check(Command::MediaPlay) && request->handle([=] {
  1160. playPause();
  1161. return true;
  1162. });
  1163. request->check(Command::MediaPause) && request->handle([=] {
  1164. pause();
  1165. return true;
  1166. });
  1167. request->check(Command::MediaPlayPause) && request->handle([=] {
  1168. playPause();
  1169. return true;
  1170. });
  1171. request->check(Command::MediaStop) && request->handle([=] {
  1172. stop();
  1173. return true;
  1174. });
  1175. request->check(Command::MediaPrevious) && request->handle([=] {
  1176. previous();
  1177. return true;
  1178. });
  1179. request->check(Command::MediaNext) && request->handle([=] {
  1180. next();
  1181. return true;
  1182. });
  1183. }, _lifetime);
  1184. }
  1185. void Instance::stopAndClose() {
  1186. _closePlayerRequests.fire({});
  1187. stop(AudioMsgId::Type::Voice);
  1188. stop(AudioMsgId::Type::Song);
  1189. Shortcuts::ToggleMediaShortcuts(false);
  1190. }
  1191. void Instance::handleStreamingUpdate(
  1192. not_null<Data*> data,
  1193. Streaming::Update &&update) {
  1194. using namespace Streaming;
  1195. v::match(update.data, [&](const Information &update) {
  1196. if (!update.video.size.isEmpty()) {
  1197. data->streamed->progress.setValueChangedCallback([=](
  1198. float64,
  1199. float64) {
  1200. requestRoundVideoRepaint();
  1201. });
  1202. _roundPlaying = true;
  1203. Core::App().floatPlayerToggleGifsPaused(true);
  1204. requestRoundVideoResize();
  1205. }
  1206. emitUpdate(data->type);
  1207. }, [&](PreloadedVideo) {
  1208. //emitUpdate(data->type, [](AudioMsgId) { return true; });
  1209. }, [&](UpdateVideo) {
  1210. emitUpdate(data->type);
  1211. }, [&](PreloadedAudio) {
  1212. //emitUpdate(data->type, [](AudioMsgId) { return true; });
  1213. }, [&](UpdateAudio) {
  1214. emitUpdate(data->type);
  1215. }, [](WaitingForData) {
  1216. }, [](SpeedEstimate) {
  1217. }, [](MutedByOther) {
  1218. }, [&](Finished) {
  1219. emitUpdate(data->type);
  1220. if (data->streamed && data->streamed->instance.player().finished()) {
  1221. clearStreamed(data);
  1222. }
  1223. });
  1224. }
  1225. HistoryItem *Instance::roundVideoItem() const {
  1226. const auto data = getData(AudioMsgId::Type::Voice);
  1227. return (data->streamed
  1228. && !data->streamed->instance.info().video.size.isEmpty()
  1229. && data->history)
  1230. ? data->history->owner().message(data->streamed->id.contextId())
  1231. : nullptr;
  1232. }
  1233. void Instance::requestRoundVideoResize() const {
  1234. if (const auto item = roundVideoItem()) {
  1235. item->history()->owner().requestItemResize(item);
  1236. }
  1237. }
  1238. void Instance::requestRoundVideoRepaint() const {
  1239. if (const auto item = roundVideoItem()) {
  1240. item->history()->owner().requestItemRepaint(item);
  1241. }
  1242. }
  1243. void Instance::handleStreamingError(
  1244. not_null<Data*> data,
  1245. Streaming::Error &&error) {
  1246. Expects(data->streamed != nullptr);
  1247. const auto document = data->streamed->id.audio();
  1248. const auto contextId = data->streamed->id.contextId();
  1249. if (error == Streaming::Error::NotStreamable) {
  1250. DocumentSaveClickHandler::SaveAndTrack(
  1251. contextId,
  1252. document);
  1253. } else if (error == Streaming::Error::OpenFailed) {
  1254. DocumentSaveClickHandler::SaveAndTrack(
  1255. contextId,
  1256. document,
  1257. DocumentSaveClickHandler::Mode::ToFile);
  1258. }
  1259. emitUpdate(data->type);
  1260. if (data->streamed && data->streamed->instance.player().failed()) {
  1261. clearStreamed(data);
  1262. }
  1263. }
  1264. } // namespace Player
  1265. } // namespace Media