Added voices to SMTC.

Fixed #28915.
This commit is contained in:
23rd
2025-12-13 05:42:42 +03:00
committed by John Preston
parent e0159c32cc
commit a027284826
7 changed files with 158 additions and 60 deletions
@@ -690,45 +690,9 @@ void Widget::handleSongChange() {
auto textWithEntities = TextWithEntities();
if (document->isVoiceMessage() || document->isVideoMessage()) {
if (const auto item = document->owner().message(current.contextId())) {
const auto name = (!item->out() || item->isPost())
? item->fromOriginal()->name()
: tr::lng_from_you(tr::now);
const auto date = [item] {
const auto parsed = ItemDateTime(item);
const auto date = parsed.date();
const auto time = QLocale().toString(parsed.time(), QLocale::ShortFormat);
const auto today = QDateTime::currentDateTime().date();
if (date == today) {
return tr::lng_player_message_today(
tr::now,
lt_time,
time);
} else if (date.addDays(1) == today) {
return tr::lng_player_message_yesterday(
tr::now,
lt_time,
time);
}
return tr::lng_player_message_date(
tr::now,
lt_date,
langDayOfMonthFull(date),
lt_time,
time);
};
textWithEntities.text = name + ' ' + date();
textWithEntities.entities.append(EntityInText(
EntityType::Semibold,
0,
name.size(),
QString()));
} else if (document->isVideoMessage()) {
textWithEntities.text = tr::lng_media_round(tr::now);
} else {
textWithEntities.text = tr::lng_media_audio(tr::now);
}
textWithEntities = Ui::Text::FormatVoiceName(
document,
current.contextId()).textWithEntities(true);
} else {
textWithEntities = Ui::Text::FormatSongNameFor(document)
.textWithEntities(true);
@@ -7,13 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/system_media_controls_manager.h"
#include "media/audio/media_audio.h"
#include "base/platform/base_platform_system_media_controls.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "history/history_item_components.h"
#include "history/history_item_helpers.h"
#include "history/history_item.h"
#include "history/history.h"
#include "lang/lang_keys.h"
#include "main/main_account.h"
#include "main/main_session.h"
#include "media/audio/media_audio.h"
@@ -54,17 +60,18 @@ SystemMediaControlsManager::SystemMediaControlsManager()
LOG(("SystemMediaControlsManager failed to init."));
return;
}
const auto type = AudioMsgId::Type::Song;
using TrackState = Media::Player::TrackState;
const auto mediaPlayer = Media::Player::instance();
auto trackFilter = rpl::filter([=](const TrackState &state) {
return (state.id.type() == type);
const auto type = state.id.type();
return (type == AudioMsgId::Type::Song)
|| (type == AudioMsgId::Type::Voice);
});
mediaPlayer->updatedNotifier(
) | trackFilter | rpl::map([=](const TrackState &state) {
const auto type = state.id.type();
using namespace Media::Player;
if (_streamed) {
const auto &player = _streamed->player();
@@ -84,9 +91,15 @@ SystemMediaControlsManager::SystemMediaControlsManager()
}, _lifetime);
rpl::merge(
mediaPlayer->stops(type) | rpl::map_to(false),
mediaPlayer->startsPlay(type) | rpl::map_to(true)
mediaPlayer->stops(AudioMsgId::Type::Song) | rpl::map_to(false),
mediaPlayer->startsPlay(AudioMsgId::Type::Song) | rpl::map_to(true),
mediaPlayer->stops(AudioMsgId::Type::Voice) | rpl::map_to(false),
mediaPlayer->startsPlay(AudioMsgId::Type::Voice) | rpl::map_to(true)
) | rpl::distinct_until_changed() | rpl::on_next([=](bool audio) {
const auto current = mediaPlayer->current(AudioMsgId::Type::Song)
? AudioMsgId::Type::Song
: AudioMsgId::Type::Voice;
const auto type = current;
_controls->setEnabled(audio);
if (audio) {
_controls->setIsNextEnabled(mediaPlayer->nextAvailable(type));
@@ -106,14 +119,18 @@ SystemMediaControlsManager::SystemMediaControlsManager()
auto trackChanged = mediaPlayer->trackChanged(
) | rpl::filter([=](AudioMsgId::Type audioType) {
return audioType == type;
return (audioType == AudioMsgId::Type::Song)
|| (audioType == AudioMsgId::Type::Voice);
});
auto unlocked = Core::App().passcodeLockChanges(
) | rpl::filter([=](bool locked) {
return !locked && (mediaPlayer->current(type));
}) | rpl::map([=] {
return type;
return !locked && (mediaPlayer->current(AudioMsgId::Type::Song)
|| mediaPlayer->current(AudioMsgId::Type::Voice));
}) | rpl::map([=]() -> AudioMsgId::Type {
return mediaPlayer->current(AudioMsgId::Type::Song)
? AudioMsgId::Type::Song
: AudioMsgId::Type::Voice;
}) | rpl::before_next([=] {
_controls->setEnabled(true);
_controls->updateDisplay();
@@ -136,9 +153,12 @@ SystemMediaControlsManager::SystemMediaControlsManager()
}
const auto document = current.audio();
const auto &[title, performer] = Ui::Text::FormatSongNameFor(document)
.composedName();
const auto &[title, performer] = (audioType
== AudioMsgId::Type::Voice)
? Ui::Text::FormatVoiceName(
document,
current.contextId()).composedName()
: Ui::Text::FormatSongNameFor(document).composedName();
_controls->setArtist(performer);
_controls->setTitle(title);
@@ -163,7 +183,42 @@ SystemMediaControlsManager::SystemMediaControlsManager()
// so we need to update the display before that.
_controls->updateDisplay();
if (document && document->isSongWithCover()) {
if (audioType == AudioMsgId::Type::Voice) {
if (const auto item = document->owner().message(
current.contextId())) {
constexpr auto kUserpicSize = 50;
const auto forwarded = item->Get<HistoryMessageForwarded>();
const auto peer = (forwarded && forwarded->originalSender)
? forwarded->originalSender
: (!item->out() || item->isPost())
? item->fromOriginal().get()
: item->history()->peer.get();
const auto userpic = PeerData::GenerateUserpicImage(
peer,
_cachedUserpicView,
kUserpicSize,
0);
if (!userpic.isNull()) {
_controls->setThumbnail(userpic);
} else {
peer->session().downloaderTaskFinished(
) | rpl::on_next([=] {
const auto userpic = PeerData::GenerateUserpicImage(
peer,
_cachedUserpicView,
kUserpicSize,
0);
if (!userpic.isNull()) {
_controls->setThumbnail(userpic);
_lifetimeDownload.destroy();
}
}, _lifetimeDownload);
_controls->clearThumbnail();
}
} else {
_controls->clearThumbnail();
}
} else if (document && document->isSongWithCover()) {
const auto view = document->createMediaView();
view->thumbnailWanted(current.contextId());
_cachedMediaView.push_back(view);
@@ -186,9 +241,12 @@ SystemMediaControlsManager::SystemMediaControlsManager()
_lastAudioMsgId = current;
}, _lifetime);
mediaPlayer->playlistChanges(
type
) | rpl::on_next([=] {
rpl::merge(
mediaPlayer->playlistChanges(AudioMsgId::Type::Song)
| rpl::map_to(AudioMsgId::Type::Song),
mediaPlayer->playlistChanges(AudioMsgId::Type::Voice)
| rpl::map_to(AudioMsgId::Type::Voice)
) | rpl::on_next([=](AudioMsgId::Type type) {
_controls->setIsNextEnabled(mediaPlayer->nextAvailable(type));
_controls->setIsPreviousEnabled(mediaPlayer->previousAvailable(type));
}, _lifetime);
@@ -211,6 +269,9 @@ SystemMediaControlsManager::SystemMediaControlsManager()
_controls->commandRequests(
) | rpl::on_next([=](Command command) {
const auto type = mediaPlayer->current(AudioMsgId::Type::Song)
? AudioMsgId::Type::Song
: AudioMsgId::Type::Voice;
switch (command) {
case Command::PlayPause: mediaPlayer->playPause(type); break;
case Command::Play: mediaPlayer->play(type); break;
@@ -250,11 +311,15 @@ SystemMediaControlsManager::SystemMediaControlsManager()
}, _lifetime);
if (_controls->seekingSupported()) {
mediaPlayer->seekingChanges(
type
rpl::merge(
mediaPlayer->seekingChanges(AudioMsgId::Type::Song),
mediaPlayer->seekingChanges(AudioMsgId::Type::Voice)
) | rpl::filter([](Media::Player::Instance::Seeking seeking) {
return (seeking == Media::Player::Instance::Seeking::Finish);
}) | rpl::map([=] {
const auto type = mediaPlayer->current(AudioMsgId::Type::Song)
? AudioMsgId::Type::Song
: AudioMsgId::Type::Voice;
return mediaPlayer->getState(type).position;
}) | rpl::distinct_until_changed(
) | rpl::on_next([=](int position) {
@@ -264,11 +329,17 @@ SystemMediaControlsManager::SystemMediaControlsManager()
_controls->seekRequests(
) | rpl::on_next([=](float64 progress) {
const auto type = mediaPlayer->current(AudioMsgId::Type::Song)
? AudioMsgId::Type::Song
: AudioMsgId::Type::Voice;
mediaPlayer->finishSeeking(type, progress);
}, _lifetime);
_controls->updatePositionRequests(
) | rpl::on_next([=] {
const auto type = mediaPlayer->current(AudioMsgId::Type::Song)
? AudioMsgId::Type::Song
: AudioMsgId::Type::Voice;
_controls->setPosition(mediaPlayer->getState(type).position);
}, _lifetime);
}
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_audio_msg_id.h"
#include "media/player/media_player_instance.h"
#include "media/media_common.h"
#include "ui/userpic_view.h"
namespace base::Platform {
class SystemMediaControls;
@@ -40,6 +41,7 @@ private:
const std::unique_ptr<base::Platform::SystemMediaControls> _controls;
std::vector<std::shared_ptr<Data::DocumentMedia>> _cachedMediaView;
Ui::PeerUserpicView _cachedUserpicView;
std::unique_ptr<Streaming::Instance> _streamed;
AudioMsgId _lastAudioMsgId;
OrderMode _lastOrderMode = OrderMode::Default;
@@ -7,9 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/text/format_song_document_name.h"
#include "ui/text/text_utilities.h"
#include "data/data_document.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "history/history_item_helpers.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "ui/text/text_utilities.h"
#include <QtCore/QLocale>
namespace Ui::Text {
@@ -39,4 +45,46 @@ TextWithEntities FormatDownloadsName(not_null<DocumentData*> document) {
: FormatSongNameFor(document).textWithEntities();
}
FormatSongName FormatVoiceName(
not_null<DocumentData*> document,
FullMsgId contextId) {
if (const auto item = document->owner().message(contextId)) {
const auto name = (!item->out() || item->isPost())
? item->fromOriginal()->name()
: tr::lng_from_you(tr::now);
const auto date = [item] {
const auto parsed = ItemDateTime(item);
const auto date = parsed.date();
const auto time = QLocale().toString(
parsed.time(),
QLocale::ShortFormat);
const auto today = QDateTime::currentDateTime().date();
if (date == today) {
return tr::lng_player_message_today(
tr::now,
lt_time,
time);
} else if (date.addDays(1) == today) {
return tr::lng_player_message_yesterday(
tr::now,
lt_time,
time);
}
return tr::lng_player_message_date(
tr::now,
lt_date,
langDayOfMonthFull(date),
lt_time,
time);
};
auto result = FormatSongName(QString(), date(), name);
result.setNoDash(true);
return result;
} else if (document->isVideoMessage()) {
return FormatSongName(QString(), tr::lng_media_round(tr::now), {});
} else {
return FormatSongName(QString(), tr::lng_media_audio(tr::now), {});
}
}
} // namespace Ui::Text
@@ -19,4 +19,8 @@ namespace Ui::Text {
[[nodiscard]] TextWithEntities FormatDownloadsName(
not_null<DocumentData*> document);
[[nodiscard]] FormatSongName FormatVoiceName(
not_null<DocumentData*> document,
FullMsgId contextId);
} // namespace Ui::Text
@@ -53,10 +53,16 @@ QString FormatSongName::string() const {
const auto &[title, performer] = _composedName;
const auto dash = (title.isEmpty() || performer.isEmpty())
? QString()
: _noDash
? QString(' ')
: QString::fromUtf8(" \xe2\x80\x93 ");
return performer + dash + title;
}
void FormatSongName::setNoDash(bool noDash) {
_noDash = noDash;
}
TextWithEntities FormatSongName::textWithEntities(
bool boldOnlyPerformer) const {
TextWithEntities result;
@@ -26,8 +26,11 @@ public:
[[nodiscard]] TextWithEntities textWithEntities(
bool boldOnlyPerformer = false) const;
void setNoDash(bool noDash);
private:
const ComposedName _composedName;
bool _noDash = false;
};