mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support repeat all in the audio player.
This commit is contained in:
parent
68378cf8e5
commit
ecb4d1d9ba
3 changed files with 124 additions and 19 deletions
|
@ -104,6 +104,10 @@ Instance::Streamed::Streamed(
|
||||||
Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview)
|
Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview)
|
||||||
: type(type)
|
: type(type)
|
||||||
, overview(overview) {
|
, overview(overview) {
|
||||||
|
if (type == AudioMsgId::Type::Song) {
|
||||||
|
repeat = Core::App().settings().playerRepeatModeValue();
|
||||||
|
order = Core::App().settings().playerOrderModeValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Instance::Data::Data(Data &&other) = default;
|
Instance::Data::Data(Data &&other) = default;
|
||||||
|
@ -203,6 +207,7 @@ void Instance::setSession(not_null<Data*> data, Main::Session *session) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data->playlistLifetime.destroy();
|
data->playlistLifetime.destroy();
|
||||||
|
data->playlistOtherLifetime.destroy();
|
||||||
data->sessionLifetime.destroy();
|
data->sessionLifetime.destroy();
|
||||||
data->session = session;
|
data->session = session;
|
||||||
if (session) {
|
if (session) {
|
||||||
|
@ -249,6 +254,8 @@ void Instance::clearStreamed(not_null<Data*> data, bool savePosition) {
|
||||||
void Instance::refreshPlaylist(not_null<Data*> data) {
|
void Instance::refreshPlaylist(not_null<Data*> data) {
|
||||||
if (!validPlaylist(data)) {
|
if (!validPlaylist(data)) {
|
||||||
validatePlaylist(data);
|
validatePlaylist(data);
|
||||||
|
} else if (!validOtherPlaylist(data)) {
|
||||||
|
validateOtherPlaylist(data);
|
||||||
}
|
}
|
||||||
playlistUpdated(data);
|
playlistUpdated(data);
|
||||||
}
|
}
|
||||||
|
@ -263,7 +270,7 @@ void Instance::playlistUpdated(not_null<Data*> data) {
|
||||||
data->playlistChanges.fire({});
|
data->playlistChanges.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Instance::validPlaylist(not_null<Data*> data) {
|
bool Instance::validPlaylist(not_null<const Data*> data) const {
|
||||||
if (const auto key = playlistKey(data)) {
|
if (const auto key = playlistKey(data)) {
|
||||||
if (!data->playlistSlice) {
|
if (!data->playlistSlice) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -324,7 +331,7 @@ void Instance::validatePlaylist(not_null<Data*> data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Instance::playlistKey(not_null<Data*> data) const
|
auto Instance::playlistKey(not_null<const Data*> data) const
|
||||||
-> std::optional<SliceKey> {
|
-> std::optional<SliceKey> {
|
||||||
const auto contextId = data->current.contextId();
|
const auto contextId = data->current.contextId();
|
||||||
const auto history = data->history;
|
const auto history = data->history;
|
||||||
|
@ -346,6 +353,67 @@ auto Instance::playlistKey(not_null<Data*> data) const
|
||||||
item->isScheduled());
|
item->isScheduled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Instance::validOtherPlaylist(not_null<const Data*> data) const {
|
||||||
|
if (const auto key = playlistOtherKey(data)) {
|
||||||
|
return data->playlistOtherSlice
|
||||||
|
&& (key == data->playlistOtherRequestedKey);
|
||||||
|
}
|
||||||
|
return !data->playlistOtherSlice;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instance::validateOtherPlaylist(not_null<Data*> 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<const Data*> data) const
|
||||||
|
-> std::optional<SliceKey> {
|
||||||
|
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*> data, int index) {
|
HistoryItem *Instance::itemByIndex(not_null<Data*> data, int index) {
|
||||||
if (!data->playlistSlice
|
if (!data->playlistSlice
|
||||||
|| index < 0
|
|| index < 0
|
||||||
|
@ -364,9 +432,7 @@ bool Instance::moveInPlaylist(
|
||||||
if (!data->playlistIndex) {
|
if (!data->playlistIndex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto newIndex = *data->playlistIndex
|
const auto jumpByItem = [&](not_null<HistoryItem*> item) {
|
||||||
+ (data->order.current() == OrderMode::Reverse ? -delta : delta);
|
|
||||||
if (const auto item = itemByIndex(data, newIndex)) {
|
|
||||||
if (const auto media = item->media()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto document = media->document()) {
|
if (const auto document = media->document()) {
|
||||||
if (autonext) {
|
if (autonext) {
|
||||||
|
@ -383,6 +449,24 @@ bool Instance::moveInPlaylist(
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -393,6 +477,8 @@ bool Instance::previousAvailable(AudioMsgId::Type type) const {
|
||||||
|
|
||||||
if (!data->playlistIndex || !data->playlistSlice) {
|
if (!data->playlistIndex || !data->playlistSlice) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if (data->repeat.current() == RepeatMode::All) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return (data->order.current() == OrderMode::Reverse)
|
return (data->order.current() == OrderMode::Reverse)
|
||||||
? (*data->playlistIndex + 1 < data->playlistSlice->size())
|
? (*data->playlistIndex + 1 < data->playlistSlice->size())
|
||||||
|
@ -405,6 +491,8 @@ bool Instance::nextAvailable(AudioMsgId::Type type) const {
|
||||||
|
|
||||||
if (!data->playlistIndex || !data->playlistSlice) {
|
if (!data->playlistIndex || !data->playlistSlice) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if (data->repeat.current() == RepeatMode::All) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return (data->order.current() == OrderMode::Reverse)
|
return (data->order.current() == OrderMode::Reverse)
|
||||||
? (*data->playlistIndex > 0)
|
? (*data->playlistIndex > 0)
|
||||||
|
@ -418,7 +506,8 @@ rpl::producer<> Media::Player::Instance::playlistChanges(
|
||||||
|
|
||||||
return rpl::merge(
|
return rpl::merge(
|
||||||
data->playlistChanges.events(),
|
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 {
|
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) {
|
void Instance::startSeeking(AudioMsgId::Type type) {
|
||||||
if (auto data = getData(type)) {
|
if (auto data = getData(type)) {
|
||||||
data->seeking = data->current;
|
data->seeking = data->current;
|
||||||
|
|
|
@ -136,11 +136,7 @@ public:
|
||||||
}
|
}
|
||||||
return rpl::never<RepeatMode>();
|
return rpl::never<RepeatMode>();
|
||||||
}
|
}
|
||||||
void setRepeatMode(AudioMsgId::Type type, RepeatMode mode) {
|
void setRepeatMode(AudioMsgId::Type type, RepeatMode mode);
|
||||||
if (const auto data = getData(type)) {
|
|
||||||
data->repeat = mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] OrderMode orderMode(AudioMsgId::Type type) const {
|
[[nodiscard]] OrderMode orderMode(AudioMsgId::Type type) const {
|
||||||
if (const auto data = getData(type)) {
|
if (const auto data = getData(type)) {
|
||||||
|
@ -162,11 +158,7 @@ public:
|
||||||
}
|
}
|
||||||
return rpl::never<OrderMode>();
|
return rpl::never<OrderMode>();
|
||||||
}
|
}
|
||||||
void setOrderMode(AudioMsgId::Type type, OrderMode mode) {
|
void setOrderMode(AudioMsgId::Type type, OrderMode mode);
|
||||||
if (const auto data = getData(type)) {
|
|
||||||
data->order = mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool isSeeking(AudioMsgId::Type type) const {
|
[[nodiscard]] bool isSeeking(AudioMsgId::Type type) const {
|
||||||
if (const auto data = getData(type)) {
|
if (const auto data = getData(type)) {
|
||||||
|
@ -232,8 +224,11 @@ private:
|
||||||
std::optional<SparseIdsMergedSlice> playlistSlice;
|
std::optional<SparseIdsMergedSlice> playlistSlice;
|
||||||
std::optional<SliceKey> playlistSliceKey;
|
std::optional<SliceKey> playlistSliceKey;
|
||||||
std::optional<SliceKey> playlistRequestedKey;
|
std::optional<SliceKey> playlistRequestedKey;
|
||||||
|
std::optional<SparseIdsMergedSlice> playlistOtherSlice;
|
||||||
|
std::optional<SliceKey> playlistOtherRequestedKey;
|
||||||
std::optional<int> playlistIndex;
|
std::optional<int> playlistIndex;
|
||||||
rpl::lifetime playlistLifetime;
|
rpl::lifetime playlistLifetime;
|
||||||
|
rpl::lifetime playlistOtherLifetime;
|
||||||
rpl::lifetime sessionLifetime;
|
rpl::lifetime sessionLifetime;
|
||||||
rpl::event_stream<> playlistChanges;
|
rpl::event_stream<> playlistChanges;
|
||||||
History *history = nullptr;
|
History *history = nullptr;
|
||||||
|
@ -273,9 +268,13 @@ private:
|
||||||
|
|
||||||
void setCurrent(const AudioMsgId &audioId);
|
void setCurrent(const AudioMsgId &audioId);
|
||||||
void refreshPlaylist(not_null<Data*> data);
|
void refreshPlaylist(not_null<Data*> data);
|
||||||
std::optional<SliceKey> playlistKey(not_null<Data*> data) const;
|
std::optional<SliceKey> playlistKey(not_null<const Data*> data) const;
|
||||||
bool validPlaylist(not_null<Data*> data);
|
bool validPlaylist(not_null<const Data*> data) const;
|
||||||
void validatePlaylist(not_null<Data*> data);
|
void validatePlaylist(not_null<Data*> data);
|
||||||
|
std::optional<SliceKey> playlistOtherKey(
|
||||||
|
not_null<const Data*> data) const;
|
||||||
|
bool validOtherPlaylist(not_null<const Data*> data) const;
|
||||||
|
void validateOtherPlaylist(not_null<Data*> data);
|
||||||
void playlistUpdated(not_null<Data*> data);
|
void playlistUpdated(not_null<Data*> data);
|
||||||
bool moveInPlaylist(not_null<Data*> data, int delta, bool autonext);
|
bool moveInPlaylist(not_null<Data*> data, int delta, bool autonext);
|
||||||
HistoryItem *itemByIndex(not_null<Data*> data, int index);
|
HistoryItem *itemByIndex(not_null<Data*> data, int index);
|
||||||
|
|
|
@ -593,7 +593,7 @@ void Widget::updateRepeatToggleIcon() {
|
||||||
(active
|
(active
|
||||||
? &st::mediaPlayerReverseIcon
|
? &st::mediaPlayerReverseIcon
|
||||||
: &st::mediaPlayerReverseDisabledIcon),
|
: &st::mediaPlayerReverseDisabledIcon),
|
||||||
active ? nullptr : &st::mediaPlayerRepeatDisabledIconOver);
|
active ? nullptr : &st::mediaPlayerReverseDisabledIconOver);
|
||||||
break;
|
break;
|
||||||
case OrderMode::Shuffle:
|
case OrderMode::Shuffle:
|
||||||
_repeatToggle->setIconOverride(
|
_repeatToggle->setIconOverride(
|
||||||
|
|
Loading…
Add table
Reference in a new issue