Here is my modified version of commercial_cut.c
Regarding -k and -d, if you make the default behavior that if -k is not specificed, the file will be deleted (without checking with the cutlist), I'm fine with that.
Thanks for a great program !!!
Mudit
Code:
/* ===========================================================================
* = NAME
* commercial_cut.c
*
* = PURPOSE
* Slice commercials out of a MythTV MPEG2 recording file.
* The cuts are nowhere near perfect (i.e. frame accurate),
* but it seems to work well for Australian DVB recordings
* and has recently had code added for PVR-250/235 recordings
* (which also seems to work for files inserted
* into MythTV from DVDs via mythcommflag --rebuild).
*
* = USAGE
* commercial_cut -vvv /myth/tv/1005_20050614110000_20050614120000.nuv
* or
* mv /usr/local/bin/mythtranscode /usr/local/bin/mythtranscode.orig
* cp commercial_cut /usr/local/bin/mythtranscode
* MythTV->Watch Recordings->{select a recording}->Begin Transcoding
*
* = DESCRIPTION
* Gets the markups (frame number vs byte offset) and cutlist (frame ranges)
* from the MythTV database, makes a copy of the original file without the
* cutlist frames in it, adds a minute to the start time, and inserts it back
* into the database. If there is a cut in the first 25 frames, or if the -k
* flag is specified, the original file is kept, otherwise 'tis deleted.
* Running as transcode has its own problems - transcode (or other jobs)
* deletes entries from the recordedmarkup and recorded tables which
* correspond to the original recording. In this case, we move the file
* and table entries to a new start time (one minute before the original).
*
* = REVISION
* $Id: commercial_cut.c 1.5 2005/06/25 13:58:45 nigel Exp $
*
* = AUTHORS
* Greg Frost, Nigel Pearson
* ========================================================================= */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <mysql/mysql.h>
/* This program is normally silent unless an error occurs. */
/* To see what is happening, verbose output can be enabled. */
#define output stdout
int verbose = 0;
/* There are three levels of verbosity:
/* The first outputs a few progress type messages. */
/* The second is for SQL queries or some of the logical steps */
/* The third is for tedious stuff like each file block copied */
#define VERBOSE1(args...) if(verbose) fprintf(output,args)
#define VERBOSE2(args...) if(verbose>1) fprintf(output,args)
#define VERBOSE3(args...) if(verbose>2) fprintf(output,args)
time_t get_time_t_from_filename_time (char *filename_time);
typedef struct
{
int frame;
off_t offset;
int num_frames;
off_t size;
} frame_mapping_type;
int mapping_comparison (const void *mm1, const void *mm2)
{
frame_mapping_type *m1 = (frame_mapping_type *) mm1;
frame_mapping_type *m2 = (frame_mapping_type *) mm2;
if (m1->frame == m2->frame)
return 0;
if (m1->frame < m2->frame)
return -1;
else
return 1;
}
time_t get_time_t_from_database_time (char *database_time)
{
char time_string[100];
struct tm time_tm = {0};
time_t time_time_t;
strncpy (time_string, database_time, sizeof (time_string));
/* Parse the elements of the time string into a struct tm. */
if (sscanf (database_time, "%d-%d-%d%*c%d:%d:%d",
&time_tm.tm_year,
&time_tm.tm_mon,
&time_tm.tm_mday,
&time_tm.tm_hour,
&time_tm.tm_min,
&time_tm.tm_sec) != 6)
{
fprintf (output, "Error parsing database time '%s'.\n", database_time);
// exit (1);
return get_time_t_from_filename_time(database_time);
}
time_tm.tm_year -= 1900;
time_tm.tm_mon -= 1;
time_time_t = mktime (&time_tm);
VERBOSE1("database time %s is %s", database_time, ctime (&time_time_t));
return time_time_t;
}
time_t get_time_t_from_filename_time (char *filename_time)
{
struct tm time = {0};
if (sscanf(filename_time, "%4d%2d%2d%2d%2d%2d",
&time.tm_year, &time.tm_mon, &time.tm_mday,
&time.tm_hour, &time.tm_min, &time.tm_sec) != 6)
{
fprintf(output, "Error parsing filename time '%s'.\n",filename_time);
exit(0);
}
time.tm_year -= 1900;
time.tm_mon -= 1;
return mktime (&time);
}
char *time_t_to_date_string (time_t time_time_t)
{
char *date_string;
struct tm time_tm;
date_string = malloc (100);
time_tm = *gmtime (&time_time_t);
sprintf (date_string, "%04d%02d%02d%02d%02d%02d",
time_tm.tm_year + 1900,
time_tm.tm_mon + 1,
time_tm.tm_mday,
time_tm.tm_hour,
time_tm.tm_min,
time_tm.tm_sec);
return date_string;
// yeah its a memory leak, but its easy!
}
int main (int argc, char*argv[])
{
char input_file_full_path[200];
char output_file_full_path[200];
char input_file_name[200];
int count_loop = 0;
int chanid = 0;
int do_usleep = 0;
int always_delete = 0;
char start_time[200];
char end_time[200];
time_t start_time_t = 0;
time_t end_time_t;
time_t new_start_time_t;
time_t new_end_time_t;
FILE *input_file;
FILE *output_file;
off_t output_file_size;
off_t seek_to;
off_t end_of_data;
char buffer[65536];
off_t num_to_write;
int seek_arg;
int cut_start[2000];
int cut_end[2000];
int num_cuts = 0;
frame_mapping_type mapping[100000];
frame_mapping_type new_mapping[100000];
int num_mappings = 0;
fpos_t file_pos;
int new_map_index = 0;
char *title = NULL;
char *subtitle = NULL;
char *description = NULL;
int delete_original = 1;
int running_as_transcode = 0;
int experimental_pvr_x50_mode = 0;
int num_fields;
MYSQL mysql;
MYSQL_RES *res;
MYSQL_ROW row;
/* Make sure that mktime doesnt stuff up because of the timezone.*/
putenv ("TZ=");
fprintf (output, "Running Greg Frost's Crude Commercial Cutter\n");
while (1)
{
int optchar;
optchar = getopt (argc, argv, "TSDcspdlkvn");
if (optchar == -1)
break;
switch (optchar)
{
case 'T':
VERBOSE1("Title: %s\n", argv[optind]);
title = argv[optind++];
break;
case 'S':
VERBOSE1("Subtitle: %s\n", argv[optind]);
subtitle = argv[optind++];
break;
case 'D':
VERBOSE1("Description: %s\n", argv[optind]);
description = argv[optind++];
break;
case 'c':
VERBOSE1("Chanid: %s\n", argv[optind]);
chanid = atoi (argv[optind]);
optind++;
break;
case 's':
strncpy (start_time, argv[optind], sizeof (start_time));
optind++;
start_time_t = get_time_t_from_database_time (start_time);
VERBOSE1("start time is %s\n", ctime (&start_time_t));
break;
case 'p':
VERBOSE1("Ignoring profile '%s' specification.\n", argv[optind]);
optind++;
break;
case 'd':
// VERBOSE1("Ignoring -d (not really sure what it does).\n");
VERBOSE1("forcibly deleted the recording.\n");
always_delete = 1;
break;
case 'l':
VERBOSE1("Using cutlist (which will be used regardless).\n");
break;
case 'n':
VERBOSE1("Run commercial_cut nicely, usleep after writes\n");
do_usleep = 1;
break;
case 'k':
VERBOSE1("Keeping the original recording.\n");
delete_original = 0;
break;
case 'v':
if (verbose)
fprintf (output, "Verbosity increased.\n");
else
fprintf (output, "Verbose output enabled.\n");
++verbose;
break;
}
}
if (verbose > 1)
for (int i = 0; i < argc; i ++)
fprintf (output, "arg %d = '%s'\n", i, argv[i]);
/* Does the command name match transcode? */
if (strcmp(basename(argv[0]), "mythtranscode") == 0)
{
running_as_transcode = 1;
VERBOSE1("Pretending to be mythtranscode!\n");
}
// Check that there is either still an unparsed option (the file name)
// or else the start time and chanid have been specified explicitly.
if ((optind >= argc) && !(start_time_t && chanid))
{
fprintf (output,
"Usage:\n"
"\n"
"./commercial_cut [-T \"Title\"] [-S \"Subtitle\"] [-D \"Description\"]\n"
" [-c chanid -s starttime] [-k] [-v] [-n] [input.nuv]\n"
"\n"
"-k Keep the original file.\n"
"-n run nicely, usleep after few writes\n"
"-v Verbose. Print extra output about commercial cut's actions.\n"
"You must either pass a file that is a valid myth recording that can\n"
"be found in the mythconverg database, or a chanid and starttime of\n"
"such a recording (this is the format supplied to mythtranscode).\n");
exit (1);
}
mysql_init (&mysql);
if (!mysql_real_connect (&mysql, "localhost", "root", NULL,
"mythconverg", 0, NULL, 0))
{
fprintf (output, "Can't connect to mythconverg MYSQL database\n");
exit (1);
}
else
VERBOSE2("Connected to the database successfully!\n");
if (start_time_t && chanid)
{
/* Get the end time from the database by queryingthe recorded table. */
VERBOSE2("Query for end time\n");
//hack more to do.
sprintf (buffer, "SELECT endtime FROM recorded WHERE chanid = %d AND "
"starttime = '%s'", chanid, start_time);
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error querying database!\n");
exit (1);
}
else
VERBOSE2("Queried the database successfully!\n");
res = mysql_use_result (&mysql);
if (!res)
{
fprintf (output, "Error using result\n");
exit (1);
}
else
VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));
if (mysql_field_count (&mysql) != 1)
{
fprintf (output, "More than one recording returned???\n");
exit (1);
}
if (mysql_num_fields (res) != 1)
{
fprintf (output, "More than one endtime field returned??\n");
exit (1);
}
row = mysql_fetch_row (res);
if (!row)
{
fprintf (output, "Error getting endtime\n");
exit (1);
}
else
VERBOSE2("Endtime is:\n%s", row[0]);
end_time_t = get_time_t_from_database_time (row[0]);
mysql_free_result (res);
}
else
{
strcpy (input_file_full_path, argv[optind]);
struct stat fs;
if (stat(input_file_full_path, &fs) == -1)
{
VERBOSE1("File %s doesn't exist. Trying in /myth/tv\n", argv[optind]);
sprintf(input_file_full_path, "/myth/tv/%s", argv[optind]);
if (stat(input_file_full_path, &fs) == -1)
{
fprintf(output, "Error. File [/myth/tv/]%s doesn't exist.\n",
argv[optind]);
exit(-1);
}
}
/* Extract the channel id and the start and end time from the name of */
/* the recording nuv file. */
strncpy (input_file_name, basename (input_file_full_path),
sizeof (input_file_name));
if (sscanf (input_file_name,
"%d_%14s_%14s.nuv", &chanid, start_time, end_time) != 3)
{
fprintf (output, "Error getting channel-id_start-time_end-time\n");
fprintf (output, " from file name %s\n", input_file_name);
exit (1);
}
/* Parse the start and end times into a time_t */
start_time_t = get_time_t_from_filename_time(start_time);
end_time_t = get_time_t_from_filename_time(end_time);
}
VERBOSE1("The start time is %s", ctime (&start_time_t));
VERBOSE1("The end time is %s", ctime (&end_time_t));
/* Attempt to adjust a transcode entry in the jobqueue to indicate that */
/* The transcode is now running. */
sprintf (buffer,
"UPDATE jobqueue SET status=4 where chanid=%d and starttime=%s "
"and type=1",
chanid, time_t_to_date_string (start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error altering jobqueue status to running!\n");
exit (1);
}
else
VERBOSE1("set jobqueue status to running\n");
sprintf (input_file_full_path, "/myth/tv/%d_%s_%s.nuv", chanid,
time_t_to_date_string (start_time_t),
time_t_to_date_string (end_time_t));
sprintf (output_file_full_path, "/myth/tv/%d_%s_%s.nuv.tmp",
chanid,
time_t_to_date_string (start_time_t),
time_t_to_date_string (end_time_t));
VERBOSE1("Input File Full Path: %s\n"
"Output File Full Path: %s\n",
input_file_full_path, output_file_full_path);
sprintf (buffer, "SELECT cutlist FROM recorded WHERE chanid = %d AND "
"starttime = '%s'", chanid, start_time);
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error querying database!\n");
exit (1);
}
else
VERBOSE2("Queried the database successfully!\n");
res = mysql_use_result (&mysql);
if (!res)
{
fprintf (output, "Error using result\n");
exit (1);
}
else
VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));
if (mysql_field_count (&mysql) != 1)
{
fprintf (output, "More than one recording returned???\n");
exit (1);
}
if (mysql_num_fields (res) != 1)
{
fprintf (output, "More than one cutlist field returned??\n");
exit (1);
}
row = mysql_fetch_row (res);
if (!row)
{
fprintf (output, "Error getting cutlist\n");
exit (1);
}
else
VERBOSE1("Cutlist is:\n%s", row[0]);
/* Parse the cutlist to get the start and end sections. */
{
char *s = row[0];
char *line;
while (line = strtok (s, "\n"))
{
s = NULL;
if (sscanf (line, "%d - %d", &cut_start[num_cuts],
&cut_end[num_cuts]) != 2)
{
fprintf (output, "Error Parsing Cutlist\n");
exit (1);
}
num_cuts++;
}
}
/* If there is at least one cut, and the first cut is non-zero and */
/* less than 25 (i.e a delete after right near the start of the */
/* recording) then keep the original recording. */
if (!always_delete && (num_cuts > 0) && (cut_start[0] > 0) && (cut_start[0] < 25))
{
delete_original = 0;
cut_start[0] = 0;
}
if (delete_original)
VERBOSE1("Will delete the original recording\n");
else
VERBOSE1("Will keep the original recording\n");
if (verbose)
for (int cut = 0; cut < num_cuts; cut++)
fprintf(output, "Cutlist %d: %d - %d\n",
cut, cut_start[cut], cut_end[cut]);
mysql_free_result (res);
/* Now get the information from the recordedmarkup for the frame */
/* file offset mappings. */
sprintf (buffer, "SELECT mark,offset FROM recordedmarkup WHERE chanid = %d AND "
"starttime = '%s' AND type=9", chanid, start_time);
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error querying database!\n");
exit (1);
}
else
VERBOSE2("Queried the database successfully!\n");
res = mysql_use_result (&mysql);
if (!res)
{
fprintf (output, "Error using result\n");
exit (1);
}
else
VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));
row = mysql_fetch_row (res);
if (!row)
{
VERBOSE1("No type 9 entries in the database. Entering experimental PVR-x50 mode\n");
experimental_pvr_x50_mode = 1;
mysql_free_result (res);
sprintf (buffer, "SELECT mark,offset FROM recordedmarkup WHERE chanid = %d AND "
"starttime = '%s' AND type=6", chanid, start_time);
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error querying database!\n");
exit (1);
}
else
VERBOSE2("Queried the database successfully!\n");
res = mysql_use_result (&mysql);
if (!res)
{
fprintf (output, "Error using result\n");
exit (1);
}
else
VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));
row = mysql_fetch_row (res);
if (!row)
{
fprintf (output, "No rows in recordedmarkup of type 6 or type 9. I'm, going to have to give up!\n");
exit (1);
}
}
do
{
VERBOSE3("%s %s\n", row[0], row[1]);
if (sscanf (row[0], "%d", &mapping[num_mappings].frame) != 1)
{
fprintf (output, "error parsing frame from frame mapping %d\n",
num_mappings);
exit (1);
}
if (sscanf (row[1], "%lld", &mapping[num_mappings].offset) != 1)
{
fprintf (output, "error parsing offset from frame mapping %d\n",
num_mappings);
exit (1);
}
num_mappings++;
} while (row = mysql_fetch_row (res));
mysql_free_result (res);
if (experimental_pvr_x50_mode)
{
for (int map = 0; map < num_mappings; map++)
mapping[map].frame *= 12;
}
qsort (mapping, num_mappings, sizeof (mapping[0]), mapping_comparison);
/* Determine the number of frames and size of each mapping. */
for (int map = 0; map < num_mappings - 1; map++)
{
mapping[map].num_frames = mapping[map + 1].frame - mapping[map].frame,
mapping[map].size = mapping[map + 1].offset - mapping[map].offset;
}
/* Determine the size of the last mapping using the file size. */
mapping[num_mappings - 1].num_frames = 0;
{
struct stat file_stats;
int stat_return;
if (stat (input_file_full_path, &file_stats))
{
fprintf (output, "Cannot stat file %s\n", input_file_full_path);
exit (1);
}
mapping[num_mappings - 1].size = file_stats.st_size;
}
VERBOSE1("file size is %lld\n", mapping[num_mappings - 1].size);
mapping[num_mappings - 1].size =
mapping[num_mappings - 1].size - mapping[num_mappings - 1].offset;
if (verbose>2)
for (int map = 0; map < num_mappings; map++)
{
fprintf (output, "mapping %d: %d %lld size %d %lld\n",
map,
mapping[map].frame,
mapping[map].offset,
mapping[map].num_frames,
mapping[map].size);
}
/* Now, copy one file to the next whilst cutting out the commercials */
/* and producing a set of mappings. */
input_file = fopen (input_file_full_path, "r");
if (!input_file)
{
fprintf (output, "Error opening file %s\n", input_file_full_path);
exit (1);
}
output_file = fopen (output_file_full_path, "w");
if (!output_file)
{
fprintf (output, "Error opening output file %s\n", output_file_full_path);
exit (1);
}
/* The list of type 6 mappings I was given to examine had the */
/* offset for frame zero at 38. I don't know what these first */
/* 38 bytes are, but I'll copy them to the new file anyway. */
/* We need to read up to the first offset otherwise all of */
/* the seeks will be wrong! It should not do any harm to do */
/* this for DVB fince the first offset should be zero. */
{
int num_bytes_to_copy = mapping[0].offset;
count_loop = 0;
while (num_bytes_to_copy != 0)
{
int num_this_time;
if (num_bytes_to_copy < sizeof (buffer))
num_this_time = num_bytes_to_copy;
else
num_this_time = sizeof (buffer);
fread (buffer, num_this_time, 1, input_file);
fwrite (buffer, num_this_time, 1, output_file);
count_loop++;
if (do_usleep && count_loop % 5 == 0) usleep(1000);
num_bytes_to_copy -= num_this_time;
}
VERBOSE2("loops in first count = %d\n", count_loop);
}
{
int map_index = 0;
off_t num_bytes_to_copy;
count_loop = 0;
/* For each cut, copy up to the start of the cut and then seek past */
/* the cut portion */
for (int cut = 0; cut <= num_cuts; cut++)
{
num_bytes_to_copy = 0;
while (map_index < num_mappings &&
((cut == num_cuts) ||
((mapping[map_index].frame + mapping[map_index].num_frames) <
cut_start[cut])))
{
new_mapping[new_map_index].num_frames = mapping[map_index].num_frames;
new_mapping[new_map_index].size = mapping[map_index].size;
num_bytes_to_copy = mapping[map_index].size;
VERBOSE3("Copying %lld bytes from %lld\n",
num_bytes_to_copy, mapping[map_index].offset);
while (num_bytes_to_copy != 0)
{
int num_this_time;
if (num_bytes_to_copy < sizeof (buffer))
num_this_time = num_bytes_to_copy;
else
num_this_time = sizeof (buffer);
fread (buffer, num_this_time, 1, input_file);
fwrite (buffer, num_this_time, 1, output_file);
count_loop++;
if (do_usleep && count_loop % 5 == 0) usleep(1000);
num_bytes_to_copy -= num_this_time;
}
map_index++;
new_map_index++;
}
/* Now skip data until we have passed the cut point. */
while (map_index < num_mappings &&
((cut == num_cuts) || (mapping[map_index].frame <= cut_end[cut])))
{
/* Skip the data we do not wish to copy. */
VERBOSE3("skipping %d: %d frames %lld bytes",
map_index,
mapping[map_index].num_frames,
mapping[map_index].size);
VERBOSE3(" from frame %d offset %lld\n",
mapping[map_index].frame,
mapping[map_index].offset);
if (fseek (input_file, mapping[map_index].size, SEEK_CUR))
{
fprintf (output, "Error seeking input file ahead %lld\n",
mapping[map_index].size);
exit (1);
}
map_index++;
}
}
VERBOSE2("loops in second count = %d\n", count_loop);
}
fclose (input_file);
fclose (output_file);
VERBOSE2("closed input and output files\n");
/* Determine the size of the output file. */
{
struct stat file_stats;
if (stat (output_file_full_path, &file_stats))
{
fprintf (output, "Cannot stat file %s\n", output_file_full_path);
exit (1);
}
output_file_size = file_stats.st_size;
}
VERBOSE1("Output file size %lld\n", output_file_size);
/* Calculate a new start time so that we can enter the new */
/* mapping info into the database (without clashing with the */
/* existing cut info. */
new_start_time_t = start_time_t;
do
{
if (new_start_time_t != start_time_t)
VERBOSE1("start time %s already in database!???\n",
time_t_to_date_string (new_start_time_t));
new_start_time_t = new_start_time_t + 60;
sprintf (buffer,
"SELECT * FROM recorded WHERE chanid = %d AND starttime = '%s'",
chanid, time_t_to_date_string (new_start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)) != 0)
{
fprintf (output, "Error querying for existing entry\n");
exit (1);
}
res = mysql_use_result (&mysql);
row = mysql_fetch_row (res);
mysql_free_result (res);
if (!row)
break;
} while (1);
VERBOSE1("new start time %s", ctime (&new_start_time_t));
/* Now calculate the new recordedmarkup info. */
for (int map_index = 0; map_index < new_map_index; map_index++)
{
if (map_index == 0)
{
new_mapping[map_index].frame = 0;
new_mapping[map_index].offset = 0;
}
else
{
new_mapping[map_index].frame =
new_mapping[map_index - 1].frame +
new_mapping[map_index - 1].num_frames;
new_mapping[map_index].offset =
new_mapping[map_index - 1].offset +
new_mapping[map_index - 1].size;
}
}
if (experimental_pvr_x50_mode)
{
for (int map_index = 0; map_index < new_map_index; map_index++)
new_mapping[map_index].frame /= 12;
}
/* Now insert the new recordedmarkup information into the database. */
for (int map_index = 0; map_index < new_map_index; map_index++)
{
sprintf (buffer,
"INSERT IGNORE INTO recordedmarkup VALUES (%d,'%s',%d,'%lld',%d)",
chanid, time_t_to_date_string (new_start_time_t),
new_mapping[map_index].frame,
new_mapping[map_index].offset,
experimental_pvr_x50_mode ? 6 : 9);
VERBOSE3("new map %d: %s", map_index, buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error entering recordedmarkup into database!\n");
exit (1);
}
else
VERBOSE3("Entered recordedmarkup into db successfully!\n");
}
/* Create a new end time based on the number of frames in */
/* the cut recording. */
new_end_time_t =
new_start_time_t + new_mapping[new_map_index - 1].frame *
(experimental_pvr_x50_mode ? 12 : 1) / 25 / 60 * 60 + 60;
VERBOSE1("new end time %s", ctime (&new_end_time_t));
/* Get all of the info from the original recording so we can */
/* write a new entry to the database. */
sprintf (buffer, "SELECT * FROM recorded WHERE chanid = %d AND "
"starttime = '%s'", chanid, start_time);
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error querying database!\n");
exit (1);
}
else
{
VERBOSE2("Queried the database successfully!\n");
}
res = mysql_use_result (&mysql);
if (!res)
{
fprintf (output, "Error using result[A\n");
exit (1);
}
else
{
num_fields = mysql_field_count (&mysql);
VERBOSE2("Result with %d fields\n", num_fields);
}
if (num_fields != 25)
{
fprintf (output, "Expected 25 fields in recorded got %d\n", num_fields);
// 0.16 had 22 fields (up to originalairdate)
// 0.17 and 0.18 have 25 fields (up to deletepending)
fprintf (output, "DB newer than 0.18. This may cause problems?\n");
}
row = mysql_fetch_row (res);
{
char *escaped_title;
char *escaped_subtitle;
char *escaped_description;
if (!title)
title = row[3];
if (!subtitle)
subtitle = row[4];
if (!description)
description = row[5];
escaped_title = calloc (strlen (title) * 3, 1);
escaped_subtitle = calloc (strlen (subtitle) * 3, 1);
escaped_description = calloc (strlen (description) * 3, 1);
mysql_real_escape_string (
&mysql, escaped_title, title, strlen (title));
mysql_real_escape_string (
&mysql, escaped_subtitle, subtitle, strlen (subtitle));
mysql_real_escape_string (
&mysql, escaped_description, description, strlen (description));
sprintf (buffer,"INSERT IGNORE INTO recorded VALUES (%s,'%s','%s','%s','%s','%s',",
row[0], // chanid
time_t_to_date_string (new_start_time_t),
time_t_to_date_string (new_end_time_t),
escaped_title,
escaped_subtitle,
escaped_description);
sprintf (buffer+strlen(buffer), "'%s','%s',NULL,%s,'',%s,%s,'%s',",
row[6], // category
row[7], // hostname
row[9], // editing
row[11], // autoexpire
row[12], // commflagged
row[13]); // recgroup
if (!row[14])
row[14] = "0";
sprintf (buffer+strlen(buffer), "%d,'%s','%s',%s,%lld,%s,%s",
atoi(row[14]) + 1, // recordid
row[15], // seriesid
row[16], // programid
row[17], // lastmodified
output_file_size,
row[19], // stars
row[20]); // previouslyshown
for (int field = 21; field < num_fields; ++field)
{
VERBOSE2("Adding field %d - %s\n", field, row[field]);
sprintf (buffer+strlen(buffer), ",'%s'", row[field]);
}
strcat(buffer+strlen(buffer), ")");
}
mysql_free_result (res);
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error querying database!\n");
exit (1);
}
else
VERBOSE2("Queried the database successfully!\n");
/* Rename the temporary file so that it matches the new start and end times. */
{
char new_output_name[200];
sprintf (new_output_name, "/myth/tv/%d_%s_%s.nuv", chanid,
time_t_to_date_string (new_start_time_t),
time_t_to_date_string (new_end_time_t));
if (rename (output_file_full_path, new_output_name))
{
fprintf (output, "error renaming %s to %s\n",
output_file_full_path, new_output_name);
exit (1);
}
}
if (delete_original)
{
/* Remove the original recording from the database and the file */
VERBOSE1("deleting original\n");
if (unlink (input_file_full_path))
fprintf (output, "error deleting %s\n", input_file_full_path);
else
VERBOSE1("deleted %s\n", input_file_full_path);
sprintf (buffer,
"DELETE FROM recorded where chanid=%d and starttime=%s",
chanid, time_t_to_date_string (start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error deleting original recorded!\n");
exit (1);
}
else
VERBOSE2("Deleted original recorded successfully\n");
sprintf (buffer,
"DELETE FROM recordedmarkup where chanid=%d and starttime=%s",
chanid, time_t_to_date_string (start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error deleting original recordedmarkup!\n");
exit (1);
}
else
VERBOSE2("Deleted original recordedmarkup successfully\n");
}
if (running_as_transcode && !delete_original)
{
VERBOSE1("attempting to do what is needed to keep original\n");
/* Find a new unique time for the recording in the past. */
/* This needs to be done because for some reason, myth deletes the */
/* recordedmarkup and the recording when the transcode job completes. */
new_start_time_t = start_time_t;
do
{
if (new_start_time_t != start_time_t)
fprintf (output, "start time %s already in database!\n",
time_t_to_date_string (new_start_time_t));
new_start_time_t = new_start_time_t - 60;
sprintf (buffer,
"SELECT * FROM recorded WHERE chanid = %d AND starttime = '%s'",
chanid, time_t_to_date_string (new_start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)) != 0)
{
fprintf (output, "Error querying for existing entry\n");
exit (1);
}
res = mysql_use_result (&mysql);
row = mysql_fetch_row (res);
mysql_free_result (res);
if (!row)
break;
} while (1);
VERBOSE1("new start time %s", ctime (&new_start_time_t));
sprintf (output_file_full_path, "/myth/tv/%d_%s_%s.nuv",
chanid, time_t_to_date_string (new_start_time_t),
time_t_to_date_string (end_time_t));
if (rename (input_file_full_path, output_file_full_path))
{
fprintf (output, "error renaming %s to %s\n",
input_file_full_path, output_file_full_path);
exit (1);
}
else
VERBOSE1("renamed %s to %s\n",
input_file_full_path, output_file_full_path);
sprintf (
buffer,
"UPDATE recorded SET starttime=%s where chanid=%d and starttime=%s",
time_t_to_date_string (new_start_time_t),
chanid, time_t_to_date_string (start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error changing the recording start time!\n");
exit (1);
}
else
VERBOSE2("changed the recording start time.\n");
sprintf (
buffer,
"UPDATE recordedmarkup SET starttime=%s where chanid=%d and starttime=%s",
time_t_to_date_string (new_start_time_t),
chanid, time_t_to_date_string (start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error changing the recordedmarkup start time!\n");
exit (1);
}
else
VERBOSE2("changed the recordedmarkup start time.\n");
}
/* Update the jobqueue to indicate the job is now complete. */
sprintf (buffer,
"UPDATE jobqueue SET status=5 where chanid=%d and starttime=%s "
"and type=1",
chanid, time_t_to_date_string (start_time_t));
VERBOSE2("%s\n", buffer);
if (mysql_real_query (&mysql, buffer, strlen(buffer)))
{
fprintf (output, "Error setting jobqueue status to stopped!\n");
exit (1);
}
else
VERBOSE1("jobqueue set to stopped.\n");
exit (0);
}