From ecb4d1d9badf46d0cb94bc900233617816145562 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 19 Nov 2021 17:52:07 +0400 Subject: [PATCH] Support repeat all in the audio player. --- .../media/player/media_player_instance.cpp | 118 +++++++++++++++++- .../media/player/media_player_instance.h | 23 ++-- .../media/player/media_player_widget.cpp | 2 +- 3 files changed, 124 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 7bf1f1c39..f32825002 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -104,6 +104,10 @@ Instance::Streamed::Streamed( Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview) : type(type) , overview(overview) { + if (type == AudioMsgId::Type::Song) { + repeat = Core::App().settings().playerRepeatModeValue(); + order = Core::App().settings().playerOrderModeValue(); + } } Instance::Data::Data(Data &&other) = default; @@ -203,6 +207,7 @@ void Instance::setSession(not_null data, Main::Session *session) { return; } data->playlistLifetime.destroy(); + data->playlistOtherLifetime.destroy(); data->sessionLifetime.destroy(); data->session = session; if (session) { @@ -249,6 +254,8 @@ void Instance::clearStreamed(not_null data, bool savePosition) { void Instance::refreshPlaylist(not_null data) { if (!validPlaylist(data)) { validatePlaylist(data); + } else if (!validOtherPlaylist(data)) { + validateOtherPlaylist(data); } playlistUpdated(data); } @@ -263,7 +270,7 @@ void Instance::playlistUpdated(not_null data) { data->playlistChanges.fire({}); } -bool Instance::validPlaylist(not_null data) { +bool Instance::validPlaylist(not_null data) const { if (const auto key = playlistKey(data)) { if (!data->playlistSlice) { return false; @@ -324,7 +331,7 @@ void Instance::validatePlaylist(not_null data) { } } -auto Instance::playlistKey(not_null data) const +auto Instance::playlistKey(not_null data) const -> std::optional { const auto contextId = data->current.contextId(); const auto history = data->history; @@ -346,6 +353,67 @@ auto Instance::playlistKey(not_null data) const item->isScheduled()); } +bool Instance::validOtherPlaylist(not_null data) const { + if (const auto key = playlistOtherKey(data)) { + return data->playlistOtherSlice + && (key == data->playlistOtherRequestedKey); + } + return !data->playlistOtherSlice; +} + +void Instance::validateOtherPlaylist(not_null data) { + data->playlistOtherLifetime.destroy(); + if (const auto key = playlistOtherKey(data)) { + data->playlistOtherRequestedKey = key; + + SharedMediaMergedViewer( + &data->history->session(), + SharedMediaMergedKey(*key, data->overview), + kIdsLimit, + kIdsLimit + ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) { + data->playlistOtherSlice = std::move(update); + playlistUpdated(data); + }, data->playlistOtherLifetime); + } else { + data->playlistOtherSlice = std::nullopt; + data->playlistOtherRequestedKey = std::nullopt; + playlistUpdated(data); + } +} + +auto Instance::playlistOtherKey(not_null data) const +-> std::optional { + if (data->repeat.current() != RepeatMode::All + || data->order.current() == OrderMode::Shuffle + || !data->playlistSlice + || (data->playlistSlice->skippedBefore() != 0 + && data->playlistSlice->skippedAfter() != 0) + || (data->playlistSlice->skippedBefore() == 0 + && data->playlistSlice->skippedAfter() == 0)) { + return {}; + } + const auto contextId = data->current.contextId(); + const auto history = data->history; + if (!contextId || !history) { + return {}; + } + const auto item = data->history->owner().message(contextId); + if (!item || !item->isRegular()) { + return {}; + } + + return SliceKey( + data->history->peer->id, + data->migrated ? data->migrated->peer->id : 0, + (data->playlistSlice->skippedBefore() == 0 + ? ServerMaxMsgId - 1 + : data->migrated + ? (1 - ServerMaxMsgId) + : 1), + false); +} + HistoryItem *Instance::itemByIndex(not_null data, int index) { if (!data->playlistSlice || index < 0 @@ -364,9 +432,7 @@ bool Instance::moveInPlaylist( if (!data->playlistIndex) { return false; } - const auto newIndex = *data->playlistIndex - + (data->order.current() == OrderMode::Reverse ? -delta : delta); - if (const auto item = itemByIndex(data, newIndex)) { + const auto jumpByItem = [&](not_null item) { if (const auto media = item->media()) { if (const auto document = media->document()) { if (autonext) { @@ -383,6 +449,24 @@ bool Instance::moveInPlaylist( return true; } } + return false; + }; + const auto jumpById = [&](FullMsgId id) { + return jumpByItem(data->history->owner().message(id)); + }; + const auto newIndex = *data->playlistIndex + + (data->order.current() == OrderMode::Reverse ? -delta : delta); + if (const auto item = itemByIndex(data, newIndex)) { + return jumpByItem(item); + } else if (data->repeat.current() == RepeatMode::All + && data->playlistOtherSlice + && data->playlistOtherSlice->size() > 0) { + const auto &other = *data->playlistOtherSlice; + if (newIndex < 0 && other.skippedAfter() == 0) { + return jumpById(other[other.size() - 1]); + } else if (newIndex > 0 && other.skippedBefore() == 0) { + return jumpById(other[0]); + } } return false; } @@ -393,6 +477,8 @@ bool Instance::previousAvailable(AudioMsgId::Type type) const { if (!data->playlistIndex || !data->playlistSlice) { return false; + } else if (data->repeat.current() == RepeatMode::All) { + return true; } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex + 1 < data->playlistSlice->size()) @@ -405,6 +491,8 @@ bool Instance::nextAvailable(AudioMsgId::Type type) const { if (!data->playlistIndex || !data->playlistSlice) { return false; + } else if (data->repeat.current() == RepeatMode::All) { + return true; } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex > 0) @@ -418,7 +506,8 @@ rpl::producer<> Media::Player::Instance::playlistChanges( return rpl::merge( data->playlistChanges.events(), - data->order.changes() | rpl::to_empty); + data->order.changes() | rpl::to_empty, + data->repeat.changes() | rpl::to_empty); } rpl::producer<> Media::Player::Instance::stops(AudioMsgId::Type type) const { @@ -651,6 +740,23 @@ void Instance::playPauseCancelClicked(AudioMsgId::Type type) { } } +void Instance::setRepeatMode(AudioMsgId::Type type, RepeatMode mode) { + if (const auto data = getData(type)) { + const auto otherNeeded = (mode == RepeatMode::All) + && (data->repeat.current() != RepeatMode::All); + data->repeat = mode; + if (otherNeeded) { + refreshPlaylist(data); + } + } +} + +void Instance::setOrderMode(AudioMsgId::Type type, OrderMode mode) { + if (const auto data = getData(type)) { + data->order = mode; + } +} + void Instance::startSeeking(AudioMsgId::Type type) { if (auto data = getData(type)) { data->seeking = data->current; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 185d80765..d5f129bbd 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -136,11 +136,7 @@ public: } return rpl::never(); } - void setRepeatMode(AudioMsgId::Type type, RepeatMode mode) { - if (const auto data = getData(type)) { - data->repeat = mode; - } - } + void setRepeatMode(AudioMsgId::Type type, RepeatMode mode); [[nodiscard]] OrderMode orderMode(AudioMsgId::Type type) const { if (const auto data = getData(type)) { @@ -162,11 +158,7 @@ public: } return rpl::never(); } - void setOrderMode(AudioMsgId::Type type, OrderMode mode) { - if (const auto data = getData(type)) { - data->order = mode; - } - } + void setOrderMode(AudioMsgId::Type type, OrderMode mode); [[nodiscard]] bool isSeeking(AudioMsgId::Type type) const { if (const auto data = getData(type)) { @@ -232,8 +224,11 @@ private: std::optional playlistSlice; std::optional playlistSliceKey; std::optional playlistRequestedKey; + std::optional playlistOtherSlice; + std::optional playlistOtherRequestedKey; std::optional playlistIndex; rpl::lifetime playlistLifetime; + rpl::lifetime playlistOtherLifetime; rpl::lifetime sessionLifetime; rpl::event_stream<> playlistChanges; History *history = nullptr; @@ -273,9 +268,13 @@ private: void setCurrent(const AudioMsgId &audioId); void refreshPlaylist(not_null data); - std::optional playlistKey(not_null data) const; - bool validPlaylist(not_null data); + std::optional playlistKey(not_null data) const; + bool validPlaylist(not_null data) const; void validatePlaylist(not_null data); + std::optional playlistOtherKey( + not_null data) const; + bool validOtherPlaylist(not_null data) const; + void validateOtherPlaylist(not_null data); void playlistUpdated(not_null data); bool moveInPlaylist(not_null data, int delta, bool autonext); HistoryItem *itemByIndex(not_null data, int index); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 96f513df4..0b149b973 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -593,7 +593,7 @@ void Widget::updateRepeatToggleIcon() { (active ? &st::mediaPlayerReverseIcon : &st::mediaPlayerReverseDisabledIcon), - active ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); + active ? nullptr : &st::mediaPlayerReverseDisabledIconOver); break; case OrderMode::Shuffle: _repeatToggle->setIconOverride(