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(®ex, "cover|front", REG_NOSUB | REG_EXTENDED); 62 if (regexec(®ex, a, 0, NULL, 0) == 0) 63 return -1; 64 if (regexec(®ex, 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(®ex, "\\.(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(®ex, 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 }