#!/usr/bin/perl
#
# avchd2srt  --  wrapper for the AVCHD2SRT-core program
#
# - calls the avchd2srt-core program, and post-processes the SRT output
# - corrects frame rate for the camcorder Panasonic HC-X929EG-K
# - translates date/time to German names and format
#
# For info and documentation on the excellent AVCHD2SRT by Henry Devettens:
#  http://avchd2srt.webs.com/
#  http://forum.videohelp.com/threads/316229-Export-AVCHD-frame-specific-metadata-to-subtitles
#
# Author:
#  Dr. Andy Spiegl (avchd2srt.andy@spiegl.de)
#
############################################
#
# History:
#
# v0.1  2014-05-14: first version
# v0.2  2014-05-16: preserve GPS info in SRT file
#
############################################

my $VERSION = "0.2";

############################################

use strict;
use warnings;

use Date::Manip;


############################################
# configurable VARIABLES
############################################

my $debug = 0;

my $PROG = "avchd2srt-core";
my $PROGPATH = `which $PROG`;
chomp $PROGPATH;
$PROGPATH = $ENV{'HOME'} . "/bin/avchd2srt-core"  unless $PROGPATH;
print "$PROG is here: $PROGPATH\n"  if $debug;


############################################
# some self detection
############################################
my $self = $0; $self =~ s|.*/||;


############################################
# Main Program
############################################

print "Starting $self (v$VERSION)\n"  if $debug > 2;

die "ERROR: no files to work on" unless @ARGV;

my $f;
my ($runcmd, $out);

while (@ARGV) {
  $f = shift @ARGV;

  if (not (-e $f))
  {
    print "WARNING: skipping $f, file does not exist.\n";
    next;
  }

  print "$PROG generating subtitles for: $f\n";

  $runcmd = $PROGPATH .' "'. $f .'"';
  print "Running: $runcmd \n"  if $debug;
  $out = `$runcmd 2>&1`;
  if ($?)
  {
    print $out;
    &done(50);
  }
  else
  {
    print $out if $debug;
  }

  &repairSRT($f);

  print "-"x75 , "\n";
}

&done(0);


####################################
# helper routines
####################################

