summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoruser <user@mail.com>2025-07-09 18:43:35 -0400
committeruser <user@mail.com>2025-07-09 18:44:18 -0400
commit1e8a4b50adeb773e946bc4101d4fb6e983f6c160 (patch)
tree1d66cfbbaf6084006c0be13079bca6754ea271c5
parent54b2e7496154d6e0f5926a2bb4c87c41a0475d50 (diff)
refactor
-rw-r--r--Makefile2
-rw-r--r--main.c2
-rw-r--r--mpd_ws.c351
-rw-r--r--mpdws.c244
-rw-r--r--mpdws.h (renamed from mpd_ws.h)0
5 files changed, 246 insertions, 353 deletions
diff --git a/Makefile b/Makefile
index d6cfd12..c1d0d0e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
.PHONY: all clean install uninstall
PROG= mpdws
-SRCS= mpd_ws.h mpd_ws.c main.c
+SRCS= mpdws.h mpdws.c main.c
NOMAN=
CPPFLAGS+= -I/usr/local/include
diff --git a/main.c b/main.c
index db21c05..be6b44d 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-#include "mpd_ws.h"
+#include "mpdws.h"
#include <stdio.h>
int main() {
diff --git a/mpd_ws.c b/mpd_ws.c
deleted file mode 100644
index c24cb6b..0000000
--- a/mpd_ws.c
+++ /dev/null
@@ -1,351 +0,0 @@
-#include "mpd_ws.h"
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#include <syslog.h>
-#include <time.h>
-#include <unistd.h>
-
-/* Global server instance for signal handling */
-static struct mpd_ws_server *g_server = NULL;
-
-/* Signal handler */
-static void signal_handler(int sig) {
- (void)sig; /* Suppress unused param warning */
- if (g_server) {
- g_server->running = 0;
- }
-}
-
-/* WebSocket callback */
-static int ws_callback(struct lws *wsi, enum lws_callback_reasons reason,
- void *user, void *in, size_t len) {
- (void)user; /* Suppress unused param warnings */
- (void)in;
- (void)len;
-
- struct mpd_ws_server *server =
- (struct mpd_ws_server *)lws_context_user(lws_get_context(wsi));
-
- switch (reason) {
- case LWS_CALLBACK_ESTABLISHED:
- client_add(server, wsi);
- /* Send current song to new client */
- if (strlen(server->current_song) > 0) {
- lws_callback_on_writable(wsi);
- }
- break;
-
- case LWS_CALLBACK_CLOSED:
- client_remove(server, wsi);
- break;
-
- case LWS_CALLBACK_SERVER_WRITEABLE:
- if (strlen(server->current_song) > 0) {
- unsigned char buf[LWS_PRE + MAX_MESSAGE_SIZE];
- size_t msg_len = strlen(server->current_song);
- memcpy(&buf[LWS_PRE], server->current_song, msg_len);
- lws_write(wsi, &buf[LWS_PRE], msg_len, LWS_WRITE_TEXT);
- }
- break;
-
- case LWS_CALLBACK_RECEIVE:
- /* Ignore client input */
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/* WebSocket protocols */
-static struct lws_protocols protocols[] = {
- {"mpd-protocol", ws_callback, 0, MAX_MESSAGE_SIZE, 0, NULL, 0},
- {NULL, NULL, 0, 0, 0, NULL, 0}};
-
-/* Initialize the server */
-int mpd_ws_init(struct mpd_ws_server *server) {
- memset(server, 0, sizeof(*server));
-
- /* Setup signal handlers */
- g_server = server;
- signal(SIGINT, signal_handler);
- signal(SIGTERM, signal_handler);
-
- /* Initialize syslog */
- openlog("mpd_ws", LOG_PID | LOG_NDELAY, LOG_DAEMON);
-
- /* Create WebSocket context */
- struct lws_context_creation_info info = {0};
- info.port = WEBSOCKET_PORT;
- info.protocols = protocols;
- info.gid = -1;
- info.uid = -1;
- info.user = server;
-
- server->ws_context = lws_create_context(&info);
- if (!server->ws_context) {
- syslog(LOG_ERR, "Failed to create WebSocket context");
- return -1;
- }
-
- syslog(LOG_INFO, "WebSocket server listening on port %d", WEBSOCKET_PORT);
-
- /* Connect to MPD */
- mpd_ws_connect(server);
-
- server->running = 1;
- return 0;
-}
-
-/* Main server loop */
-void mpd_ws_run(struct mpd_ws_server *server) {
- time_t last_reconnect = 0;
-
- while (server->running) {
- /* Service WebSocket events */
- lws_service(server->ws_context, 10);
-
- /* Handle MPD reconnection */
- if (!mpd_ws_is_connected(server)) {
- time_t now = time(NULL);
- if (now - last_reconnect >= RECONNECT_INTERVAL_SEC) {
- if (mpd_ws_connect(server) == 0) {
- mpd_ws_update_song(server);
- mpd_ws_start_idle(server);
- }
- last_reconnect = now;
- }
- sleep(SELECT_TIMEOUT_SEC);
- continue;
- }
-
- /* Start idle mode if not active */
- if (!server->mpd_idle_active) {
- if (mpd_ws_start_idle(server) < 0) {
- mpd_ws_disconnect(server);
- continue;
- }
- }
-
- /* Check for MPD events */
- fd_set readfds;
- int mpd_fd = mpd_connection_get_fd(server->mpd_conn);
- struct timeval timeout = {0, SELECT_TIMEOUT_SEC};
-
- FD_ZERO(&readfds);
- FD_SET(mpd_fd, &readfds);
-
- int result = select(mpd_fd + 1, &readfds, NULL, NULL, &timeout);
- if (result > 0 && FD_ISSET(mpd_fd, &readfds)) {
- mpd_ws_process_idle(server);
- if (!mpd_ws_is_connected(server)) {
- mpd_ws_disconnect(server);
- }
- }
- }
-}
-
-/* Cleanup and shutdown */
-void mpd_ws_cleanup(struct mpd_ws_server *server) {
- syslog(LOG_INFO, "Shutting down");
-
- mpd_ws_disconnect(server);
-
- if (server->ws_context) {
- lws_context_destroy(server->ws_context);
- }
-
- /* Free client list */
- while (server->clients) {
- struct client_session *next = server->clients->next;
- free(server->clients);
- server->clients = next;
- }
-
- closelog();
-}
-
-/* Stop the server */
-void mpd_ws_stop(struct mpd_ws_server *server) { server->running = 0; }
-
-/* Connect to MPD */
-int mpd_ws_connect(struct mpd_ws_server *server) {
- if (server->mpd_conn) {
- mpd_connection_free(server->mpd_conn);
- }
-
- server->mpd_conn = mpd_connection_new(MPD_HOST, MPD_PORT, 0);
- if (mpd_connection_get_error(server->mpd_conn) != MPD_ERROR_SUCCESS) {
- syslog(LOG_ERR, "Failed to connect to MPD: %s",
- mpd_connection_get_error_message(server->mpd_conn));
- mpd_connection_free(server->mpd_conn);
- server->mpd_conn = NULL;
- return -1;
- }
-
- server->mpd_idle_active = 0;
- syslog(LOG_INFO, "Connected to MPD at %s:%d", MPD_HOST, MPD_PORT);
- return 0;
-}
-
-/* Disconnect from MPD */
-void mpd_ws_disconnect(struct mpd_ws_server *server) {
- if (server->mpd_conn) {
- if (server->mpd_idle_active) {
- mpd_send_noidle(server->mpd_conn);
- mpd_response_finish(server->mpd_conn);
- }
- mpd_connection_free(server->mpd_conn);
- server->mpd_conn = NULL;
- }
- server->mpd_idle_active = 0;
-}
-
-/* Check if MPD is connected */
-int mpd_ws_is_connected(struct mpd_ws_server *server) {
- return server->mpd_conn &&
- mpd_connection_get_error(server->mpd_conn) == MPD_ERROR_SUCCESS;
-}
-
-/* Update current song and broadcast */
-void mpd_ws_update_song(struct mpd_ws_server *server) {
- if (!mpd_ws_is_connected(server)) {
- return;
- }
-
- mpd_command_list_begin(server->mpd_conn, false);
- mpd_send_current_song(server->mpd_conn);
- mpd_command_list_end(server->mpd_conn);
-
- struct mpd_song *song = mpd_recv_song(server->mpd_conn);
- if (mpd_connection_get_error(server->mpd_conn) != MPD_ERROR_SUCCESS) {
- syslog(LOG_ERR, "Failed to get current song: %s",
- mpd_connection_get_error_message(server->mpd_conn));
- return;
- }
-
- /* Create song message */
- if (song == NULL) {
- snprintf(server->current_song, MAX_MESSAGE_SIZE, "Now Playing: No song");
- } else {
- const char *artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0);
- const char *title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
-
- if (artist && title) {
- snprintf(server->current_song, MAX_MESSAGE_SIZE, "Now Playing: %s - %s",
- artist, title);
- } else if (title) {
- snprintf(server->current_song, MAX_MESSAGE_SIZE, "Now Playing: %s",
- title);
- } else {
- const char *uri = mpd_song_get_uri(song);
- snprintf(server->current_song, MAX_MESSAGE_SIZE, "Now Playing: %s",
- uri ? uri : "Unknown");
- }
- }
-
- syslog(LOG_DEBUG, "Broadcasting: %s", server->current_song);
-
- /* Only broadcast if song actually changed */
- if (strcmp(server->current_song, server->previous_song) != 0) {
- client_broadcast(server, server->current_song);
- strlcpy(server->previous_song, server->current_song,
- sizeof(server->previous_song));
- }
-
- if (song) {
- mpd_song_free(song);
- }
- mpd_response_finish(server->mpd_conn);
-}
-
-/* Start MPD idle mode */
-int mpd_ws_start_idle(struct mpd_ws_server *server) {
- if (!mpd_ws_is_connected(server)) {
- return -1;
- }
-
- if (!mpd_send_idle_mask(server->mpd_conn, MPD_IDLE_PLAYER)) {
- syslog(LOG_ERR, "Failed to send idle command: %s",
- mpd_connection_get_error_message(server->mpd_conn));
- return -1;
- }
-
- server->mpd_idle_active = 1;
- return 0;
-}
-
-/* Process MPD idle response */
-void mpd_ws_process_idle(struct mpd_ws_server *server) {
- if (!mpd_ws_is_connected(server) || !server->mpd_idle_active) {
- return;
- }
-
- enum mpd_idle events = mpd_recv_idle(server->mpd_conn, false);
- server->mpd_idle_active = 0;
-
- if (mpd_connection_get_error(server->mpd_conn) != MPD_ERROR_SUCCESS) {
- syslog(LOG_WARNING, "MPD idle error: %s",
- mpd_connection_get_error_message(server->mpd_conn));
- return;
- }
-
- if (events & MPD_IDLE_PLAYER) {
- mpd_ws_update_song(server);
- }
-
- /* Restart idle mode */
- mpd_ws_start_idle(server);
-}
-
-/* Add client */
-void client_add(struct mpd_ws_server *server, struct lws *wsi) {
- struct client_session *client = malloc(sizeof(struct client_session));
- if (!client) {
- syslog(LOG_ERR, "Failed to allocate client session");
- return;
- }
-
- client->wsi = wsi;
- client->next = server->clients;
- server->clients = client;
-
- syslog(LOG_INFO, "Client connected");
-}
-
-/* Remove client */
-void client_remove(struct mpd_ws_server *server, struct lws *wsi) {
- struct client_session **current = &server->clients;
-
- while (*current) {
- if ((*current)->wsi == wsi) {
- struct client_session *to_remove = *current;
- *current = (*current)->next;
- free(to_remove);
- syslog(LOG_INFO, "Client disconnected");
- return;
- }
- current = &(*current)->next;
- }
-}
-
-/* Broadcast message to all clients */
-void client_broadcast(struct mpd_ws_server *server, const char *message) {
- struct client_session *client = server->clients;
- size_t len = strlen(message);
-
- while (client) {
- unsigned char buf[LWS_PRE + MAX_MESSAGE_SIZE];
- memcpy(&buf[LWS_PRE], message, len);
-
- if (lws_write(client->wsi, &buf[LWS_PRE], len, LWS_WRITE_TEXT) < 0) {
- syslog(LOG_WARNING, "Failed to write to websocket");
- }
- client = client->next;
- }
-}
diff --git a/mpdws.c b/mpdws.c
new file mode 100644
index 0000000..8e488e7
--- /dev/null
+++ b/mpdws.c
@@ -0,0 +1,244 @@
+#include "mpdws.h"
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+static struct mpd_ws_server *g_server = NULL;
+
+static void signal_handler(int sig) {
+ (void)sig;
+ if (g_server)
+ g_server->running = 0;
+}
+
+static void broadcast_current_song(struct mpd_ws_server *server) {
+ struct client_session *client = server->clients;
+ size_t len = strlen(server->current_song);
+
+ while (client) {
+ unsigned char buf[LWS_PRE + MAX_MESSAGE_SIZE];
+ memcpy(&buf[LWS_PRE], server->current_song, len);
+ lws_write(client->wsi, &buf[LWS_PRE], len, LWS_WRITE_TEXT);
+ client = client->next;
+ }
+}
+
+static int ws_callback(struct lws *wsi, enum lws_callback_reasons reason,
+ void *user, void *in, size_t len) {
+ (void)user;
+ (void)in;
+ (void)len;
+
+ struct mpd_ws_server *server =
+ (struct mpd_ws_server *)lws_context_user(lws_get_context(wsi));
+
+ switch (reason) {
+ case LWS_CALLBACK_ESTABLISHED: {
+ struct client_session *client = malloc(sizeof(*client));
+ if (client) {
+ client->wsi = wsi;
+ client->next = server->clients;
+ server->clients = client;
+ syslog(LOG_INFO, "Client connected");
+ if (strlen(server->current_song) > 0) {
+ lws_callback_on_writable(wsi);
+ }
+ }
+ break;
+ }
+ case LWS_CALLBACK_CLOSED: {
+ struct client_session **current = &server->clients;
+ while (*current) {
+ if ((*current)->wsi == wsi) {
+ struct client_session *to_remove = *current;
+ *current = (*current)->next;
+ free(to_remove);
+ syslog(LOG_INFO, "Client disconnected");
+ break;
+ }
+ current = &(*current)->next;
+ }
+ break;
+ }
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ if (strlen(server->current_song) > 0) {
+ unsigned char buf[LWS_PRE + MAX_MESSAGE_SIZE];
+ size_t msg_len = strlen(server->current_song);
+ memcpy(&buf[LWS_PRE], server->current_song, msg_len);
+ lws_write(wsi, &buf[LWS_PRE], msg_len, LWS_WRITE_TEXT);
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct lws_protocols protocols[] = {
+ {"mpd-protocol", ws_callback, 0, MAX_MESSAGE_SIZE, 0, NULL, 0},
+ {NULL, NULL, 0, 0, 0, NULL, 0}};
+
+static int connect_mpd(struct mpd_ws_server *server) {
+ if (server->mpd_conn) {
+ if (server->mpd_idle_active) {
+ mpd_send_noidle(server->mpd_conn);
+ mpd_response_finish(server->mpd_conn);
+ }
+ mpd_connection_free(server->mpd_conn);
+ }
+
+ server->mpd_conn = mpd_connection_new(MPD_HOST, MPD_PORT, 0);
+ server->mpd_idle_active = 0;
+
+ if (mpd_connection_get_error(server->mpd_conn) != MPD_ERROR_SUCCESS) {
+ syslog(LOG_ERR, "MPD connection failed: %s",
+ mpd_connection_get_error_message(server->mpd_conn));
+ mpd_connection_free(server->mpd_conn);
+ server->mpd_conn = NULL;
+ return -1;
+ }
+
+ syslog(LOG_INFO, "Connected to MPD at %s:%d", MPD_HOST, MPD_PORT);
+ return 0;
+}
+
+static void update_current_song(struct mpd_ws_server *server) {
+ if (!server->mpd_conn ||
+ mpd_connection_get_error(server->mpd_conn) != MPD_ERROR_SUCCESS) {
+ return;
+ }
+
+ mpd_send_current_song(server->mpd_conn);
+ struct mpd_song *song = mpd_recv_song(server->mpd_conn);
+
+ if (mpd_connection_get_error(server->mpd_conn) != MPD_ERROR_SUCCESS) {
+ mpd_response_finish(server->mpd_conn);
+ return;
+ }
+
+ char new_song[MAX_MESSAGE_SIZE];
+ if (!song) {
+ snprintf(new_song, sizeof(new_song), "Now Playing: No song");
+ } else {
+ const char *artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0);
+ const char *title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
+
+ snprintf(new_song, sizeof(new_song), "Now Playing: %s - %s", artist, title);
+ mpd_song_free(song);
+ }
+
+ // Only send song if it's changed
+ if (strcmp(new_song, server->current_song) != 0) {
+ strlcpy(server->current_song, new_song, sizeof(server->current_song));
+ broadcast_current_song(server);
+ syslog(LOG_DEBUG, "Broadcasting: %s", server->current_song);
+ }
+
+ mpd_response_finish(server->mpd_conn);
+}
+
+int mpd_ws_init(struct mpd_ws_server *server) {
+ memset(server, 0, sizeof(*server));
+
+ g_server = server;
+ signal(SIGINT, signal_handler);
+ signal(SIGTERM, signal_handler);
+ openlog("mpd_ws", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ struct lws_context_creation_info info = {0};
+ info.port = WEBSOCKET_PORT;
+ info.protocols = protocols;
+ info.gid = info.uid = -1;
+ info.user = server;
+
+ server->ws_context = lws_create_context(&info);
+ if (!server->ws_context) {
+ syslog(LOG_ERR, "Failed to create WebSocket context");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "WebSocket server listening on port %d", WEBSOCKET_PORT);
+ connect_mpd(server);
+ server->running = 1;
+ return 0;
+}
+
+void mpd_ws_run(struct mpd_ws_server *server) {
+ time_t last_reconnect = 0;
+
+ while (server->running) {
+ lws_service(server->ws_context, 10);
+
+ // Handle MPD connection
+ if (!server->mpd_conn ||
+ mpd_connection_get_error(server->mpd_conn) != MPD_ERROR_SUCCESS) {
+ time_t now = time(NULL);
+ if (now - last_reconnect >= RECONNECT_INTERVAL_SEC) {
+ if (connect_mpd(server) == 0) {
+ update_current_song(server);
+ }
+ last_reconnect = now;
+ }
+ sleep(SELECT_TIMEOUT_SEC);
+ continue;
+ }
+
+ // Start idle if needed
+ if (!server->mpd_idle_active) {
+ if (mpd_send_idle_mask(server->mpd_conn, MPD_IDLE_PLAYER)) {
+ server->mpd_idle_active = 1;
+ } else {
+ connect_mpd(server);
+ continue;
+ }
+ }
+
+ // Check for MPD events
+ fd_set readfds;
+ int mpd_fd = mpd_connection_get_fd(server->mpd_conn);
+ struct timeval timeout = {0, SELECT_TIMEOUT_SEC};
+
+ FD_ZERO(&readfds);
+ FD_SET(mpd_fd, &readfds);
+
+ if (select(mpd_fd + 1, &readfds, NULL, NULL, &timeout) > 0 &&
+ FD_ISSET(mpd_fd, &readfds)) {
+
+ enum mpd_idle events = mpd_recv_idle(server->mpd_conn, false);
+ server->mpd_idle_active = 0;
+
+ if (mpd_connection_get_error(server->mpd_conn) == MPD_ERROR_SUCCESS) {
+ if (events & MPD_IDLE_PLAYER) {
+ update_current_song(server);
+ }
+ }
+ }
+ }
+}
+
+void mpd_ws_cleanup(struct mpd_ws_server *server) {
+ syslog(LOG_INFO, "Shutting down");
+
+ if (server->mpd_conn) {
+ if (server->mpd_idle_active) {
+ mpd_send_noidle(server->mpd_conn);
+ mpd_response_finish(server->mpd_conn);
+ }
+ mpd_connection_free(server->mpd_conn);
+ }
+
+ if (server->ws_context) {
+ lws_context_destroy(server->ws_context);
+ }
+
+ while (server->clients) {
+ struct client_session *next = server->clients->next;
+ free(server->clients);
+ server->clients = next;
+ }
+
+ closelog();
+}
diff --git a/mpd_ws.h b/mpdws.h
index 7802d0c..7802d0c 100644
--- a/mpd_ws.h
+++ b/mpdws.h