Check all active players for "Playing" Status

This commit is contained in:
Alex Oxorn 2025-04-25 15:02:32 -04:00 committed by DarkOK
parent 8baa6d2a20
commit 3a22976fec

View file

@ -1,10 +1,10 @@
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <list>
#include <optional> #include <optional>
#include <string> #include <string>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
#include <ranges>
#include <gio/gio.h> #include <gio/gio.h>
#include <os/media.h> #include <os/media.h>
#include <os/logger.h> #include <os/logger.h>
@ -22,13 +22,10 @@ static const char* DBusPath = "/org/freedesktop/DBus";
static const char* MPRIS2Interface = "org.mpris.MediaPlayer2"; static const char* MPRIS2Interface = "org.mpris.MediaPlayer2";
static const char* MPRIS2PlayerInterface = "org.mpris.MediaPlayer2.Player"; static const char* MPRIS2PlayerInterface = "org.mpris.MediaPlayer2.Player";
static const char* MPRIS2Path = "/org/mpris/MediaPlayer2"; static const char* MPRIS2Path = "/org/mpris/MediaPlayer2";
static const char* MPRIS2PlayerctldBus = "org.mpris.MediaPlayer2.playerctld";
static std::optional<std::thread> g_dbusThread; static std::optional<std::thread> g_dbusThread;
static std::string g_playerctldBus;
static std::unordered_map<std::string, PlaybackStatus> g_playerStatus; static std::unordered_map<std::string, PlaybackStatus> g_playerStatus;
static std::list<std::string> g_activePlayers; static std::atomic<bool> g_isPlaying = false;
static std::atomic<PlaybackStatus> g_activeStatus;
static PlaybackStatus PlaybackStatusFromString(const char* str) static PlaybackStatus PlaybackStatusFromString(const char* str)
{ {
@ -40,42 +37,16 @@ static PlaybackStatus PlaybackStatusFromString(const char* str)
return PlaybackStatus::Stopped; return PlaybackStatus::Stopped;
} }
static bool ContainerHas(const auto& container, const auto& value)
{
return std::find(container.begin(), container.end(), value) != container.end();
}
static void UpdateActiveStatus() static void UpdateActiveStatus()
{ {
if (!g_activePlayers.empty()) g_isPlaying = std::ranges::any_of(
g_activeStatus = g_playerStatus[g_activePlayers.front()]; g_playerStatus | std::views::values,
else [](PlaybackStatus status) { return status == PlaybackStatus::Playing; }
g_activeStatus = PlaybackStatus::Stopped; );
} }
static void UpdateActivePlayers(const char* name, PlaybackStatus status) static void UpdateActivePlayers(const char* name, PlaybackStatus status)
{ {
if (status == PlaybackStatus::Stopped)
{
// Don't remove playerctld from the queue, we want to prefer it for as long as it's available
if (!g_str_equal(name, g_playerctldBus.c_str()))
{
g_activePlayers.remove(name);
}
}
else if (!ContainerHas(g_activePlayers, name))
{
// Prioritise playerctld if and when it appears
if (g_str_equal(name, g_playerctldBus.c_str()))
{
g_activePlayers.emplace_front(name);
}
else
{
g_activePlayers.emplace_back(name);
}
}
g_playerStatus.insert_or_assign(name, status); g_playerStatus.insert_or_assign(name, status);
UpdateActiveStatus(); UpdateActiveStatus();
} }
@ -137,7 +108,7 @@ static void DBusConnectionClosed(GDBusConnection* connection,
gpointer userData) gpointer userData)
{ {
LOG_ERROR("D-Bus connection closed"); LOG_ERROR("D-Bus connection closed");
g_activeStatus = PlaybackStatus::Stopped; g_isPlaying = false;
g_main_loop_quit((GMainLoop*)userData); g_main_loop_quit((GMainLoop*)userData);
} }
@ -157,15 +128,9 @@ static void DBusNameOwnerChanged(GDBusConnection* connection,
if (g_str_has_prefix(name, MPRIS2Interface)) if (g_str_has_prefix(name, MPRIS2Interface))
{ {
if (g_str_equal(name, MPRIS2PlayerctldBus))
{
g_playerctldBus = newOwner;
}
if (oldOwner[0]) if (oldOwner[0])
{ {
g_playerStatus.erase(oldOwner); g_playerStatus.erase(oldOwner);
g_activePlayers.remove(oldOwner);
} }
UpdateActiveStatus(); UpdateActiveStatus();
@ -206,11 +171,9 @@ static void MPRISPropertiesChanged(GDBusConnection* connection,
g_variant_unref(changed); g_variant_unref(changed);
} }
/* Called upon connect to discover already active MPRIS2 players by looking for /* Called upon CONNECT to discover already active MPRIS2 players by looking for
well-known bus names that begin with the MPRIS2 path. well-known bus names that begin with the MPRIS2 path.
We determine what their priority would be when pushed to the player queue g_playerStatus stores unique connection names,
based on their current status.
The queue (and accompanying data structures) stores unique connection names,
not their well-known ones, as the PropertiesChanged signal only provides the not their well-known ones, as the PropertiesChanged signal only provides the
former. */ former. */
static void DBusListNamesReceived(GObject* object, GAsyncResult* res, gpointer userData) static void DBusListNamesReceived(GObject* object, GAsyncResult* res, gpointer userData)
@ -221,13 +184,10 @@ static void DBusListNamesReceived(GObject* object, GAsyncResult* res, gpointer u
GVariant* tupleChild; GVariant* tupleChild;
GVariantIter iter; GVariantIter iter;
const gchar* name; const gchar* name;
bool foundPlayerctld;
std::list<std::string> pausedPlayers;
connection = G_DBUS_CONNECTION(object); connection = G_DBUS_CONNECTION(object);
error = NULL; error = NULL;
response = g_dbus_connection_call_finish(connection, res, &error); response = g_dbus_connection_call_finish(connection, res, &error);
foundPlayerctld = false;
if (!response) if (!response)
{ {
@ -274,31 +234,10 @@ static void DBusListNamesReceived(GObject* object, GAsyncResult* res, gpointer u
g_variant_get(ownerResponse, "(&s)", &ownerName); g_variant_get(ownerResponse, "(&s)", &ownerName);
status = MPRISGetPlaybackStatus(connection, ownerName); status = MPRISGetPlaybackStatus(connection, ownerName);
// Prioritise playerctld, even if current playback status is stopped
if (!foundPlayerctld && g_str_equal(name, MPRIS2PlayerctldBus))
{
foundPlayerctld = true;
g_playerctldBus = ownerName;
g_activePlayers.emplace_front(ownerName);
}
else if (status != PlaybackStatus::Stopped)
{
if (status == PlaybackStatus::Playing)
{
g_activePlayers.emplace_back(ownerName);
}
else
{
pausedPlayers.emplace_back(ownerName);
}
}
g_playerStatus.insert_or_assign(ownerName, status); g_playerStatus.insert_or_assign(ownerName, status);
g_variant_unref(ownerResponse); g_variant_unref(ownerResponse);
} }
// Put all the paused players at the end of the queue
g_activePlayers.splice(g_activePlayers.end(), pausedPlayers);
UpdateActiveStatus(); UpdateActiveStatus();
g_variant_unref(tupleChild); g_variant_unref(tupleChild);
@ -385,5 +324,5 @@ bool os::media::IsExternalMediaPlaying()
g_dbusThread->detach(); g_dbusThread->detach();
} }
return g_activeStatus == PlaybackStatus::Playing; return g_isPlaying;
} }