Use playerctld to get player names when available

fixes #192
This commit is contained in:
Tony Crisci
2020-11-07 16:18:06 -05:00
parent a1cfd4a790
commit bfed117652
13 changed files with 245 additions and 91 deletions

View File

@@ -20,11 +20,11 @@ RUN pip3 install -r requirements.txt
ADD . /app ADD . /app
COPY data/test/dbus-system.conf /etc/dbus-1/system.d/test-dbus-system.conf COPY test/data/dbus-system.conf /etc/dbus-1/system.d/test-dbus-system.conf
RUN meson --prefix=/usr build && \ RUN meson --prefix=/usr build && \
ninja -C build && ninja -C build install ninja -C build && ninja -C build install
RUN mkdir -p /run/dbus RUN mkdir -p /run/dbus
ENV PYTHONASYNCIODEBUG=1 ENV PYTHONASYNCIODEBUG=1
ENV DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket ENV DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket
CMD ["bash", "-c", "dbus-daemon --nopidfile --system && dbus-run-session python3 -m pytest -vv"] CMD ["bash", "-c", "dbus-daemon --nopidfile --system && dbus-run-session python3 -m pytest -vvs"]

View File

@@ -38,6 +38,8 @@ Here is a list of available commands:
Without specifying any players to control, Playerctl will act on the first player it can find. Without specifying any players to control, Playerctl will act on the first player it can find.
Playerctl comes with a service called `playerctld` that monitors the activity of media players in the background. If `playerctld` is running, Playerctl will act on players in order of their last activity.
You can list the names of players that are available to control that are running on the system with `playerctl --list-all`. You can list the names of players that are available to control that are running on the system with `playerctl --list-all`.
If you'd only like to control certain players, you can pass the names of those players separated by commas with the `--player` flag. Playerctl will select the first instance of a player in that list that supports the command. To control all players in the list, you can use the `--all-players` flag. If you'd only like to control certain players, you can pass the names of those players separated by commas with the `--player` flag. Playerctl will select the first instance of a player in that list that supports the command. To control all players in the list, you can use the `--all-players` flag.
@@ -69,15 +71,6 @@ playerctl --player=%any,chromium play
playerctl --player=vlc,%any play playerctl --player=vlc,%any play
``` ```
#### Selecting the Most Recent Player
Playerctl comes with a service called `playerctld` you can use that monitors the activity of media players to select the one with the most recent activity. To use it, simply pass `playerctld` as the selected player to Playerctl and the service should start automatically (if it doesn't, see the troubleshooting section).
```
# Command the most recent player to play
playerctl --player=playerctld play
```
### Printing Properties and Metadata ### Printing Properties and Metadata
You can pass a format string with the `--format` argument to print properties in a specific format. Pass the variable you want to print in the format string between double braces like `{{ VARIABLE }}`. The variables available are either the name of the query command, or anything in the metadata map which can be viewed with `playerctl metadata`. You can use this to integrate playerctl into a statusline generator. You can pass a format string with the `--format` argument to print properties in a specific format. Pass the variable you want to print in the format string between double braces like `{{ VARIABLE }}`. The variables available are either the name of the query command, or anything in the metadata map which can be viewed with `playerctl metadata`. You can use this to integrate playerctl into a statusline generator.

View File

@@ -723,7 +723,7 @@ static gboolean playercmd_metadata(PlayerctlPlayer *player, gchar **argv, gint a
return TRUE; return TRUE;
} }
static void managed_player_properties_callback(PlayerctlPlayer *player, gpointer *data) { static void managed_player_properties_callback(PlayerctlPlayer *player, gpointer data) {
playerctl_player_manager_move_player_to_top(manager, player); playerctl_player_manager_move_player_to_top(manager, player);
GError *error = NULL; GError *error = NULL;
managed_players_execute_command(&error); managed_players_execute_command(&error);
@@ -883,6 +883,28 @@ static GList *parse_player_list(gchar *player_list_arg) {
return players; return players;
} }
static gboolean name_is_selected(const gchar *name) {
if (ignored_player_names != NULL) {
gboolean ignored =
(g_list_find_custom(ignored_player_names, name,
(GCompareFunc)pctl_player_name_string_instance_compare) != NULL);
if (ignored) {
return FALSE;
}
}
if (player_names != NULL) {
gboolean selected =
(g_list_find_custom(player_names, name,
(GCompareFunc)pctl_player_name_string_instance_compare) != NULL);
if (!selected) {
return FALSE;
}
}
return TRUE;
}
static int handle_version_flag() { static int handle_version_flag() {
g_print("v%s\n", PLAYERCTL_VERSION_S); g_print("v%s\n", PLAYERCTL_VERSION_S);
return 0; return 0;
@@ -892,22 +914,26 @@ static int handle_list_all_flag() {
GError *tmp_error = NULL; GError *tmp_error = NULL;
GList *player_names_list = playerctl_list_players(&tmp_error); GList *player_names_list = playerctl_list_players(&tmp_error);
player_names_list =
g_list_sort_with_data(player_names_list, player_name_compare_func, (gpointer)player_names);
if (tmp_error != NULL) { if (tmp_error != NULL) {
g_printerr("%s\n", tmp_error->message); g_printerr("%s\n", tmp_error->message);
return 1; return 1;
} }
if (player_names_list == NULL) { gboolean one_selected = FALSE;
if (!no_status_error_messages) {
g_printerr("No players were found\n");
}
return 0;
}
GList *l = NULL; GList *l = NULL;
for (l = player_names_list; l != NULL; l = l->next) { for (l = player_names_list; l != NULL; l = l->next) {
PlayerctlPlayerName *name = l->data; PlayerctlPlayerName *name = l->data;
printf("%s\n", name->instance); if (name_is_selected(name->instance)) {
one_selected = TRUE;
printf("%s\n", name->instance);
}
}
if (!one_selected && !no_status_error_messages) {
g_printerr("No players were found\n");
} }
pctl_player_name_list_destroy(player_names_list); pctl_player_name_list_destroy(player_names_list);
@@ -958,28 +984,6 @@ static void managed_players_execute_command(GError **error) {
} }
} }
static gboolean name_is_selected(gchar *name) {
if (ignored_player_names != NULL) {
gboolean ignored =
(g_list_find_custom(ignored_player_names, name,
(GCompareFunc)pctl_player_name_string_instance_compare) != NULL);
if (ignored) {
return FALSE;
}
}
if (player_names != NULL) {
gboolean selected =
(g_list_find_custom(player_names, name,
(GCompareFunc)pctl_player_name_string_instance_compare) != NULL);
if (!selected) {
return FALSE;
}
}
return TRUE;
}
static void name_appeared_callback(PlayerctlPlayerManager *manager, PlayerctlPlayerName *name, static void name_appeared_callback(PlayerctlPlayerManager *manager, PlayerctlPlayerName *name,
gpointer *data) { gpointer *data) {
if (!name_is_selected(name->instance)) { if (!name_is_selected(name->instance)) {
@@ -1074,75 +1078,76 @@ static void player_vanished_callback(PlayerctlPlayerManager *manager, PlayerctlP
} }
} }
gint player_name_string_compare_func(gconstpointer a, gconstpointer b) { gint player_name_string_compare_func(gconstpointer a, gconstpointer b, gpointer user_data) {
const gchar *name_a = a; const gchar *name_a = a;
const gchar *name_b = b; const gchar *name_b = b;
GList *names = user_data;
if (g_strcmp0(name_a, name_b) == 0) { if (g_strcmp0(name_a, name_b) == 0) {
return 0; return 0;
} }
int a_index = -1; int a_match_index = -1;
int b_index = -1; int b_match_index = -1;
int any_index = INT_MAX; int any_index = INT_MAX;
int i = 0; int i = 0;
GList *l = NULL; GList *l = NULL;
for (l = player_names; l != NULL; l = l->next) { for (l = names; l != NULL; l = l->next) {
gchar *name = l->data; gchar *name = l->data;
if (g_strcmp0(name, "%any") == 0) { if (g_strcmp0(name, "%any") == 0) {
if (any_index == INT_MAX) { if (any_index == INT_MAX) {
any_index = i; any_index = i;
} }
} else if (g_strcmp0(name_a, name) == 0) { continue;
if (a_index == -1) { }
a_index = i;
if (pctl_player_name_string_instance_compare(name, name_a) == 0) {
if (a_match_index == -1) {
a_match_index = i;
} }
} else if (g_strcmp0(name_b, name) == 0) { }
if (b_index == -1) {
b_index = i; if (pctl_player_name_string_instance_compare(name, name_b) == 0) {
} if (b_match_index == -1) {
} else if (pctl_player_name_string_instance_compare(name, name_a) == 0) { b_match_index = i;
if (a_index == -1) {
a_index = i;
}
} else if (pctl_player_name_string_instance_compare(name, name_b) == 0) {
if (b_index == -1) {
b_index = i;
} }
} }
++i; ++i;
} }
if (a_index == -1 && b_index == -1) { if (a_match_index == -1 && b_match_index == -1) {
// neither are in the list // neither are in the list
return 0; return 0;
} else if (a_index == -1) { } else if (a_match_index == -1) {
// b is in the list // b is in the list
return (b_index < any_index ? 1 : -1); return (b_match_index < any_index ? 1 : -1);
} else if (b_index == -1) { } else if (b_match_index == -1) {
// a is in the list // a is in the list
return (a_index < any_index ? -1 : 1); return (a_match_index < any_index ? -1 : 1);
} else if (a_match_index == b_match_index) {
// preserve order
return 0;
} else { } else {
// both are in the list return (a_match_index < b_match_index ? -1 : 1);
return (a_index < b_index ? -1 : 1);
} }
} }
gint player_name_compare_func(gconstpointer a, gconstpointer b) { gint player_name_compare_func(gconstpointer a, gconstpointer b, gpointer user_data) {
const PlayerctlPlayerName *name_a = a; const PlayerctlPlayerName *name_a = a;
const PlayerctlPlayerName *name_b = b; const PlayerctlPlayerName *name_b = b;
return player_name_string_compare_func(name_a->instance, name_b->instance); return player_name_string_compare_func(name_a->instance, name_b->instance, user_data);
} }
gint player_compare_func(gconstpointer a, gconstpointer b) { gint player_compare_func(gconstpointer a, gconstpointer b, gpointer user_data) {
PlayerctlPlayer *player_a = PLAYERCTL_PLAYER(a); PlayerctlPlayer *player_a = PLAYERCTL_PLAYER(a);
PlayerctlPlayer *player_b = PLAYERCTL_PLAYER(b); PlayerctlPlayer *player_b = PLAYERCTL_PLAYER(b);
gchar *name_a = NULL; gchar *name_a = NULL;
gchar *name_b = NULL; gchar *name_b = NULL;
g_object_get(player_a, "player-name", &name_a, NULL); g_object_get(player_a, "player-name", &name_a, NULL);
g_object_get(player_b, "player-name", &name_b, NULL); g_object_get(player_b, "player-name", &name_b, NULL);
gint result = player_name_string_compare_func(name_a, name_b); gint result = player_name_string_compare_func(name_a, name_b, user_data);
g_free(name_a); g_free(name_a);
g_free(name_b); g_free(name_b);
return result; return result;
@@ -1163,6 +1168,9 @@ int main(int argc, char *argv[]) {
exit(0); exit(0);
} }
player_names = parse_player_list(player_arg);
ignored_player_names = parse_player_list(ignore_player_arg);
if (print_version_and_exit) { if (print_version_and_exit) {
int result = handle_version_flag(); int result = handle_version_flag();
exit(result); exit(result);
@@ -1191,8 +1199,6 @@ int main(int argc, char *argv[]) {
} }
} }
player_names = parse_player_list(player_arg);
ignored_player_names = parse_player_list(ignore_player_arg);
playercmd_args = playercmd_args_create(command_arg, num_commands); playercmd_args = playercmd_args_create(command_arg, num_commands);
manager = playerctl_player_manager_new(&error); manager = playerctl_player_manager_new(&error);
@@ -1203,13 +1209,14 @@ int main(int argc, char *argv[]) {
} }
if (player_names != NULL && !select_all_players) { if (player_names != NULL && !select_all_players) {
playerctl_player_manager_set_sort_func(manager, (GCompareDataFunc)player_compare_func, NULL, playerctl_player_manager_set_sort_func(manager, player_compare_func, (gpointer)player_names,
NULL); NULL);
} }
g_object_get(manager, "player-names", &available_players, NULL); g_object_get(manager, "player-names", &available_players, NULL);
available_players = g_list_copy(available_players); available_players = g_list_copy(available_players);
available_players = g_list_sort(available_players, (GCompareFunc)player_name_compare_func); available_players =
g_list_sort_with_data(available_players, player_name_compare_func, (gpointer)player_names);
PlayerctlPlayerName playerctld_name = { PlayerctlPlayerName playerctld_name = {
.instance = "playerctld", .instance = "playerctld",
@@ -1222,10 +1229,11 @@ int main(int argc, char *argv[]) {
// playerctld is not ignored, was specified exactly in the list of // playerctld is not ignored, was specified exactly in the list of
// players, and is not in the list of available players. Add it to the // players, and is not in the list of available players. Add it to the
// list and try to autostart it. // list and try to autostart it.
g_debug("%s", "playerctld was selected and is not available, attempting to autostart it"); g_debug("%s", "playerctld was selected explicitly, it may autostart");
available_players = g_list_append( available_players = g_list_append(
available_players, pctl_player_name_new("playerctld", PLAYERCTL_SOURCE_DBUS_SESSION)); available_players, pctl_player_name_new("playerctld", PLAYERCTL_SOURCE_DBUS_SESSION));
available_players = g_list_sort(available_players, (GCompareFunc)player_name_compare_func); available_players = g_list_sort_with_data(available_players, player_name_compare_func,
(gpointer)player_names);
} }
gboolean has_selected = FALSE; gboolean has_selected = FALSE;

View File

@@ -21,8 +21,11 @@
#include <glib.h> #include <glib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <strings.h> #include <strings.h>
#define PLAYERCTLD_BUS_NAME "org.mpris.MediaPlayer2.playerctld"
gboolean pctl_parse_playback_status(const gchar *status_str, PlayerctlPlaybackStatus *status) { gboolean pctl_parse_playback_status(const gchar *status_str, PlayerctlPlaybackStatus *status) {
if (status_str == NULL) { if (status_str == NULL) {
return FALSE; return FALSE;
@@ -162,8 +165,9 @@ gint pctl_player_name_string_instance_compare(const gchar *name, const gchar *in
} }
gboolean exact_match = (g_strcmp0(name, instance) == 0); gboolean exact_match = (g_strcmp0(name, instance) == 0);
gboolean instance_match = !exact_match && (g_str_has_prefix(instance, name) && gboolean instance_match =
g_str_has_prefix(instance + strlen(name), ".")); !exact_match && (g_str_has_prefix(instance, name) && strlen(instance) > strlen(name) &&
g_str_has_prefix(instance + strlen(name), "."));
if (exact_match || instance_match) { if (exact_match || instance_match) {
return 0; return 0;
@@ -191,12 +195,16 @@ GList *pctl_player_name_find_instance(GList *list, gchar *player_id, PlayerctlSo
} }
void pctl_player_name_list_destroy(GList *list) { void pctl_player_name_list_destroy(GList *list) {
if (list == NULL) {
return;
}
g_list_free_full(list, (GDestroyNotify)playerctl_player_name_free); g_list_free_full(list, (GDestroyNotify)playerctl_player_name_free);
} }
GList *pctl_list_player_names_on_bus(GBusType bus_type, GError **err) { GList *pctl_list_player_names_on_bus(GBusType bus_type, GError **err) {
GError *tmp_error = NULL; GError *tmp_error = NULL;
GList *players = NULL; GList *players = NULL;
gboolean has_playerctld = FALSE;
GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync( GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(
bus_type, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.DBus", "/org/freedesktop/DBus", bus_type, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.DBus", "/org/freedesktop/DBus",
@@ -237,6 +245,46 @@ GList *pctl_list_player_names_on_bus(GBusType bus_type, GError **err) {
gsize reply_count; gsize reply_count;
const gchar **names = g_variant_get_strv(reply_child, &reply_count); const gchar **names = g_variant_get_strv(reply_child, &reply_count);
// If playerctld is in the names, get the list of players from there
// because it will be in order of activity
for (gsize i = 0; i < reply_count; i += 1) {
if (g_strcmp0(names[i], PLAYERCTLD_BUS_NAME) == 0) {
g_debug("%s", "Playerctld is running. Getting names from there.");
has_playerctld = TRUE;
GDBusProxy *playerctld_proxy = g_dbus_proxy_new_for_bus_sync(
bus_type, G_DBUS_PROXY_FLAGS_NONE, NULL, PLAYERCTLD_BUS_NAME,
"/org/mpris/MediaPlayer2", "com.github.altdesktop.playerctld", NULL, &tmp_error);
if (tmp_error != NULL) {
g_warning("Could not get player names from playerctld: %s", tmp_error->message);
g_clear_error(&tmp_error);
g_object_unref(playerctld_proxy);
break;
}
GVariant *playerctld_reply =
g_dbus_proxy_get_cached_property(playerctld_proxy, "PlayerNames");
if (playerctld_reply == NULL) {
g_warning(
"%s",
"Could not get player names from playerctld: PlayerNames property not found");
g_clear_error(&tmp_error);
g_object_unref(playerctld_proxy);
break;
}
g_variant_unref(reply);
g_free(names);
reply = playerctld_reply;
names = g_variant_get_strv(reply, &reply_count);
g_object_unref(playerctld_proxy);
has_playerctld = TRUE;
break;
}
}
size_t offset = strlen(MPRIS_PREFIX); size_t offset = strlen(MPRIS_PREFIX);
for (gsize i = 0; i < reply_count; i += 1) { for (gsize i = 0; i < reply_count; i += 1) {
if (g_str_has_prefix(names[i], MPRIS_PREFIX)) { if (g_str_has_prefix(names[i], MPRIS_PREFIX)) {
@@ -246,6 +294,10 @@ GList *pctl_list_player_names_on_bus(GBusType bus_type, GError **err) {
} }
} }
if (!has_playerctld) {
players = g_list_sort(players, (GCompareFunc)pctl_player_name_compare);
}
g_object_unref(proxy); g_object_unref(proxy);
g_variant_unref(reply); g_variant_unref(reply);
g_variant_unref(reply_child); g_variant_unref(reply_child);

View File

@@ -56,7 +56,7 @@ struct _PlayerctlPlayerManagerPrivate {
GList *player_names; GList *player_names;
GList *players; GList *players;
GCompareDataFunc sort_func; GCompareDataFunc sort_func;
gpointer *sort_data; gpointer sort_data;
GDestroyNotify sort_notify; GDestroyNotify sort_notify;
}; };
@@ -412,7 +412,7 @@ PlayerctlPlayerManager *playerctl_player_manager_new(GError **err) {
* for using this list as a priority queue. * for using this list as a priority queue.
*/ */
void playerctl_player_manager_set_sort_func(PlayerctlPlayerManager *manager, void playerctl_player_manager_set_sort_func(PlayerctlPlayerManager *manager,
GCompareDataFunc sort_func, gpointer *sort_data, GCompareDataFunc sort_func, gpointer sort_data,
GDestroyNotify notify) { GDestroyNotify notify) {
// TODO figure out how to make this work with the bindings // TODO figure out how to make this work with the bindings
manager->priv->sort_func = sort_func; manager->priv->sort_func = sort_func;

View File

@@ -93,7 +93,7 @@ void playerctl_player_manager_manage_player(PlayerctlPlayerManager *manager,
PlayerctlPlayer *player); PlayerctlPlayer *player);
void playerctl_player_manager_set_sort_func(PlayerctlPlayerManager *manager, void playerctl_player_manager_set_sort_func(PlayerctlPlayerManager *manager,
GCompareDataFunc sort_func, gpointer *sort_data, GCompareDataFunc sort_func, gpointer sort_data,
GDestroyNotify notify); GDestroyNotify notify);
void playerctl_player_manager_move_player_to_top(PlayerctlPlayerManager *manager, void playerctl_player_manager_move_player_to_top(PlayerctlPlayerManager *manager,

View File

@@ -24,4 +24,10 @@
char *pctl_player_get_instance(PlayerctlPlayer *player); char *pctl_player_get_instance(PlayerctlPlayer *player);
gint player_name_string_compare_func(gconstpointer a, gconstpointer b, gpointer user_data);
gint player_name_compare_func(gconstpointer a, gconstpointer b, gpointer user_data);
gint player_compare_func(gconstpointer a, gconstpointer b, gpointer user_data);
#endif /* __PLAYERCTL_PLAYER_PRIVATE_H__ */ #endif /* __PLAYERCTL_PLAYER_PRIVATE_H__ */

View File

@@ -6,6 +6,7 @@ import asyncio
async def setup_mpris(*names, bus_address=None, system=False): async def setup_mpris(*names, bus_address=None, system=False):
# TODO maybe they should all share a bus for speed
async def setup(name): async def setup(name):
if system: if system:
bus_type = BusType.SYSTEM bus_type = BusType.SYSTEM
@@ -23,6 +24,15 @@ async def setup_mpris(*names, bus_address=None, system=False):
return await asyncio.gather(*(setup(name) for name in names)) return await asyncio.gather(*(setup(name) for name in names))
async def setup_playerctld(bus_address=None):
bus = await MessageBus(bus_address=bus_address).connect()
playerctld = PlayerctldInterface(bus)
bus.export('/org/mpris/MediaPlayer2', playerctld)
reply = await bus.request_name('org.mpris.MediaPlayer2.playerctld')
assert reply == RequestNameReply.PRIMARY_OWNER
return playerctld
class MprisRoot(ServiceInterface): class MprisRoot(ServiceInterface):
def __init__(self): def __init__(self):
super().__init__('org.mpris.MediaPlayer2') super().__init__('org.mpris.MediaPlayer2')
@@ -240,3 +250,18 @@ class MprisPlayer(ServiceInterface):
@dbus_property(access=PropertyAccess.READ) @dbus_property(access=PropertyAccess.READ)
def CanControl(self) -> 'b': def CanControl(self) -> 'b':
return self.can_control return self.can_control
class PlayerctldInterface(ServiceInterface):
'''just enough of playerctld for testing'''
def __init__(self, bus):
super().__init__('com.github.altdesktop.playerctld')
self.bus = bus
self.player_names = []
@dbus_property(access=PropertyAccess.READ)
def PlayerNames(self) -> 'as':
return self.player_names
def disconnect(self):
self.bus.disconnect()

View File

@@ -1,5 +1,6 @@
import asyncio import asyncio
import os import os
from shlex import join
class CommandResult: class CommandResult:
@@ -33,7 +34,7 @@ class PlayerctlProcess:
break break
asyncio.get_event_loop().create_task(reader(proc.stdout)) asyncio.get_event_loop().create_task(reader(proc.stdout))
asyncio.get_event_loop().create_task(printer(proc.stderr)) # asyncio.get_event_loop().create_task(printer(proc.stderr))
def running(self): def running(self):
return self.proc.returncode is None return self.proc.returncode is None
@@ -69,3 +70,16 @@ class PlayerctlCli:
stdout, stderr = await proc.communicate() stdout, stderr = await proc.communicate()
await proc.wait() await proc.wait()
return CommandResult(stdout, stderr, proc.returncode) return CommandResult(stdout, stderr, proc.returncode)
async def list(self, players=[], ignored=[]):
args = ['--list-all']
if players:
args.extend(['--player', ','.join(players)])
if ignored:
args.extend(['--ignored-players', ','.join(ignored)])
cmd = await self.run(join(args))
assert cmd.returncode == 0, cmd.stderr
return cmd.stdout.splitlines()

View File

@@ -62,7 +62,7 @@ async def test_list_names(bus_address):
async def test_system_list_players(bus_address): async def test_system_list_players(bus_address):
system_players = await setup_mpris('system', system=True) system_players = await setup_mpris('system', system=True)
session_players = await setup_mpris('session1', bus_address=bus_address) session_players = await setup_mpris('session1', bus_address=bus_address)
playerctl = PlayerctlCli(bus_address, debug=False) playerctl = PlayerctlCli(bus_address)
result = await playerctl.run('-l') result = await playerctl.run('-l')
assert result.returncode == 0, result.stdout assert result.returncode == 0, result.stdout
assert result.stdout.split() == ['session1', 'system'] assert result.stdout.split() == ['session1', 'system']

View File

@@ -21,7 +21,7 @@ async def test_commands(bus_address):
def get_called(cmd): def get_called(cmd):
return getattr(mpris, f'{cmd.replace("-", "_")}_called') return getattr(mpris, f'{cmd.replace("-", "_")}_called')
playerctl = PlayerctlCli(bus_address, debug=True) playerctl = PlayerctlCli(bus_address)
results = await asyncio.gather(*(playerctl.run(f'-p commands {cmd}') results = await asyncio.gather(*(playerctl.run(f'-p commands {cmd}')
for cmd in commands + setters)) for cmd in commands + setters))

View File

@@ -1,4 +1,4 @@
from .mpris import setup_mpris from .mpris import setup_mpris, setup_playerctld
from .playerctl import PlayerctlCli from .playerctl import PlayerctlCli
import pytest import pytest
@@ -56,11 +56,10 @@ async def test_selection(bus_address):
(s1i, s1): (s1i, s1), (s1i, s1): (s1i, s1),
(m6, s1): (s6i, s1, s1i), (m6, s1): (s6i, s1, s1i),
(m4, m6, s3): (s6i, s3), (m4, m6, s3): (s6i, s3),
(any_player, ): (any_player, ): (s1, s1i, s2, s3, s6i),
(s2, s3, s1i, s6i, s1), # order undefined, but consistent
(s1, any_player): (s1, s1i, s2, s3, s6i), # s1 first (s1, any_player): (s1, s1i, s2, s3, s6i), # s1 first
(any_player, s1): (s2, s3, s6i, s1i, s1), # s1 last (any_player, s1): (s2, s3, s6i, s1, s1i), # s1 last
(m6, any_player, s2): (s6i, s3, s1i, s1, s2), # s6 first, s2 last (m6, any_player, s2): (s6i, s1, s1i, s3, s2), # s6 first, s2 last
(m6, s1, any_player, s2): (s6i, s1, s1i, s3, s2), (m6, s1, any_player, s2): (s6i, s1, s1i, s3, s2),
} }
@@ -68,9 +67,66 @@ async def test_selection(bus_address):
for selection, expected in selections.items(): for selection, expected in selections.items():
result = await select(*selection) result = await select(*selection)
assert result == expected[0] assert result == expected[0], (selection, expected, result)
result = await select_many(*selection) result = await select_many(*selection)
assert result == expected assert result == expected
for mpris in mpris_players: for mpris in mpris_players:
mpris.disconnect() mpris.disconnect()
@pytest.mark.asyncio
async def test_daemon_selection(bus_address):
playerctld = await setup_playerctld(bus_address=bus_address)
playerctl = PlayerctlCli(bus_address)
def iface_name(player_name):
return f'org.mpris.MediaPlayer2.{player_name}'
def set_players(players):
playerctld.player_names = [iface_name(p) for p in players]
s1 = 'selection1'
s1i = 'selection1.i_123'
s2 = 'selection2'
s2i = 'selection2.i_123'
s3 = 'selection3'
m4 = 'selection4'
m5 = 'selection5'
m6 = 'selection6'
s6i = 'selection6.i_2'
any_player = '%any'
# selection, players, expected result
all_players = [s1, s1i, s2, s3, s6i]
tests = [
(None, all_players, all_players),
(all_players, all_players, all_players),
([s2], [s1, s2], [s2]),
([s1], [s2, s1i, s1], [s1i, s1]),
([s1], [s2, s1, s1i], [s1, s1i]),
([s1i, s1], [s1, s1i], [s1i, s1]),
([any_player], all_players, all_players),
([any_player, s1], [s1, s1i, s2i, s2], [s2i, s2, s1, s1i]),
([any_player, s1], [s1, s1i, s2, s2i], [s2, s2i, s1, s1i]),
([any_player, s1], [s1i, s1, s2i, s2], [s2i, s2, s1i, s1]),
([any_player, s1], [s1i, s1, s2, s2i], [s2, s2i, s1i, s1]),
([s2, any_player], [s1, s1i, s2i, s2], [s2i, s2, s1, s1i]),
([s2, any_player], [s1, s1i, s2, s2i], [s2, s2i, s1, s1i]),
([s2, any_player], [s1i, s1, s2i, s2], [s2i, s2, s1i, s1]),
([s2, any_player], [s1i, s1, s2, s2i], [s2, s2i, s1i, s1]),
([s2i, any_player], [s1i, s1, s2, s2i], [s2i, s1i, s1, s2]),
]
async def daemon_selection_test(test):
selection, players, expected = test
set_players(players)
result = await playerctl.list(players=selection)
assert result == expected, test
for test in tests:
# unfortunately it won't work in parallel because there's only one
# playerctld
await daemon_selection_test(test)
playerctld.disconnect()