disco-dl.c (8663B)
1 #include <stdarg.h> 2 #include <unistd.h> 3 #include <errno.h> 4 #include <stdlib.h> 5 #include <sys/stat.h> 6 #include <stdio.h> 7 #include <string.h> 8 #include <libgen.h> 9 #include <fts.h> 10 11 #include "config.h" 12 #include "disco-dl.h" 13 14 int 15 main(void) 16 { 17 int line_count = 0; 18 Album **albums; 19 20 albums = get_albums(&line_count); 21 22 for (int i = 0; i < line_count; i++) { 23 dl_album(albums[i]); 24 tag_album(albums[i]); 25 26 free(albums[i]->band); 27 free(albums[i]->album); 28 free(albums[i]->genre); 29 free(albums[i]->url); 30 free(albums[i]->tracklist); 31 free(albums[i]->dir); 32 free(albums[i]); 33 } 34 free(albums); 35 36 return EXIT_SUCCESS; 37 } 38 39 void 40 dl_album(Album *album) 41 { 42 char *genredir = make_message("%s%s%s", ROOT_DIR, DIR_SEP, album->genre); 43 char *banddir = make_message("%s%s%s", genredir, DIR_SEP, album->band); 44 char *albumdir = make_message("%s%s%s", banddir, DIR_SEP, album->album); 45 char *sys_command; 46 47 make_dir(ROOT_DIR); 48 make_dir(genredir); 49 make_dir(banddir); 50 int status = make_dir(albumdir); 51 if (status == 1) 52 fprintf(stdout, "Pathname already exists: %s\n", albumdir); 53 status = chdir(albumdir); 54 if (status != 0) 55 exit(status); 56 57 album->dir = albumdir; 58 59 60 sys_command = make_message("yt-dlp -x -f bestaudio -i -o \"%s/%(playlist_index)s - %(title)s.%(ext)s\" \"%s\"", albumdir, album->url); 61 system(sys_command); 62 63 free(sys_command); 64 free(genredir); 65 free(banddir); 66 } 67 68 void 69 tag_album(Album *album) 70 { 71 char *token; 72 char *tracklist; 73 unsigned int count = 0; 74 Track *track; 75 76 if (album->tracklist && *album->tracklist != '\0') { 77 tracklist = album->tracklist; 78 while ((token = strsep(&tracklist, ";")) != NULL) { 79 track = get_track(&album, &token, ++count); 80 if (track != NULL) { 81 if (track->title != NULL) { 82 printf("%s - %s\n", track->tracknum, track->title); 83 convert(track); 84 /* TODO: fix tagging implementation and remove from convert() 85 id3_tag(track); 86 */ 87 } 88 free(track->path); 89 free(track->tracknum); 90 free(track); 91 } 92 } 93 94 free(token); 95 free(tracklist); 96 } 97 } 98 99 Track * 100 get_track(Album **album, char **track_name, unsigned int count) 101 { 102 char *filename; 103 char *tracknumber; 104 FTS *ftsp; 105 FTSENT *p, *chp; 106 int fts_options = FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR; 107 char *pp[] = { (*album)->dir, NULL }; 108 Track *track = NULL; 109 char *substr; 110 111 112 if ((ftsp = fts_open(pp, fts_options, NULL)) == NULL) 113 fatal("Error during fts_open %s\n", pp); 114 115 chp = fts_children(ftsp, FTS_NAMEONLY); 116 117 if (chp == NULL) 118 fatal("Error during fts_children %s\n", pp); 119 120 while ((p = fts_read(ftsp)) != NULL) { 121 switch (p->fts_info) { 122 case FTS_F: 123 filename = basename(p->fts_path); 124 substr = strstr(filename, " "); 125 if (substr == NULL) 126 break; 127 tracknumber = calloc(3, sizeof(char)); 128 memcpy(tracknumber, filename, substr - filename); 129 if (atoi(tracknumber) == count) { 130 track = (Track*) malloc(sizeof(Track)); 131 if (track == NULL) 132 fatal("Fatal: failed to allocate bytes for track.\n"); 133 track->path = (char*) malloc(strlen(p->fts_path) + 1); 134 if (track->path == NULL) 135 fatal("Fatal: failed to allocate bytes for track->path.\n"); 136 track->title = *track_name; 137 track->tracknum = tracknumber; 138 track->album = *album; 139 strcpy(track->path, p->fts_path); 140 goto end; 141 } 142 free(tracknumber); 143 144 break; 145 default: 146 break; 147 } 148 } 149 end: 150 151 fts_close(ftsp); 152 153 return track; 154 } 155 156 void 157 convert(Track *track){ 158 char *sys_command; 159 char *path; 160 char *base; 161 162 base = strdup(track->path); 163 base = dirname(base); 164 165 path = make_message("%s%s%s - %s.mp3", base, DIR_SEP, track->tracknum, track->title); 166 sys_command = make_message("ffmpeg -loglevel error -i \"%s\" \"%s\"", track->path, path); 167 system(sys_command); 168 free(sys_command); 169 170 remove(track->path); 171 free(track->path); 172 track->path = strdup(path); 173 174 sys_command = make_message("id3 -t '%s' -a '%s' -l '%s' -y '%d' -n '%s' -g '%s' '%s'", 175 track->title, 176 track->album->band, 177 track->album->album, 178 track->album->year, 179 track->tracknum, 180 track->album->genre, 181 track->path); 182 system(sys_command); 183 184 free(path); 185 free(base); 186 free(sys_command); 187 } 188 189 Album ** 190 get_albums(int *line_count) 191 { 192 char *line_buf = NULL; 193 size_t line_buf_size = 0; 194 ssize_t line_size; 195 FILE *fp = fopen(FILENAME, "r"); 196 Album **albums = NULL; 197 198 if (!fp) 199 fatal("Error opening file '%s'\n", FILENAME); 200 201 line_size = getline(&line_buf, &line_buf_size, fp); 202 albums = (Album**) malloc(sizeof(Album)); 203 204 if (albums == NULL) 205 fatal("Fatal: failed to allocate bytes for albums.\n"); 206 207 if (line_size <= 0) 208 fatal("File '%s' is empty\n", FILENAME); 209 210 while (line_size >= 0) { 211 albums = (Album**) realloc(albums, (sizeof(Album) * (*line_count + 1))); 212 if (albums == NULL) 213 fatal("Fatal: failed to reallocate bytes for albums.\n"); 214 215 albums[*line_count] = get_album(line_buf, line_size); 216 217 line_size = getline(&line_buf, &line_buf_size, fp); 218 (*line_count)++; 219 } 220 221 free(line_buf); 222 fclose(fp); 223 224 return albums; 225 } 226 227 Album * 228 get_album(char *line_buf, ssize_t line_size) 229 { 230 Album *album = (Album*) malloc(sizeof(Album)); 231 if (album == NULL) 232 fatal("Fatal: failed to allocate bytes for album.\n"); 233 234 album->band = (char*) malloc(line_size); 235 album->album = (char*) malloc(line_size); 236 album->genre = (char*) malloc(line_size); 237 album->url = (char*) malloc(line_size); 238 album->tracklist = (char*) malloc(line_size); 239 240 if (album->band == NULL | album->album == NULL | album->genre == NULL | album->url == NULL | album->tracklist == NULL) 241 fatal("Fatal: failed to allocate bytes for album.\n"); 242 243 sscanf(line_buf, 244 "%[^|]|%[^|]|%[^|]|%d|%[^|]|%[^0]", 245 album->band, 246 album->album, 247 album->genre, 248 &album->year, 249 album->url, 250 album->tracklist); 251 252 return album; 253 } 254 255 int 256 make_dir(const char * name) 257 { 258 int status = EXIT_SUCCESS; 259 errno = 0; 260 int ret = mkdir(name, S_IRWXU); 261 if (ret == -1) { 262 switch (errno) { 263 case EACCES: 264 fatal("The parent directory does not allow write: %s\n", name); 265 case EEXIST: 266 status = 1; 267 break; 268 case ENAMETOOLONG: 269 fatal("Pathname is too long: %s\n", name); 270 default: 271 fatal("mkdir error: %s\n", name); 272 } 273 } 274 275 return status; 276 } 277 278 char * 279 concat(const char *s1, const char *s2) 280 { 281 char *result = malloc(strlen(s1) + strlen(s2) + 1); 282 if (result == NULL) 283 fatal("Fatal: failed to allocate bytes for concat.\n"); 284 285 strcpy(result, s1); 286 strcat(result, s2); 287 288 return result; 289 } 290 291 char * 292 make_message(const char *fmt, ...) 293 { 294 int n = 0; 295 size_t size = 0; 296 char *p = NULL; 297 va_list ap; 298 299 va_start(ap, fmt); 300 n = vsnprintf(p, size, fmt, ap); 301 va_end(ap); 302 303 if (n < 0) 304 return NULL; 305 306 size = (size_t) n + 1; 307 p = malloc(size); 308 if (p == NULL) 309 return NULL; 310 311 va_start(ap, fmt); 312 n = vsnprintf(p, size, fmt, ap); 313 va_end(ap); 314 315 if (n < 0) { 316 free(p); 317 return NULL; 318 } 319 320 return p; 321 } 322 323 void 324 id3_tag(Track *track) 325 { 326 char *tagfile = "taginf.txt"; 327 char yearstr[5]; 328 unsigned char pad[7] = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76 }; 329 330 int len = snprintf(yearstr, 5, "%d", track->album->year); 331 FILE* fp; 332 fp = fopen(tagfile, "wb"); 333 if (fp == NULL) 334 fatal("Failed to open file %s\n", tagfile); 335 336 fprintf(fp, "ID3"); 337 fwrite(pad, sizeof(pad), 1, fp); 338 339 tag("TRCK", track->tracknum, &fp); 340 tag("TIT2", track->title, &fp); 341 tag("TYER", yearstr, &fp); 342 tag("TPE1", track->album->band, &fp); 343 tag("TALB", track->album->album, &fp); 344 tag("TCON", track->album->genre, &fp); 345 346 fclose(fp); 347 merge_file(tagfile, track->path); 348 } 349 350 void tag 351 (char *tag, char *value, FILE **f1) 352 { 353 int size = 0; 354 unsigned char pad[3] = { 0x00, 0x00, 0x00 }; 355 356 fprintf(*f1, tag); 357 fwrite(pad, sizeof(pad), 1, *f1); 358 size = strlen(value) + 1; 359 fprintf(*f1, "%c", size); 360 fwrite(pad, sizeof(pad), 1, *f1); 361 fprintf(*f1, "%s", value); 362 } 363 364 void 365 merge_file(char *prefile, char *file) 366 { 367 FILE *f1, *f2, *f3; 368 char *tmp = "tmp.txt"; 369 int i = 0; 370 int ch; 371 f1 = fopen(prefile, "rb"); 372 f2 = fopen(file, "rb"); 373 f3 = fopen(tmp, "wb"); 374 375 if (f1 == NULL) 376 fatal("Failed to open file %s\n", prefile); 377 if (f2 == NULL) 378 fatal("Failed to open file %s\n", file); 379 if (f3 == NULL) 380 fatal("Failed to open file %s\n", tmp); 381 382 /* 383 while ((ch = fgetc(f2)) != EOF && ++i <= 34) 384 fputc(ch, f3); 385 386 while ((ch = fgetc(f1)) != EOF) 387 fputc(ch, f3); 388 389 i = 0; 390 while ((ch = fgetc(f2)) != EOF) { 391 if (++i > 34) 392 fputc(ch, f3); 393 } 394 */ 395 396 while ((ch = fgetc(f1)) != EOF) 397 fputc(ch, f3); 398 399 while ((ch = fgetc(f2)) != EOF) 400 fputc(ch, f3); 401 402 fclose(f1); 403 fclose(f2); 404 fclose(f3); 405 406 rename(tmp, file); 407 remove(prefile); 408 remove(tmp); 409 } 410 411 void 412 fatal(const char *fmt, ...) 413 { 414 va_list ap; 415 416 va_start(ap, fmt); 417 vfprintf(stderr, fmt, ap); 418 va_end(ap); 419 420 exit(EXIT_FAILURE); 421 } 422