disco-dl

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

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