From 267598643a17f5f7a2ba1aaf0feba4d1dfbcbcbd Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch Date: Tue, 7 Sep 2021 06:26:00 +0200 Subject: 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. --- NEWS | 2 ++ nncmpp.adoc | 6 ++++- nncmpp.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 59432ea..136a134 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ * 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) diff --git a/nncmpp.adoc b/nncmpp.adoc index 0909dd3..bcbb740 100644 --- a/nncmpp.adoc +++ b/nncmpp.adoc @@ -10,7 +10,7 @@ nncmpp - terminal-based MPD client Synopsis -------- -*nncmpp* [_OPTION_]... +*nncmpp* [_OPTION_]... [_URL_ | _PATH_]... 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* 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 ------- *-d*, *--debug*:: diff --git a/nncmpp.c b/nncmpp.c index e0c0409..1366302 100644 --- a/nncmpp.c +++ b/nncmpp.c @@ -962,6 +962,7 @@ static struct app_context struct config config; ///< Program configuration 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 *tabs; ///< All other tabs @@ -1224,6 +1225,7 @@ app_init_context (void) g.client = mpd_client_make (&g.poller); g.config = config_make (); g.streams = strv_make (); + g.enqueue = strv_make (); g.playlist = item_list_make (); g.playback_info = str_map_make (free); @@ -1285,6 +1287,7 @@ app_free_context (void) mpd_client_free (&g.client); str_map_free (&g.playback_info); strv_free (&g.streams); + strv_free (&g.enqueue); item_list_free (&g.playlist); #ifdef WITH_FFTW @@ -4186,12 +4189,55 @@ mpd_queue_reconnect (void) 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 +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 (struct mpd_client *c) +mpd_on_ready (void) { mpd_request_info (); library_tab_reload (NULL); spectrum_setup_fifo (); + mpd_enqueue_step (0); } static void @@ -4203,7 +4249,7 @@ mpd_on_password_response (const struct mpd_response *response, struct mpd_client *c = &g.client; if (response->success) - mpd_on_ready (c); + mpd_on_ready (); else { print_error ("%s: %s", @@ -4226,7 +4272,7 @@ mpd_on_connected (void *user_data) mpd_client_add_task (c, mpd_on_password_response, NULL); } else - mpd_on_ready (c); + mpd_on_ready (); } static void @@ -4525,6 +4571,28 @@ app_init_poller_events (void) 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 main (int argc, char *argv[]) { @@ -4537,7 +4605,8 @@ main (int argc, char *argv[]) }; 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; while ((c = opt_handler_get (&oh)) != -1) @@ -4560,12 +4629,6 @@ main (int argc, char *argv[]) argc -= optind; argv += optind; - - if (argc) - { - opt_handler_usage (&oh, stderr); - exit (EXIT_FAILURE); - } opt_handler_free (&oh); // 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"); app_init_context (); + app_init_enqueue (argv, argc); app_load_configuration (); signals_setup_handlers (); app_init_poller_events (); @@ -4596,6 +4660,11 @@ main (int argc, char *argv[]) app_prepend_tab (current_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; while (g.polling) poller_run (&g.poller); -- cgit v1.2.3-70-g09d2