Split-Audio-File/main.c

322 行
8.7 KiB
C

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#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;
}