# rewrite from:
#  1
#  00:00:00,000 --> 00:00:00,060
#  Sunday 08-Sep-2013 16:01:13
# to:
#  1
#  00:00:00,000 --> 00:00:01,000
#  Sonntag, 8.9.2013 16:01:13
sub repairSRT
{
  my ($file) = @_;

  my $srtfile;
  my ($count, $countFile, $timecode, $date, $nl, $gps);
  my $newSRT='';
  my ($day, $err);
  my ($recordtime, $zoneinfo);
  my ($timestamp, $runcmd, $out);

  $day = new Date::Manip::Date;

  $srtfile = $file;
  $srtfile =~ s/\.[^.]+?$/.srt/;
  print "srtfilename: $srtfile\n"  if $debug;

  if (not open(SRTFILE, "$srtfile"))
  {
    die "ERROR: can't read \"$srtfile\": $!";
  }

  print "Reading subtitle file ...\n";
  $zoneinfo='';
  $count=0;
  undef $timestamp;
  while (<SRTFILE>)
  {
    $count++;
    $countFile = $_;
    die "ERROR reading $srtfile" unless $count == $countFile;
    $timecode = <SRTFILE>;
    $date = <SRTFILE>;
    chomp $date;
    undef $gps;
    $nl = <SRTFILE>;
    if ($nl =~ /^GPS/)
    {
      $gps = $nl;
      $nl = <SRTFILE>;
    }
    die "ERROR reading $srtfile" unless $nl eq "\n";

#    $date = "Wednesday 04-Sep-2013 12:37:03 (+00:00)";
#    $date =~ s/\(\+00:00\)/UTC/;

    $day->config("Language","English","DateFormat","US");
#    $err = $day->parse($date);
    $err = $day->parse_format(".*? %d-%b-%Y %T( \\(%z\\))?", $date);
    if ($err)
    {
      print "ERROR: invalid (". $day->err() .") datetime format: \"$date\".\n";
      &done(101);
    }

    # save first date in the file as timestamp
    if (not $timestamp)
    {
      $timestamp = $day->printf("%Y-%m-%d %T");
    }

    $day->config("Language","German","DateFormat","non-US");

    # nice routine but don't do this (not only not necessary, but even wrong)
    if (1==0)
    {
      $err = $day->convert('Europe/Berlin');
      if ($err)
      {
        print "ERROR: cannot convert date to local time zone: .". $day->err() ."\n";
        &done(102);
      }
    }

    if ($day->printf("%z") ne "+0000")
    {
      $zoneinfo = $day->printf("Movie was recorded in this timezone: %z = %Z");
      $recordtime = $day->printf("%A, %d.%m.%Y %T (%Z)\n");
    }
    else
    {
      $recordtime = $day->printf("%A, %d.%m.%Y %T\n");
    }

    $newSRT .= $countFile;
    # not precise enough
#    $newSRT .= &seconds2timecode($count-1) .",000 --> ". &seconds2timecode($count) .",000\n";
    # better: doubling the value avchd2srt calculated
    $newSRT .= &doubleTimecode($timecode) . "\n";     # 50 instead of 10S !
    $newSRT .= $recordtime;
    $newSRT .= $gps  if $gps;
    $newSRT .= "\n";
  }
  close SRTFILE;

  print "\n======= INFO: $zoneinfo =======\n"  if $zoneinfo;

  # write changes back to file
  print "Writing corrected subtitle file: $srtfile\n";
  if (not open(SRTFILE, ">$srtfile"))
  {
    die "ERROR: can't write \"$srtfile\": $!";
  }
  print SRTFILE $newSRT;
  close SRTFILE;

  print "Setting timestamp of subtitle file: ". $timestamp ."\n";
  # touch -d "$day" $srtfile
  $runcmd = 'touch -d "'. $timestamp .'" '. $srtfile;
  print "Running: $runcmd \n"  if $debug;
  $out = `$runcmd 2>&1`;
  if ($?)
  {
    print "ERROR: couldn't touch $srtfile: $!";
    print $out;
    &done(60);
  }
  else
  {
    print $out if $debug;
  }

  return;
}


# convert from 100 fps to 50 fps
# from:
#  00:00:01,030 --> 00:00:01,510
# to:
#  00:00:02,060 --> 00:00:03,020
sub doubleTimecode
{
  my ($timecode) = @_;

  my ($h1,$m1,$s1);
  my ($h2,$m2,$s2);
  my $newTimecode;

  if ( not $timecode =~ /^(\d\d):(\d\d):(\d\d,\d\d\d) --> (\d\d):(\d\d):(\d\d,\d\d\d)$/ )
  {
    die "ERROR: bad timecode format \"$timecode\"\n";
  }
  ($h1,$m1,$s1, $h2,$m2,$s2) = ($1,$2,$3,$4,$5,$6);
  $s1 =~ s/,/./;
  $s2 =~ s/,/./;

  # double it
  $h1 *= 2;  $m1 *= 2;  $s1 *= 2;
  $h2 *= 2;  $m2 *= 2;  $s2 *= 2;

  # handle overflows
  if ($s1 >= 60)
  {
    $s1 -= 60;
    $m1++;
  }
  if ($m1 >= 60)
  {
    $m1 -= 60;
    $h1++;
  }

  if ($s2 >= 60)
  {
    $s2 -= 60;
    $m2++;
  }
  if ($m2 >= 60)
  {
    $m2 -= 60;
    $h2++;
  }

  $newTimecode = sprintf("%02d:%02d:%06.3f --> %02d:%02d:%06.3f", $h1,$m1,$s1, $h2,$m2,$s2);
  $newTimecode =~ s/\./,/g;

  return $newTimecode;
}

sub seconds2timecode
{
  my ($sec) = @_;

  # special case
  return "00:00:00"  if $sec == 0;

  my $tc = new Date::Manip::Date;
  $tc->parse_format("%s", $sec);
  $tc->convert('UTC');
  return $tc->printf("%T");
}

sub done
{
  my ($exitcode) = @_;
  print "$self (v$VERSION) ended successfully.\n"  if $exitcode == 0 and $debug > 2;
  exit $exitcode;
}
