#include "services/dbus/mpris.hpp" #include #include #include #include "helpers/string.hpp" #include "giomm/dbusconnection.h" #include "giomm/dbusproxy.h" std::shared_ptr MprisController::getInstance() { static std::shared_ptr instance = std::shared_ptr(new MprisController()); return instance; } MprisController::MprisController() { Gio::DBus::Connection::get( Gio::DBus::BusType::SESSION, sigc::mem_fun(*this, &MprisController::on_bus_connected)); } sigc::signal &MprisController::signal_mpris_updated() { return mprisUpdatedSignal; } sigc::signal &MprisController::signal_playback_status_changed() { return playbackStatusChangedSignal; } sigc::signal &MprisController::signal_playback_position_changed() { return playbackPositionChangedSignal; } void MprisController::on_bus_connected(const Glib::RefPtr &result) { if (!result) { spdlog::error("DBus Connection Error: null async result"); return; } try { m_connection = Gio::DBus::Connection::get_finish(result); std::string player_bus_name = "org.mpris.MediaPlayer2.spotify"; m_proxy = Gio::DBus::Proxy::create_sync( m_connection, player_bus_name, // The Bus Name "/org/mpris/MediaPlayer2", // The Object Path "org.mpris.MediaPlayer2.Player" // The Interface ); if (m_proxy) { spdlog::info("Connected to: {}", player_bus_name); signalNotification(); m_proxy->signal_properties_changed().connect( sigc::mem_fun(*this, &MprisController::on_properties_changed)); } } catch (const Glib::Error &ex) { spdlog::error("DBus Connection Error: {}", ex.what()); } } void MprisController::signalNotification() { if (!m_proxy) { return; } Glib::VariantBase metadata_var; m_proxy->get_cached_property(metadata_var, "Metadata"); if (!metadata_var) { spdlog::info("No metadata available."); return; } if (!metadata_var.is_of_type(Glib::VariantType("a{sv}"))) { spdlog::error("Unexpected metadata type."); return; } using MetadataMap = std::map; MetadataMap metadata_map; Glib::Variant variant_dict = Glib::VariantBase::cast_dynamic>(metadata_var); metadata_map = variant_dict.get(); std::string title, artwork_url; std::vector artist; if (metadata_map.count("xesam:title")) { const auto &title_base = metadata_map["xesam:title"]; if (title_base.is_of_type(Glib::VariantType("s"))) { auto title_var = Glib::VariantBase::cast_dynamic>(title_base); title = title_var.get(); } } if (metadata_map.count("xesam:artist")) { const auto &artist_var = metadata_map["xesam:artist"]; if (artist_var.is_of_type(Glib::VariantType("as"))) { auto artists = Glib::VariantBase::cast_dynamic>>(artist_var).get(); if (!artists.empty()) { for (const auto &a : artists) { artist.push_back(a); } } } else if (artist_var.is_of_type(Glib::VariantType("s"))) { auto artist_str = Glib::VariantBase::cast_dynamic>(artist_var); artist.push_back(artist_str.get()); } } if (metadata_map.count("mpris:artUrl")) { const auto &art_base = metadata_map["mpris:artUrl"]; if (art_base.is_of_type(Glib::VariantType("s"))) { auto art_var = Glib::VariantBase::cast_dynamic>(art_base); artwork_url = art_var.get(); } } int64_t length_us = 0; if (metadata_map.count("mpris:length")) { const auto &length_base = metadata_map["mpris:length"]; if (length_base.is_of_type(Glib::VariantType("x"))) { auto length_var = Glib::VariantBase::cast_dynamic>(length_base); length_us = static_cast(length_var.get()); } else if (length_base.is_of_type(Glib::VariantType("t"))) { auto length_var = Glib::VariantBase::cast_dynamic>(length_base); length_us = static_cast(length_var.get()); } } MprisPlayer2Message mpris; mpris.title = StringHelper::trimToSize(title, 30); mpris.artist = artist; mpris.artwork_url = artwork_url; mpris.play_pause = [this]() { this->toggle_play(); }; mpris.next = [this]() { this->next_song(); }; mpris.previous = [this]() { this->previous_song(); }; mpris.length_ms = length_us; // Convert microseconds to milliseconds mprisUpdatedSignal.emit(mpris); } void MprisController::on_properties_changed(const Gio::DBus::Proxy::MapChangedProperties &changed_properties, const std::vector &) { if (changed_properties.find("Metadata") != changed_properties.end()) { signalNotification(); } if (changed_properties.find("PlaybackStatus") != changed_properties.end()) { auto status_var = changed_properties.at("PlaybackStatus"); if (status_var.is_of_type(Glib::VariantType("s"))) { auto status = Glib::VariantBase::cast_dynamic>(status_var).get(); spdlog::info("Playback Status changed to: {}", status.raw()); auto parsedStatusIt = playbackStatusMap.find(static_cast(status)); if (parsedStatusIt != playbackStatusMap.end()) { currentPlaybackStatus = parsedStatusIt->second; playbackStatusChangedSignal.emit(currentPlaybackStatus); } else { spdlog::error("Unknown playback status: {}", status.raw()); } } } if (changed_properties.find("Position") != changed_properties.end()) { auto position_var = changed_properties.at("Position"); if (position_var.is_of_type(Glib::VariantType("x"))) { auto position = Glib::VariantBase::cast_dynamic>(position_var).get(); spdlog::info("Position changed to: {}", position); playbackPositionChangedSignal.emit(static_cast(position)); } } } void MprisController::previous_song() { if (m_proxy) { m_proxy->call("Previous"); } } void MprisController::toggle_play() { if (m_proxy) { m_proxy->call("PlayPause"); } } void MprisController::next_song() { if (m_proxy) { m_proxy->call("Next"); } } void MprisController::emit_seeked(int64_t position_us) { if (!m_proxy) { return; } try { Glib::VariantContainerBase params = Glib::VariantContainerBase::create_tuple( Glib::Variant::create(position_us)); m_proxy->call("Seek", params); } catch (const Glib::Error &ex) { spdlog::error("Error seeking: {}", ex.what()); } }