/* * Copyright (C) 2019 Anthony Chomienne, anthony@mob-dev.fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #define PROG_NAME "Split Audio File" #define VERSION "0.1" struct internal_data { char* input_file; char* output_dir; char* split_file_info; }; struct track_info { int num; char* title; char* start; char* end; }; struct album_info { int info_number; char* group; char* album; char* year; char* genre; int track_number; struct track_info* tracks; }; char* prog_name; void help() { fprintf(stderr, "---------------- "PROG_NAME" --------------------------------\n"); fprintf(stderr, "Usage: %s [options]\n" \ " Available options:\n" \ " \t -i | --input-file : Input File to split\n" \ " \t -o | --output-dir : Output Directory for split files\n" \ " \t -s | --split-file-info : Split File information\n" \ " \t -h | --help : Print this help\n" \ " \t -v | --version : Print programm version\n" \ " All other arguments are data for the command, handled depending on the command.\n", prog_name); fprintf(stderr, "-----------------------------------------------------------------------\n"); } void extract_trackinfo(FILE* split_file_info, struct track_info* info, int track_number) { int eof = 0; int parsed_tracks = 0; while(!eof && parsed_tracks < track_number) { char* line = NULL; size_t default_size = 0; int final_size = getline(&line,&default_size,split_file_info); if(final_size == -1) { eof = 1; continue; } char* split = strtok(line,";"); size_t success = sscanf(split,"%d",&info[parsed_tracks].num); if(!success) { fprintf(stderr,"File Malformed: Track Number Error"); } split = strtok(NULL,";"); info[parsed_tracks].title = strdup(split); split = strtok(NULL,";"); info[parsed_tracks].start = strdup(split); split = strtok(NULL,";"); info[parsed_tracks].end = strdup(split); parsed_tracks++; free(line); } } void extract_albuminfo(struct internal_data* glob, struct album_info* info) { FILE* split_file = fopen(glob->split_file_info,"r"); if(split_file == NULL) { fprintf(stderr,"%s doesn't exist\n", glob->split_file_info); exit(-1); } int found_tracks = 0; while(!found_tracks) { char* line = NULL; size_t default_size = 0; getline(&line,&default_size,split_file); char* category = strtok(line,";"); if(strcmp(category,"group") == 0) { category = strtok(NULL,";"); info->group = strdup(category); info->info_number++; } else if(strcmp(category,"album") == 0) { category = strtok(NULL,";"); info->album = strdup(category); info->info_number++; } else if(strcmp(category,"year") == 0) { category = strtok(NULL,";"); info->year = strdup(category); info->info_number++; } else if(strcmp(category,"genre") == 0) { category = strtok(NULL,";"); info->genre = strdup(category); info->info_number++; } else if(strcmp(category,"tracks") == 0) { category = strtok(NULL,";"); size_t success = sscanf(category,"%d",&info->track_number); if(!success) { fprintf(stderr,"File Malformed: Number of Tracks Error\n"); exit(-1); } found_tracks = 1; info->info_number+=4*info->track_number; } free(line); } info->tracks = malloc(info->track_number * sizeof(struct track_info)); extract_trackinfo(split_file,info->tracks, info->track_number); fclose(split_file); } void build_args(char* input_file, char* output_dir, struct album_info* info,int track_id, char*** args) { *args = malloc((15+2*info->info_number+1)*sizeof(char*)); (*args)[0]="ffmpeg"; (*args)[1]="-i"; (*args)[2]=input_file; (*args)[3]="-acodec"; (*args)[4]="copy"; int optionnal = 5; if(info->group != NULL) { (*args)[optionnal++]="-metadata"; (*args)[optionnal] = malloc(strlen("artist=")+strlen(info->group)+1); sprintf((*args)[optionnal++],"artist=%s",info->group); } if(info->album != NULL) { (*args)[optionnal++]="-metadata"; (*args)[optionnal] = malloc(strlen("album=")+strlen(info->album)+1); sprintf((*args)[optionnal++],"album=%s",info->album); } if(info->genre != NULL) { (*args)[optionnal++]="-metadata"; (*args)[optionnal] = malloc(strlen("genre=")+strlen(info->genre)+1); sprintf((*args)[optionnal++],"genre=%s",info->genre); } if(info->year != NULL) { (*args)[optionnal++]="-metadata"; (*args)[optionnal] = malloc(strlen("date=")+strlen(info->year)+1); sprintf((*args)[optionnal++],"date=%s",info->year); } (*args)[optionnal++]="-metadata"; (*args)[optionnal] = malloc(strlen("title=")+strlen(info->tracks[track_id].title)+1); sprintf((*args)[optionnal++],"title=%s",info->tracks[track_id].title); (*args)[optionnal++]="-metadata"; (*args)[optionnal] = malloc(strlen("tracknumber=")+2+1); sprintf((*args)[optionnal++],"tracknumber=%02d",info->tracks[track_id].num); (*args)[optionnal++]="-ss"; (*args)[optionnal] = malloc(8+1); sprintf((*args)[optionnal++],"00:%s",info->tracks[track_id].start); (*args)[optionnal++]="-to"; (*args)[optionnal] = malloc(8+1); sprintf((*args)[optionnal++],"00:%s",info->tracks[track_id].end); (*args)[optionnal] = malloc(strlen(output_dir)+1+2+3+strlen(info->tracks[track_id].title)+4+1); sprintf((*args)[optionnal++],"%s/%02d - %s.ogg",output_dir,info->tracks[track_id].num,info->tracks[track_id].title); (*args)[optionnal]=NULL; printf("%d\n",optionnal); } void split_into_files(struct internal_data* data, struct album_info* info) { for(int i=0; i < info->track_number; i++) { if(fork() == 0) { char** args = NULL; build_args(data->input_file, data->output_dir, info, i, &args); execvp("ffmpeg",args); fprintf(stderr,"Error starting ffmpeg\n"); exit(-1); } wait(NULL); } } void cleanup(struct album_info* info) { free(info->year); free(info->group); free(info->album); free(info->genre); for(int i=0; i < info->track_number; i++) { free(info->tracks[i].title); free(info->tracks[i].start); free(info->tracks[i].end); } free(info->tracks); } int main(int argc, char* argv[]) { prog_name = argv[0]; struct internal_data glob; memset(&glob,0,sizeof(struct internal_data)); while(1) { int option_index = 0; int c = 0; struct option long_options[] = { {"input-file", required_argument, 0, 'i'}, {"output-dir", required_argument, 0, 'o'}, {"split-file-info", required_argument, 0, 's'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "i:o:s:hv", long_options, &option_index); /* no more options to parse */ if (c == -1) break; switch (c) { /* i Input Audio File */ case 'i': glob.input_file = optarg; break; /* o, Output Directory*/ case 'o': glob.output_dir = optarg; break; /* s, Split Information File */ case 's': glob.split_file_info = optarg; break; /* v, version */ case 'v': printf("%s Version %s\n", PROG_NAME, VERSION); return 0; break; /* h, help */ case 'h': default: help(); return 0; } } if(glob.input_file == NULL || glob.output_dir == NULL || glob.split_file_info == NULL) { fprintf(stderr,"Error, need input file, output directory and split file informations\n"); help(argv[0]); } struct album_info album_info; memset(&album_info,0,sizeof(struct album_info)); extract_albuminfo(&glob,&album_info); split_into_files(&glob,&album_info); cleanup(&album_info); return 0; }