Add program arguments to MPD's current playlist
I was tired of using `mpv --no-video`, this is a bit better. It's all rather quirky, but very little code is involved. I've added a few related TODO entries.
This commit is contained in:
parent
fba1210e9f
commit
267598643a
2
NEWS
2
NEWS
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
* Made it possible to show a spectrum visualiser when built against FFTW
|
* Made it possible to show a spectrum visualiser when built against FFTW
|
||||||
|
|
||||||
|
* Any program arguments are now added to MPD's current playlist
|
||||||
|
|
||||||
|
|
||||||
1.0.0 (2020-11-05)
|
1.0.0 (2020-11-05)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ nncmpp - terminal-based MPD client
|
||||||
|
|
||||||
Synopsis
|
Synopsis
|
||||||
--------
|
--------
|
||||||
*nncmpp* [_OPTION_]...
|
*nncmpp* [_OPTION_]... [_URL_ | _PATH_]...
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
@ -19,6 +19,10 @@ you with an overview of all key bindings and the actions they're assigned to.
|
||||||
Individual tabs can be switched to either using the mouse or by pressing *M-1*
|
Individual tabs can be switched to either using the mouse or by pressing *M-1*
|
||||||
through *M-9*, corresponding to the order they appear in.
|
through *M-9*, corresponding to the order they appear in.
|
||||||
|
|
||||||
|
As a convenience utility, any program arguments are added to the MPD queue.
|
||||||
|
Note that to add files outside the database, you need to connect to MPD using
|
||||||
|
a socket file.
|
||||||
|
|
||||||
Options
|
Options
|
||||||
-------
|
-------
|
||||||
*-d*, *--debug*::
|
*-d*, *--debug*::
|
||||||
|
|
89
nncmpp.c
89
nncmpp.c
|
@ -962,6 +962,7 @@ static struct app_context
|
||||||
|
|
||||||
struct config config; ///< Program configuration
|
struct config config; ///< Program configuration
|
||||||
struct strv streams; ///< List of "name NUL URI NUL"
|
struct strv streams; ///< List of "name NUL URI NUL"
|
||||||
|
struct strv enqueue; ///< Items to enqueue once connected
|
||||||
|
|
||||||
struct tab *help_tab; ///< Special help tab
|
struct tab *help_tab; ///< Special help tab
|
||||||
struct tab *tabs; ///< All other tabs
|
struct tab *tabs; ///< All other tabs
|
||||||
|
@ -1224,6 +1225,7 @@ app_init_context (void)
|
||||||
g.client = mpd_client_make (&g.poller);
|
g.client = mpd_client_make (&g.poller);
|
||||||
g.config = config_make ();
|
g.config = config_make ();
|
||||||
g.streams = strv_make ();
|
g.streams = strv_make ();
|
||||||
|
g.enqueue = strv_make ();
|
||||||
g.playlist = item_list_make ();
|
g.playlist = item_list_make ();
|
||||||
|
|
||||||
g.playback_info = str_map_make (free);
|
g.playback_info = str_map_make (free);
|
||||||
|
@ -1285,6 +1287,7 @@ app_free_context (void)
|
||||||
mpd_client_free (&g.client);
|
mpd_client_free (&g.client);
|
||||||
str_map_free (&g.playback_info);
|
str_map_free (&g.playback_info);
|
||||||
strv_free (&g.streams);
|
strv_free (&g.streams);
|
||||||
|
strv_free (&g.enqueue);
|
||||||
item_list_free (&g.playlist);
|
item_list_free (&g.playlist);
|
||||||
|
|
||||||
#ifdef WITH_FFTW
|
#ifdef WITH_FFTW
|
||||||
|
@ -4186,12 +4189,55 @@ mpd_queue_reconnect (void)
|
||||||
poller_timer_set (&g.connect_event, 5 * 1000);
|
poller_timer_set (&g.connect_event, 5 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On an error, MPD discards the rest of our enqueuing commands--work it around
|
||||||
|
static void mpd_enqueue_step (size_t start_offset);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mpd_on_ready (struct mpd_client *c)
|
mpd_on_enqueue_response (const struct mpd_response *response,
|
||||||
|
const struct strv *data, void *user_data)
|
||||||
|
{
|
||||||
|
(void) data;
|
||||||
|
intptr_t start_offset = (intptr_t) user_data;
|
||||||
|
|
||||||
|
if (response->success)
|
||||||
|
strv_reset (&g.enqueue);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Their addition may also overflow, but YOLO
|
||||||
|
hard_assert (start_offset >= 0 && response->list_offset >= 0);
|
||||||
|
|
||||||
|
print_error ("%s: %s", response->message_text,
|
||||||
|
g.enqueue.vector[start_offset + response->list_offset]);
|
||||||
|
mpd_enqueue_step (start_offset + response->list_offset + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mpd_enqueue_step (size_t start_offset)
|
||||||
|
{
|
||||||
|
struct mpd_client *c = &g.client;
|
||||||
|
if (start_offset >= g.enqueue.len)
|
||||||
|
{
|
||||||
|
strv_reset (&g.enqueue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: might want to consider using addid and autoplaying
|
||||||
|
mpd_client_list_begin (c);
|
||||||
|
for (size_t i = start_offset; i < g.enqueue.len; i++)
|
||||||
|
mpd_client_send_command (c, "add", g.enqueue.vector[i], NULL);
|
||||||
|
mpd_client_list_end (c);
|
||||||
|
mpd_client_add_task (c, mpd_on_enqueue_response, (void *) start_offset);
|
||||||
|
mpd_client_idle (c, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mpd_on_ready (void)
|
||||||
{
|
{
|
||||||
mpd_request_info ();
|
mpd_request_info ();
|
||||||
library_tab_reload (NULL);
|
library_tab_reload (NULL);
|
||||||
spectrum_setup_fifo ();
|
spectrum_setup_fifo ();
|
||||||
|
mpd_enqueue_step (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -4203,7 +4249,7 @@ mpd_on_password_response (const struct mpd_response *response,
|
||||||
struct mpd_client *c = &g.client;
|
struct mpd_client *c = &g.client;
|
||||||
|
|
||||||
if (response->success)
|
if (response->success)
|
||||||
mpd_on_ready (c);
|
mpd_on_ready ();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
print_error ("%s: %s",
|
print_error ("%s: %s",
|
||||||
|
@ -4226,7 +4272,7 @@ mpd_on_connected (void *user_data)
|
||||||
mpd_client_add_task (c, mpd_on_password_response, NULL);
|
mpd_client_add_task (c, mpd_on_password_response, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mpd_on_ready (c);
|
mpd_on_ready ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -4525,6 +4571,28 @@ app_init_poller_events (void)
|
||||||
g.refresh_event.dispatcher = app_on_refresh;
|
g.refresh_event.dispatcher = app_on_refresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_init_enqueue (char *argv[], int argc)
|
||||||
|
{
|
||||||
|
// TODO: MPD is unwilling to play directories, so perhaps recurse ourselves
|
||||||
|
char cwd[4096] = "";
|
||||||
|
for (int i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
// This is a super-trivial method of URL detection, however anything
|
||||||
|
// contaning the scheme and authority delimiters in a sequence is most
|
||||||
|
// certainly not a filesystem path, and thus it will work as expected.
|
||||||
|
// Error handling may be done by MPD.
|
||||||
|
const char *path_or_URL = argv[i];
|
||||||
|
if (*path_or_URL == '/' || strstr (path_or_URL, "://"))
|
||||||
|
strv_append (&g.enqueue, path_or_URL);
|
||||||
|
else if (!*cwd && !getcwd (cwd, sizeof cwd))
|
||||||
|
exit_fatal ("getcwd: %s", strerror (errno));
|
||||||
|
else
|
||||||
|
strv_append_owned (&g.enqueue,
|
||||||
|
xstrdup_printf ("%s/%s", cwd, path_or_URL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char *argv[])
|
main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -4537,7 +4605,8 @@ main (int argc, char *argv[])
|
||||||
};
|
};
|
||||||
|
|
||||||
struct opt_handler oh =
|
struct opt_handler oh =
|
||||||
opt_handler_make (argc, argv, opts, NULL, "Terminal-based MPD client.");
|
opt_handler_make (argc, argv, opts,
|
||||||
|
"[URL | PATH]...", "Terminal-based MPD client.");
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = opt_handler_get (&oh)) != -1)
|
while ((c = opt_handler_get (&oh)) != -1)
|
||||||
|
@ -4560,12 +4629,6 @@ main (int argc, char *argv[])
|
||||||
|
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
if (argc)
|
|
||||||
{
|
|
||||||
opt_handler_usage (&oh, stderr);
|
|
||||||
exit (EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
opt_handler_free (&oh);
|
opt_handler_free (&oh);
|
||||||
|
|
||||||
// We only need to convert to and from the terminal encoding
|
// We only need to convert to and from the terminal encoding
|
||||||
|
@ -4573,6 +4636,7 @@ main (int argc, char *argv[])
|
||||||
print_warning ("failed to set the locale");
|
print_warning ("failed to set the locale");
|
||||||
|
|
||||||
app_init_context ();
|
app_init_context ();
|
||||||
|
app_init_enqueue (argv, argc);
|
||||||
app_load_configuration ();
|
app_load_configuration ();
|
||||||
signals_setup_handlers ();
|
signals_setup_handlers ();
|
||||||
app_init_poller_events ();
|
app_init_poller_events ();
|
||||||
|
@ -4596,6 +4660,11 @@ main (int argc, char *argv[])
|
||||||
app_prepend_tab (current_tab_init ());
|
app_prepend_tab (current_tab_init ());
|
||||||
app_switch_tab ((g.help_tab = help_tab_init ()));
|
app_switch_tab ((g.help_tab = help_tab_init ()));
|
||||||
|
|
||||||
|
// TODO: the help tab should be the default for new users only,
|
||||||
|
// so provide a configuration option to flip this
|
||||||
|
if (argc)
|
||||||
|
app_switch_tab (&g_current_tab);
|
||||||
|
|
||||||
g.polling = true;
|
g.polling = true;
|
||||||
while (g.polling)
|
while (g.polling)
|
||||||
poller_run (&g.poller);
|
poller_run (&g.poller);
|
||||||
|
|
Loading…
Reference in New Issue