Add support for setting cover with MPRIS

This commit is contained in:
Ilya Fedin 2021-03-31 21:55:14 +04:00 committed by John Preston
parent 30c86523ff
commit ae54c8af6a
1 changed files with 92 additions and 34 deletions

View File

@ -11,13 +11,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/linux/base_linux_glibmm_helper.h" #include "base/platform/linux/base_linux_glibmm_helper.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "media/player/media_player_instance.h" #include "media/player/media_player_instance.h"
#include "data/data_file_origin.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h"
#include "core/sandbox.h" #include "core/sandbox.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "main/main_domain.h"
#include "main/main_account.h"
#include "main/main_session.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "app.h" #include "app.h"
#include <QtCore/QBuffer>
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
#include <glibmm.h> #include <glibmm.h>
@ -90,34 +96,57 @@ constexpr auto kPlayerIntrospectionXML = R"INTROSPECTION(<node>
</interface> </interface>
</node>)INTROSPECTION"_cs; </node>)INTROSPECTION"_cs;
auto CreateMetadata(const Media::Player::TrackState &state) { auto CreateMetadata(
const Media::Player::TrackState &state,
Data::DocumentMedia *trackView) {
std::map<Glib::ustring, Glib::VariantBase> result; std::map<Glib::ustring, Glib::VariantBase> result;
if (!Media::Player::IsStoppedOrStopping(state.state)) { if (Media::Player::IsStoppedOrStopping(state.state)) {
result["mpris:trackid"] = Glib::wrap(g_variant_new_object_path( return result;
kFakeTrackPath.utf8().constData())); }
result["mpris:length"] = Glib::Variant<gint64>::create(
state.length * 1000);
const auto audioData = state.id.audio(); result["mpris:trackid"] = Glib::wrap(g_variant_new_object_path(
if (audioData) { kFakeTrackPath.utf8().constData()));
result["xesam:title"] = Glib::Variant< result["mpris:length"] = Glib::Variant<gint64>::create(
Glib::ustring state.length * 1000);
>::create(audioData->filename().toStdString());
if (audioData->isSong()) { const auto audioData = state.id.audio();
const auto songData = audioData->song(); if (audioData) {
if (!songData->performer.isEmpty()) { result["xesam:title"] = Glib::Variant<
result["xesam:artist"] = Glib::Variant< Glib::ustring
std::vector<Glib::ustring> >::create(audioData->filename().toStdString());
>::create({ songData->performer.toStdString() });
} if (audioData->isSong()) {
if (!songData->performer.isEmpty()) { const auto songData = audioData->song();
result["xesam:title"] = Glib::Variant< if (!songData->performer.isEmpty()) {
Glib::ustring result["xesam:artist"] = Glib::Variant<
>::create(songData->title.toStdString()); std::vector<Glib::ustring>
} >::create({ songData->performer.toStdString() });
} }
if (!songData->performer.isEmpty()) {
result["xesam:title"] = Glib::Variant<
Glib::ustring
>::create(songData->title.toStdString());
}
}
}
if (trackView) {
trackView->thumbnailWanted(Data::FileOrigin());
if (trackView->thumbnail()) {
QByteArray thumbnailData;
QBuffer thumbnailBuffer(&thumbnailData);
trackView->thumbnail()->original().save(
&thumbnailBuffer,
"JPG",
87);
result["mpris:artUrl"] = Glib::Variant<
Glib::ustring
>::create("data:image/jpeg;base64,"
+ thumbnailData
.toBase64()
.toStdString());
} }
} }
@ -237,8 +266,16 @@ void HandleGetProperty(
const auto state = Media::Player::instance()->getState( const auto state = Media::Player::instance()->getState(
kSongType); kSongType);
const auto trackView = [&]() -> std::shared_ptr<Data::DocumentMedia> {
const auto audioData = state.id.audio();
if (audioData && audioData->isSongWithCover()) {
return audioData->activeMediaView();
}
return nullptr;
}();
property = base::Platform::MakeGlibVariant( property = base::Platform::MakeGlibVariant(
CreateMetadata(state)); CreateMetadata(state, trackView.get()));
} else if (property_name == "MinimumRate") { } else if (property_name == "MinimumRate") {
property = Glib::Variant<float64>::create(1.0); property = Glib::Variant<float64>::create(1.0);
} else if (property_name == "PlaybackStatus") { } else if (property_name == "PlaybackStatus") {
@ -346,10 +383,13 @@ public:
uint registerId = 0; uint registerId = 0;
uint playerRegisterId = 0; uint playerRegisterId = 0;
std::map<Glib::ustring, Glib::VariantBase> metadata;
Glib::ustring playbackStatus; Glib::ustring playbackStatus;
gint64 position = 0; gint64 position = 0;
DocumentData *audioData = nullptr;
std::shared_ptr<Data::DocumentMedia> trackView;
Image *thumbnail = nullptr;
rpl::lifetime lifetime; rpl::lifetime lifetime;
}; };
@ -359,22 +399,34 @@ void MPRISSupport::Private::updateTrackState(
return; return;
} }
const auto currentMetadata = CreateMetadata(state); const auto currentAudioData = state.id.audio();
const auto currentPosition = state.position * 1000; const auto currentPosition = state.position * 1000;
const auto currentPlaybackStatus = PlaybackStatus(state.state); const auto currentPlaybackStatus = PlaybackStatus(state.state);
if (!ranges::equal(currentMetadata, metadata, [&]( if (currentAudioData != audioData) {
const auto &item1, audioData = currentAudioData;
const auto &item2) { if (audioData && audioData->isSongWithCover()) {
return item1.first == item2.first trackView = audioData->createMediaView();
&& item1.second.equal(item2.second); thumbnail = trackView->thumbnail();
})) { } else {
metadata = currentMetadata; trackView = nullptr;
thumbnail = nullptr;
}
PlayerPropertyChanged( PlayerPropertyChanged(
"Metadata", "Metadata",
Glib::Variant< Glib::Variant<
std::map<Glib::ustring, Glib::VariantBase> std::map<Glib::ustring, Glib::VariantBase>
>::create(metadata)); >::create(CreateMetadata(state, trackView.get())));
}
if (trackView && (trackView->thumbnail() != thumbnail)) {
thumbnail = trackView->thumbnail();
PlayerPropertyChanged(
"Metadata",
Glib::Variant<
std::map<Glib::ustring, Glib::VariantBase>
>::create(CreateMetadata(state, trackView.get())));
} }
if (currentPlaybackStatus != playbackStatus) { if (currentPlaybackStatus != playbackStatus) {
@ -423,6 +475,12 @@ MPRISSupport::MPRISSupport()
_private->updateTrackState( _private->updateTrackState(
Media::Player::instance()->getState(kSongType)); Media::Player::instance()->getState(kSongType));
Core::App().domain().active().session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
_private->updateTrackState(
Media::Player::instance()->getState(kSongType));
}, _private->lifetime);
Media::Player::instance()->updatedNotifier( Media::Player::instance()->updatedNotifier(
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
const Media::Player::TrackState &state) { const Media::Player::TrackState &state) {