#include #include #include #include #include "services/notificationController.hpp" #include "giomm/dbusconnection.h" #include "giomm/dbusproxy.h" class MprisController { public: MprisController() { // 1. Connect to the Session Bus Gio::DBus::Connection::get( Gio::DBus::BusType::SESSION, sigc::mem_fun(*this, &MprisController::on_bus_connected) ); } private: Glib::RefPtr m_connection; Glib::RefPtr m_proxy; void on_bus_connected(const Glib::RefPtr& result) { if (!result) { std::cerr << "DBus Connection Error: null async result" << std::endl; return; } try { m_connection = Gio::DBus::Connection::get_finish(result); // 2. Create a Proxy to the media player // NOTE: In a real app, you must find the name dynamically (see Step 2 below). // For now, ensure a player like Spotify or VLC is running. // Try "org.mpris.MediaPlayer2.spotify" or "org.mpris.MediaPlayer2.vlc" 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) { std::cout << "Connected to: " << player_bus_name << std::endl; // Get initial state print_metadata(); // 3. Listen for changes (Song change, Pause/Play) m_proxy->signal_properties_changed().connect( sigc::mem_fun(*this, &MprisController::on_properties_changed) ); } } catch (const Glib::Error& ex) { std::cerr << "DBus Connection Error: " << ex.what() << std::endl; } } void print_metadata() { // Retrieve the cached property "Metadata" Glib::VariantBase metadata_var; m_proxy->get_cached_property(metadata_var, "Metadata"); if (!metadata_var) { std::cout << "No metadata available." << std::endl; return; } // 4. Unpack Metadata (Type: a{sv}) // This is a dictionary of string -> variant using MetadataMap = std::map; MetadataMap metadata_map; // Cast the variant to a container and iterate Glib::Variant variant_dict = Glib::VariantBase::cast_dynamic>(metadata_var); metadata_map = variant_dict.get(); std::string title, artist, artwork_url; if (metadata_map.count("xesam:title")) { auto title_var = Glib::VariantBase::cast_dynamic>(metadata_map["xesam:title"]); title = title_var.get(); } if (metadata_map.count("xesam:artist")) { 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()) { artist = artists[0]; // Take the first artist } } } if (metadata_map.count("mpris:artUrl")) { auto art_var = Glib::VariantBase::cast_dynamic>(metadata_map["mpris:artUrl"]); artwork_url = art_var.get(); } auto notifactionController = NotificationController::getInstance(); notifactionController->showSpotifyNotification(title, artist, artwork_url); } // Called when the song changes void on_properties_changed(const Gio::DBus::Proxy::MapChangedProperties& changed_properties, const std::vector& invalidated_properties) { (void)invalidated_properties; // Only refresh when Metadata is updated if (changed_properties.find("Metadata") != changed_properties.end()) { // You could parse 'changed_properties' directly, but it's easier to just pull the new cache print_metadata(); } } public: // Call this to toggle play/pause void toggle_play() { if(m_proxy) { m_proxy->call("PlayPause"); } } // Call this to skip void next_song() { if(m_proxy) { m_proxy->call("Next"); } } };