diff --git a/src/backend.hpp b/src/backend.hpp index 870772b..4d2eeef 100644 --- a/src/backend.hpp +++ b/src/backend.hpp @@ -14,6 +14,7 @@ struct MediaInfo { int64_t songDuration; int64_t songElapsedTime; std::string playbackSource; + MediaInfo() {} MediaInfo(bool p, std::string title, std::string artist, std::string album, std::string source, std::string thumbnail, int duration, int elapsed) : paused(p), diff --git a/src/backends/linux.cpp b/src/backends/linux.cpp index e54f60d..93d80bb 100644 --- a/src/backends/linux.cpp +++ b/src/backends/linux.cpp @@ -1,12 +1,20 @@ #if !defined(_WIN32) && !defined(__APPLE__) #include +#include +#include #include #include "../backend.hpp" DBusConnection* conn = nullptr; +std::string getExecutablePath() { + char result[PATH_MAX]{}; + ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); + return (count != -1) ? std::string(result, count) : std::string(); +} + std::string getActivePlayer(DBusConnection* conn) { DBusMessage* msg; DBusMessageIter args; @@ -56,10 +64,8 @@ bool isPlayerPaused(DBusConnection* conn, const std::string& player) { msg = dbus_message_new_method_call(player.c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); - if (!msg) { - std::cerr << "Message Null" << std::endl; + if (!msg) return false; - } const char* interface = "org.mpris.MediaPlayer2.Player"; const char* property = "PlaybackStatus"; @@ -87,119 +93,92 @@ bool isPlayerPaused(DBusConnection* conn, const std::string& player) { return isPaused; } -void getNowPlaying(DBusConnection* conn, const std::string& player) { - DBusMessage* msg; - DBusMessageIter args; +void getNowPlaying(DBusConnection* conn, const std::string& player, MediaInfo& mediaInfo) { DBusError err; dbus_error_init(&err); - msg = dbus_message_new_method_call(player.c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", - "Get"); - const char* iface = "org.mpris.MediaPlayer2.Player"; - const char* prop = "Metadata"; - dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID); + auto sendDBusMessage = [&](const char* property) -> DBusMessage* { + DBusMessage* msg = dbus_message_new_method_call(player.c_str(), "/org/mpris/MediaPlayer2", + "org.freedesktop.DBus.Properties", "Get"); + const char* iface = "org.mpris.MediaPlayer2.Player"; + dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); + DBusMessage* reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + dbus_message_unref(msg); + return reply; + }; - DBusMessage* reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); - dbus_message_unref(msg); + auto processMetadata = [&](DBusMessage* reply) { + if (!reply) return; + + DBusMessageIter args; + dbus_message_iter_init(reply, &args); + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) return; - if (!reply) { - std::cerr << "Failed to get metadata: " << (err.message ? err.message : "Unknown error") << std::endl; - dbus_error_free(&err); - return; - } - - dbus_message_iter_init(reply, &args); - if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_VARIANT) { DBusMessageIter variant; dbus_message_iter_recurse(&args, &variant); + if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY) return; - if (dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_ARRAY) { - DBusMessageIter array_iter; - dbus_message_iter_recurse(&variant, &array_iter); + DBusMessageIter array_iter; + dbus_message_iter_recurse(&variant, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter dict_entry; + dbus_message_iter_recurse(&array_iter, &dict_entry); - while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter dict_entry; - dbus_message_iter_recurse(&array_iter, &dict_entry); + const char* key; + dbus_message_iter_get_basic(&dict_entry, &key); + dbus_message_iter_next(&dict_entry); - const char* key; - dbus_message_iter_get_basic(&dict_entry, &key); - dbus_message_iter_next(&dict_entry); + DBusMessageIter value_variant; + dbus_message_iter_recurse(&dict_entry, &value_variant); - if (std::string(key) == "xesam:title") { - DBusMessageIter value_variant; - dbus_message_iter_recurse(&dict_entry, &value_variant); - - if (dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_STRING) { - const char* title; - dbus_message_iter_get_basic(&value_variant, &title); - std::cout << "Title: " << title << std::endl; - } - } else if (std::string(key) == "xesam:artist") { - DBusMessageIter value_variant; - dbus_message_iter_recurse(&dict_entry, &value_variant); - - if (dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_ARRAY) { - DBusMessageIter artist_array; - dbus_message_iter_recurse(&value_variant, &artist_array); - - std::cout << "Artist(s): "; - bool first = true; - while (dbus_message_iter_get_arg_type(&artist_array) == DBUS_TYPE_STRING) { - const char* artist; - dbus_message_iter_get_basic(&artist_array, &artist); - if (!first) - std::cout << ", "; - std::cout << artist; - first = false; - dbus_message_iter_next(&artist_array); - } - std::cout << std::endl; - } - } else if (std::string(key) == "mpris:length") { - DBusMessageIter value_variant; - dbus_message_iter_recurse(&dict_entry, &value_variant); - int arg_type = dbus_message_iter_get_arg_type(&value_variant); - if (arg_type == DBUS_TYPE_INT64 || arg_type == DBUS_TYPE_UINT64) { - int64_t length; - dbus_message_iter_get_basic(&value_variant, &length); - std::cout << "Length (Duration): " << length / 1000000.0 << " seconds" << std::endl; - } + if (std::string(key) == "xesam:title" && dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_STRING) { + const char* title; + dbus_message_iter_get_basic(&value_variant, &title); + mediaInfo.songTitle = title; + } else if (std::string(key) == "xesam:album" && dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_STRING) { + const char* album; + dbus_message_iter_get_basic(&value_variant, &album); + mediaInfo.songAlbum = album; + } else if (std::string(key) == "xesam:artist" && dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_ARRAY) { + DBusMessageIter artist_array; + dbus_message_iter_recurse(&value_variant, &artist_array); + if (dbus_message_iter_get_arg_type(&artist_array) == DBUS_TYPE_STRING) { + const char* artist; + dbus_message_iter_get_basic(&artist_array, &artist); + mediaInfo.songArtist = artist; } + } else if (std::string(key) == "mpris:length" && + (dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_INT64 || dbus_message_iter_get_arg_type(&value_variant) == DBUS_TYPE_UINT64)) { + int64_t length; + dbus_message_iter_get_basic(&value_variant, &length); + mediaInfo.songDuration = length / 1000; + } + dbus_message_iter_next(&array_iter); + } + }; - dbus_message_iter_next(&array_iter); + DBusMessage* metadataReply = sendDBusMessage("Metadata"); + processMetadata(metadataReply); + dbus_message_unref(metadataReply); + + DBusMessage* positionReply = sendDBusMessage("Position"); + if (positionReply) { + DBusMessageIter args; + dbus_message_iter_init(positionReply, &args); + if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_VARIANT) { + DBusMessageIter variant; + dbus_message_iter_recurse(&args, &variant); + if (dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_INT64 || dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_UINT64) { + int64_t position; + dbus_message_iter_get_basic(&variant, &position); + mediaInfo.songElapsedTime = position / 1000; } } - } else { - std::cerr << "Unexpected reply type for Metadata" << std::endl; + dbus_message_unref(positionReply); } - dbus_message_unref(reply); - msg = dbus_message_new_method_call(player.c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", - "Get"); - prop = "Position"; - dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID); - - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); - dbus_message_unref(msg); - - if (!reply) { - std::cerr << "Failed to get position: " << (err.message ? err.message : "Unknown error") << std::endl; - dbus_error_free(&err); - return; - } - - dbus_message_iter_init(reply, &args); - if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_VARIANT) { - DBusMessageIter variant; - dbus_message_iter_recurse(&args, &variant); - int arg_type = dbus_message_iter_get_arg_type(&variant); - if (arg_type == DBUS_TYPE_INT64 || arg_type == DBUS_TYPE_UINT64) { - int64_t position; - dbus_message_iter_get_basic(&variant, &position); - std::cout << "Current Position: " << position / 1000000.0 << " seconds" << std::endl; - } - } - dbus_message_unref(reply); + dbus_error_free(&err); } bool backend::init() { @@ -215,12 +194,48 @@ bool backend::init() { } std::shared_ptr backend::getMediaInformation() { + MediaInfo ret; std::string player = getActivePlayer(conn); if (player == "") return nullptr; - getNowPlaying(conn, player); - return nullptr; + getNowPlaying(conn, player, ret); + ret.paused = isPlayerPaused(conn, player); + ret.playbackSource = player; + return std::make_shared(ret); } -bool backend::toggleAutostart(bool enabled) { return false; } +bool backend::toggleAutostart(bool enabled) { + const char* xdgHome = std::getenv("XDG_CONFIG_HOME"); + + const char* home = std::getenv("HOME"); + + if (!xdgHome && !home) + return false; + + std::string realxdgHome = xdgHome ? xdgHome : home; + std::filesystem::path xdgAutostartPath = realxdgHome; + if (!xdgHome) + xdgAutostartPath = xdgAutostartPath / ".config"; + xdgAutostartPath = xdgAutostartPath / "autostart"; + + std::filesystem::create_directories(xdgAutostartPath); + + std::filesystem::path desktopPath = xdgAutostartPath / "PlayerLink.desktop"; + + if (!enabled && std::filesystem::exists(desktopPath)) { + std::filesystem::remove(desktopPath); + return true; + } + + // I would like to use std::format here, but a lot of modern linux distributions like Debian 12 still use clang 14 + // which doesnt support std::format + std::string formattedDesktop = "[Desktop Entry]\nType=Application\nName=PlayerLink\nExec=" + getExecutablePath() + + "\nX-GNOME-Autostart-enabled=true\n"; + std::ofstream o(desktopPath); + o.write(formattedDesktop.c_str(), formattedDesktop.size()); + o.close(); + return true; +} + +#undef XDG_AUTOSTART_TEMPLATE #endif \ No newline at end of file