@@ -20,11 +20,11 @@ RUN pip3 install -r requirements.txt
|
||||
|
||||
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 && \
|
||||
ninja -C build && ninja -C build install
|
||||
RUN mkdir -p /run/dbus
|
||||
ENV PYTHONASYNCIODEBUG=1
|
||||
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"]
|
||||
|
11
README.md
11
README.md
@@ -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.
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
#### 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
|
||||
|
||||
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.
|
||||
|
@@ -723,7 +723,7 @@ static gboolean playercmd_metadata(PlayerctlPlayer *player, gchar **argv, gint a
|
||||
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);
|
||||
GError *error = NULL;
|
||||
managed_players_execute_command(&error);
|
||||
@@ -883,6 +883,28 @@ static GList *parse_player_list(gchar *player_list_arg) {
|
||||
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() {
|
||||
g_print("v%s\n", PLAYERCTL_VERSION_S);
|
||||
return 0;
|
||||
@@ -892,22 +914,26 @@ static int handle_list_all_flag() {
|
||||
GError *tmp_error = NULL;
|
||||
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) {
|
||||
g_printerr("%s\n", tmp_error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (player_names_list == NULL) {
|
||||
if (!no_status_error_messages) {
|
||||
g_printerr("No players were found\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
gboolean one_selected = FALSE;
|
||||
GList *l = NULL;
|
||||
for (l = player_names_list; l != NULL; l = l->next) {
|
||||
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);
|
||||
@@ -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,
|
||||
gpointer *data) {
|
||||
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_b = b;
|
||||
GList *names = user_data;
|
||||
|
||||
if (g_strcmp0(name_a, name_b) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int a_index = -1;
|
||||
int b_index = -1;
|
||||
int a_match_index = -1;
|
||||
int b_match_index = -1;
|
||||
|
||||
int any_index = INT_MAX;
|
||||
int i = 0;
|
||||
GList *l = NULL;
|
||||
for (l = player_names; l != NULL; l = l->next) {
|
||||
for (l = names; l != NULL; l = l->next) {
|
||||
gchar *name = l->data;
|
||||
|
||||
if (g_strcmp0(name, "%any") == 0) {
|
||||
if (any_index == INT_MAX) {
|
||||
any_index = i;
|
||||
}
|
||||
} else if (g_strcmp0(name_a, name) == 0) {
|
||||
if (a_index == -1) {
|
||||
a_index = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
} else if (pctl_player_name_string_instance_compare(name, name_a) == 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (pctl_player_name_string_instance_compare(name, name_b) == 0) {
|
||||
if (b_match_index == -1) {
|
||||
b_match_index = i;
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if (a_index == -1 && b_index == -1) {
|
||||
if (a_match_index == -1 && b_match_index == -1) {
|
||||
// neither are in the list
|
||||
return 0;
|
||||
} else if (a_index == -1) {
|
||||
} else if (a_match_index == -1) {
|
||||
// b is in the list
|
||||
return (b_index < any_index ? 1 : -1);
|
||||
} else if (b_index == -1) {
|
||||
return (b_match_index < any_index ? 1 : -1);
|
||||
} else if (b_match_index == -1) {
|
||||
// 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 {
|
||||
// both are in the list
|
||||
return (a_index < b_index ? -1 : 1);
|
||||
return (a_match_index < b_match_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_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_b = PLAYERCTL_PLAYER(b);
|
||||
gchar *name_a = NULL;
|
||||
gchar *name_b = NULL;
|
||||
g_object_get(player_a, "player-name", &name_a, 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_b);
|
||||
return result;
|
||||
@@ -1163,6 +1168,9 @@ int main(int argc, char *argv[]) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
player_names = parse_player_list(player_arg);
|
||||
ignored_player_names = parse_player_list(ignore_player_arg);
|
||||
|
||||
if (print_version_and_exit) {
|
||||
int result = handle_version_flag();
|
||||
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);
|
||||
|
||||
manager = playerctl_player_manager_new(&error);
|
||||
@@ -1203,13 +1209,14 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
g_object_get(manager, "player-names", &available_players, NULL);
|
||||
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 = {
|
||||
.instance = "playerctld",
|
||||
@@ -1222,10 +1229,11 @@ int main(int argc, char *argv[]) {
|
||||
// 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
|
||||
// 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, 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;
|
||||
|
@@ -21,8 +21,11 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#define PLAYERCTLD_BUS_NAME "org.mpris.MediaPlayer2.playerctld"
|
||||
|
||||
gboolean pctl_parse_playback_status(const gchar *status_str, PlayerctlPlaybackStatus *status) {
|
||||
if (status_str == NULL) {
|
||||
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 instance_match = !exact_match && (g_str_has_prefix(instance, name) &&
|
||||
g_str_has_prefix(instance + strlen(name), "."));
|
||||
gboolean instance_match =
|
||||
!exact_match && (g_str_has_prefix(instance, name) && strlen(instance) > strlen(name) &&
|
||||
g_str_has_prefix(instance + strlen(name), "."));
|
||||
|
||||
if (exact_match || instance_match) {
|
||||
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) {
|
||||
if (list == NULL) {
|
||||
return;
|
||||
}
|
||||
g_list_free_full(list, (GDestroyNotify)playerctl_player_name_free);
|
||||
}
|
||||
|
||||
GList *pctl_list_player_names_on_bus(GBusType bus_type, GError **err) {
|
||||
GError *tmp_error = NULL;
|
||||
GList *players = NULL;
|
||||
gboolean has_playerctld = FALSE;
|
||||
|
||||
GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(
|
||||
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;
|
||||
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);
|
||||
for (gsize i = 0; i < reply_count; i += 1) {
|
||||
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_variant_unref(reply);
|
||||
g_variant_unref(reply_child);
|
||||
|
@@ -56,7 +56,7 @@ struct _PlayerctlPlayerManagerPrivate {
|
||||
GList *player_names;
|
||||
GList *players;
|
||||
GCompareDataFunc sort_func;
|
||||
gpointer *sort_data;
|
||||
gpointer sort_data;
|
||||
GDestroyNotify sort_notify;
|
||||
};
|
||||
|
||||
@@ -412,7 +412,7 @@ PlayerctlPlayerManager *playerctl_player_manager_new(GError **err) {
|
||||
* for using this list as a priority queue.
|
||||
*/
|
||||
void playerctl_player_manager_set_sort_func(PlayerctlPlayerManager *manager,
|
||||
GCompareDataFunc sort_func, gpointer *sort_data,
|
||||
GCompareDataFunc sort_func, gpointer sort_data,
|
||||
GDestroyNotify notify) {
|
||||
// TODO figure out how to make this work with the bindings
|
||||
manager->priv->sort_func = sort_func;
|
||||
|
@@ -93,7 +93,7 @@ void playerctl_player_manager_manage_player(PlayerctlPlayerManager *manager,
|
||||
PlayerctlPlayer *player);
|
||||
|
||||
void playerctl_player_manager_set_sort_func(PlayerctlPlayerManager *manager,
|
||||
GCompareDataFunc sort_func, gpointer *sort_data,
|
||||
GCompareDataFunc sort_func, gpointer sort_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
void playerctl_player_manager_move_player_to_top(PlayerctlPlayerManager *manager,
|
||||
|
@@ -24,4 +24,10 @@
|
||||
|
||||
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__ */
|
||||
|
@@ -6,6 +6,7 @@ import asyncio
|
||||
|
||||
|
||||
async def setup_mpris(*names, bus_address=None, system=False):
|
||||
# TODO maybe they should all share a bus for speed
|
||||
async def setup(name):
|
||||
if 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))
|
||||
|
||||
|
||||
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):
|
||||
def __init__(self):
|
||||
super().__init__('org.mpris.MediaPlayer2')
|
||||
@@ -240,3 +250,18 @@ class MprisPlayer(ServiceInterface):
|
||||
@dbus_property(access=PropertyAccess.READ)
|
||||
def CanControl(self) -> 'b':
|
||||
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()
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import asyncio
|
||||
import os
|
||||
from shlex import join
|
||||
|
||||
|
||||
class CommandResult:
|
||||
@@ -33,7 +34,7 @@ class PlayerctlProcess:
|
||||
break
|
||||
|
||||
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):
|
||||
return self.proc.returncode is None
|
||||
@@ -69,3 +70,16 @@ class PlayerctlCli:
|
||||
stdout, stderr = await proc.communicate()
|
||||
await proc.wait()
|
||||
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()
|
||||
|
@@ -62,7 +62,7 @@ async def test_list_names(bus_address):
|
||||
async def test_system_list_players(bus_address):
|
||||
system_players = await setup_mpris('system', system=True)
|
||||
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')
|
||||
assert result.returncode == 0, result.stdout
|
||||
assert result.stdout.split() == ['session1', 'system']
|
||||
|
@@ -21,7 +21,7 @@ async def test_commands(bus_address):
|
||||
def get_called(cmd):
|
||||
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}')
|
||||
for cmd in commands + setters))
|
||||
|
@@ -1,4 +1,4 @@
|
||||
from .mpris import setup_mpris
|
||||
from .mpris import setup_mpris, setup_playerctld
|
||||
from .playerctl import PlayerctlCli
|
||||
import pytest
|
||||
|
||||
@@ -56,11 +56,10 @@ async def test_selection(bus_address):
|
||||
(s1i, s1): (s1i, s1),
|
||||
(m6, s1): (s6i, s1, s1i),
|
||||
(m4, m6, s3): (s6i, s3),
|
||||
(any_player, ):
|
||||
(s2, s3, s1i, s6i, s1), # order undefined, but consistent
|
||||
(any_player, ): (s1, s1i, s2, s3, s6i),
|
||||
(s1, any_player): (s1, s1i, s2, s3, s6i), # s1 first
|
||||
(any_player, s1): (s2, s3, s6i, s1i, s1), # s1 last
|
||||
(m6, any_player, s2): (s6i, s3, s1i, s1, s2), # s6 first, s2 last
|
||||
(any_player, s1): (s2, s3, s6i, s1, s1i), # s1 last
|
||||
(m6, any_player, s2): (s6i, s1, s1i, s3, s2), # s6 first, s2 last
|
||||
(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():
|
||||
result = await select(*selection)
|
||||
assert result == expected[0]
|
||||
assert result == expected[0], (selection, expected, result)
|
||||
result = await select_many(*selection)
|
||||
assert result == expected
|
||||
|
||||
for mpris in mpris_players:
|
||||
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()
|
||||
|
Reference in New Issue
Block a user