scripts

misc scripts and tools
git clone git://git.2f30.org/scripts
Log | Files | Refs

mpdart.c (4818B)


      1 #include <sys/types.h>
      2 #include <sys/time.h>
      3 
      4 #include <dirent.h>
      5 #include <libgen.h>
      6 #include <poll.h>
      7 #include <regex.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <time.h>
     12 #include <unistd.h>
     13 
     14 #include <mpd/client.h>
     15 
     16 #ifdef DEBUG
     17 #define DPRINTF printf
     18 #define DPRINTF_D(t) printf(#t"=%d\n", t)
     19 #define DPRINTF_U(t) printf(#t"=%u\n", t)
     20 #define DPRINTF_S(t) printf(#t"=%s\n", t)
     21 #else
     22 #define DPRINTF
     23 #define DPRINTF_D(t)
     24 #define DPRINTF_U(t)
     25 #define DPRINTF_S(t)
     26 #endif
     27 
     28 char *musicdir = "/home/lostd/.mpd/music";
     29 char **imgs;
     30 unsigned int cur;
     31 char *defimg = "/home/lostd/.mpdart.png";
     32 unsigned int cyclms = 3000;
     33 unsigned int pollms = 3000;
     34 int change;
     35 
     36 char *
     37 xdirname(const char *path)
     38 {
     39 	char *p, *tmp;
     40 
     41 	tmp = strdup(path);
     42 	p = dirname(tmp);
     43 	p = strdup(p);
     44 	free(tmp);
     45 
     46 	return p;
     47 }
     48 
     49 int
     50 imgcmp(const void *va, const void *vb)
     51 {
     52 	const char *a, *b;
     53 	static regex_t regex;
     54 
     55 	a = *(const char **)va;
     56 	b = *(const char **)vb;
     57 	DPRINTF_S(a);
     58 	DPRINTF_S(b);
     59 
     60 	/* Front covers first */
     61 	regcomp(&regex, "cover|front", REG_NOSUB | REG_EXTENDED);
     62 	if (regexec(&regex, a, 0, NULL, 0) == 0)
     63 		return -1;
     64 	if (regexec(&regex, b, 0, NULL, 0) == 0)
     65 		return 1;
     66 
     67 	return strcmp(a, b);
     68 }
     69 
     70 void
     71 init_imgs_default(void)
     72 {
     73 	DPRINTF("default\n");
     74 
     75 	imgs = malloc(2 * sizeof(*imgs));
     76 	imgs[0] = strdup(defimg);
     77 	imgs[1] = NULL;
     78 	cur = 0;
     79 }
     80 
     81 void
     82 init_imgs_from_path(char *path)
     83 {
     84 	DIR *dirp;
     85 	struct dirent *dp;
     86 	unsigned int n = 0;
     87 	static regex_t regex;
     88 
     89 	DPRINTF("images\n");
     90 
     91 	regcomp(&regex, "\\.(jpg|png|gif)$", REG_NOSUB | REG_EXTENDED);
     92 
     93 	dirp = opendir(path);
     94 	if (dirp == NULL)
     95 		err(1, "opendir %s", path);
     96 
     97 	while ((dp = readdir(dirp)) != NULL) {
     98 		/* Skip self and parent */
     99 		if (strcmp(dp->d_name, ".") == 0
    100 		    || strcmp(dp->d_name, "..") == 0)
    101 			continue;
    102 		if (regexec(&regex, dp->d_name, 0, NULL, 0) != 0)
    103 			continue;
    104 		imgs = realloc(imgs, (n + 1) * sizeof(*imgs));
    105 		asprintf(&imgs[n], "%s/%s", path, dp->d_name);
    106 		n++;
    107 	}
    108 
    109 	qsort(imgs, n, sizeof(*imgs), imgcmp);
    110 
    111 	/* Terminate list or set default image if empty */
    112 	if (n > 0) {
    113 		imgs = realloc(imgs, (n + 1) * sizeof(*imgs));
    114 		imgs[n] = NULL;
    115 		cur = 0;
    116 	} else {
    117 		init_imgs_default();
    118 	}
    119 
    120 	closedir(dirp);
    121 }
    122 
    123 void
    124 free_imgs(void)
    125 {
    126 	unsigned int i;
    127 
    128 	for (i = 0; imgs[i] != NULL; i++)
    129 		free(imgs[i]);
    130 	free(imgs);
    131 	imgs = NULL;
    132 }
    133 
    134 void
    135 print_imgs(void)
    136 {
    137 	unsigned int i;
    138 	static char *oldimg = NULL;
    139 	static struct timespec tp, oldtp = { 0, 0 };
    140 	unsigned int delta;
    141 
    142 	clock_gettime(CLOCK_MONOTONIC, &tp);
    143 	delta = (tp.tv_sec * 1000 + tp.tv_nsec / 1000000)
    144 	    - (oldtp.tv_sec * 1000 + oldtp.tv_nsec / 1000000);
    145 
    146 	DPRINTF("delta=%u\n", delta);
    147 
    148 	if (delta < cyclms && !change) {
    149 		pollms = cyclms - delta;
    150 		return;
    151 	}
    152 
    153 	if (change) {
    154 		cur = 0;
    155 		change = 0;
    156 	}
    157 
    158 	oldtp = tp;
    159 
    160 	for (i = 0; imgs[i] != NULL; i++)
    161 		DPRINTF("%s\n", imgs[i]);
    162 	DPRINTF("i=%u cur=%u\n", i, cur);
    163 
    164 	if (oldimg == NULL || strcmp(imgs[cur], oldimg) != 0) {
    165 		printf("%s\n", imgs[cur]);
    166 		fflush(stdout);
    167 	}
    168 	free(oldimg);
    169 	oldimg = strdup(imgs[cur]);
    170 
    171 	cur++;
    172 	if (cur == i)
    173 		cur = 0;
    174 }
    175 
    176 void
    177 update_imgs(struct mpd_connection *conn)
    178 {
    179 	struct mpd_status *status;
    180 	struct mpd_song *song;
    181 	const char *name;
    182 	enum mpd_state state;
    183 	static char *path, *oldpath = NULL;
    184 	char *abspath;
    185 
    186 	DPRINTF("update\n");
    187 
    188 	/* State */
    189 	mpd_send_status(conn);
    190 	status = mpd_recv_status(conn);
    191 	state = mpd_status_get_state(status);
    192 	/* Not playing */
    193 	if (state != MPD_STATE_PLAY &&
    194 	    state != MPD_STATE_PAUSE) {
    195 		DPRINTF("state\n");
    196 		free_imgs();
    197 		init_imgs_default();
    198 		mpd_status_free(status);
    199 		free(oldpath);
    200 		oldpath = NULL;
    201 		change = 1;
    202 		return;
    203 	}
    204 	mpd_response_finish(conn);
    205 
    206 	/* Song */
    207 	mpd_send_current_song(conn);
    208 	song = mpd_recv_song(conn);
    209 	name = mpd_song_get_uri(song);
    210 	path = xdirname(name);
    211 	/* Album change */
    212 	if (oldpath == NULL || strcmp(path, oldpath) != 0) {
    213 		DPRINTF("song\n");
    214 		DPRINTF("path=%s oldpath=%s\n", path, oldpath);
    215 		asprintf(&abspath, "%s/%s", musicdir, path);
    216 		free_imgs();
    217 		init_imgs_from_path(abspath);
    218 		free(abspath);
    219 		change = 1;
    220 	}
    221 	free(oldpath);
    222 	oldpath = strdup(path);
    223 	mpd_song_free(song);
    224 	mpd_response_finish(conn);
    225 }
    226 
    227 int
    228 main(int argc, char *argv[])
    229 {
    230 	struct mpd_connection *conn;
    231 	struct pollfd fds[1];
    232 
    233 	conn = mpd_connection_new(NULL, 0, 0);
    234 	if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS)
    235 		err(1, "mpd_connection_new");
    236 
    237 	fds[0].fd = mpd_connection_get_fd(conn);
    238 	fds[0].events = POLLIN;
    239 
    240 	init_imgs_default();
    241 
    242 	for (;;) {
    243 		/* Update images if needed */
    244 		update_imgs(conn);
    245 
    246 		/* Print next image on change or timeout */
    247 		print_imgs();
    248 
    249 		/* Wait for a player change */
    250 		mpd_send_idle_mask(conn, MPD_IDLE_PLAYER);
    251 		poll(fds, 1, pollms);
    252 		mpd_send_noidle(conn);
    253 		mpd_response_finish(conn);
    254 
    255 		DPRINTF("poll\n");
    256 	}
    257 
    258 	mpd_connection_free(conn);
    259 
    260 	return 0;
    261 }