disco-dl

Discography/album downloader
git clone git://git.wimdupont.com/disco-dl.git
Log | Files | Refs | LICENSE

disco-dl.c (9484B)


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