#!/usr/bin/perl

##################################################
#
# GiantDisc mp3/ogg Jukebox
# 
# © 2000, Rolf Brugger
#
##################################################


# synchronizes the content of two independent GiantDisc systems


use lib '/home/music/bin';
use gdgentools;
use gdparams;
use DBI;
use strict;
use FileHandle;	    # to set auto flush on stdin
#use Cwd;           # get current working dir

autoflush STDIN 1;

use Getopt::Long;


my ($help, $gencprl, $gencplr);
my ($lochost, $locdbh);
my ($remhost, $remdbh);
my ($dellocal, $delremote, $usessh, $pattern);
my $sizethres = 200;   # audio file size difference threshold (bytes)
my $test = 0;

#$lochost   = `hostname -i`;	# IP address of database server
#chop ($lochost);
$lochost   = "localhost";


$Getopt::Long::autoabbrev=1;
GetOptions(
	"remhost=s" => \$remhost,      # mandatory string (=)
	"gencprl" 	=> \$gencprl, 
	"gencplr" 	=> \$gencplr, 
	"dellocal" 	=> \$dellocal, 
	"delremote" => \$delremote, 
	"ssh" 	    => \$usessh, 
    
	"pattern:s" => \$pattern,      # pattern that matches begin (not substr) of cddbid	
	"test"  	=> \$test, 
	"help" 		=> \$help 
	);

if ($help || !($remhost)){
print <<EndOfTxt;
gdsync.pl: Synchronize the content of two independent GiantDisc 
           systems

Options

--remhost      IP adress or hostname of remote host to be synchronized

--gencprl      copy genre table from remote host to local host
--gencplr      copy genre table from local host to remote host

--dellocal     delete tracks including audio files on the local host
               for which no corresponding track on the remote host
               was found. 
--delremote    delete tracks including audio files on the REMOTE host
               for which no corresponding track on the local host
               was found. 
               BE CAREFUL with these options, because they irrevocably
               delete track items and audio files on the local/remote host!

--ssh          use secure shell commands. Uses ssh instead of rsh and 
               scp instead of rcp

--test         dont modify database and files, just show what would
               be done

--help

This script compares the content of two GiantDisc servers. It detects
the last modifications since the last synchronization and updates both
servers. At the end of the synchronization operation both servers
(almost: see below) have the same database content and the same 
audio files.

The comparison procedure between local and remote host is not fully
symmetrical. The local host loops through all his tracks and compares 
them with the corresponding remote tracks. This means that the local
host is not aware of new tracks that were added on the remote host.
If tracks were added on both hosts, then the 'gdsync.pl' must be
invoked twice on both servers.

IMPORTANT: Syncronizing with the wrong parameters can severely damage
your music collection. It is therefore strongly recommended to test
your call to gdsync.pl usin the option --test before you really to it.

The database content is directly compared over the network using the
DBI library. The local user must therefore have read and modification
access rights to the remote GiantDisc database.

You have to make sure, that the ssh/scp (or rsh/rcp) commands can be
executed without being prompted for a password. Refer to the GiantDisc
how to set up the public key (or rhosts) authentication scheme.
           
EndOfTxt
exit 0;
}



### main
### Init constants
my $dbh;
my $gdbase = gdparams::gdbase();	# music files base
my $freespace = 1000;		#mb of required free space to copy tracks

### Open database connection
$locdbh = DBI->connect("DBI:mysql:GiantDisc:$lochost", "music", undef)
    or die "unable to connect to local GiantDisc db";

$remdbh = DBI->connect("DBI:mysql:GiantDisc:$remhost", "music", undef)
    or die "unable to connect to remote GiantDisc db";

### use rsh or ssh?
my ($remshell, $remcopy);
if($usessh){
  $remshell = "ssh";
  $remcopy  = "scp";
}
else{
  $remshell = "rsh";
  $remcopy  = "rcp";
}

### Copy genres
if($gencprl){
  print "overwrite local genre list with remote list\n";
  if (!$test){
    copy_genres($remdbh, $locdbh);
  }
}
if($gencplr){
  print "overwrite remote genre list with local list\n";
  if (!$test){
    copy_genres($locdbh, $remdbh);
  }
}

### Compare local and remote genres
compare_genres($locdbh, $remdbh);

sync_albums($locdbh, $remdbh, $pattern);
sync_tracks($locdbh, $remdbh, $pattern);
sync_images();


#print("### close database connection\n");
$locdbh->disconnect;
$remdbh->disconnect;


if (!$test){
  print("\nrecalculate local and remote frequencies of genres and languages\n");
  system("killall -q gdrefresh.pl");		# recalculate table frequencies
  system("gdrefresh.pl --dbhost $lochost --trkgenre --albgenre");
  system("$remshell $remhost /home/music/bin/gdrefresh.pl "
        ."gdrefresh.pl --dbhost $remhost --trkgenre --albgenre");

#  system "gdrefresh.pl $lochost a";     # a: all
#  system "$remshell $remhost /home/music/bin/gdrefresh.pl $remhost a";

}

print("\n### local and remote hosts are synchronized\n");

exit;



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

###################
### sync albums ###
###################


sub sync_albums{ #
  my ($locdbh, $remdbh, $pattern) = @_;

  my ($localbh, $localb, $remalbh, $remalb);
  my ($remupdates, $locupdates) = (0, 0);
  my ($loccreated, $remcreated) = (0, 0);
  my ($locdeleted, $remdeleted) = (0, 0);

  #################################
  ### Get LOCAL album records
  if (length($pattern)>0){
    $localbh = $locdbh->prepare("SELECT * FROM album WHERE cddbid LIKE '".$pattern."%' ORDER BY cddbid");
  }
  else{
    $localbh = $locdbh->prepare("SELECT * FROM album ORDER BY cddbid");
  }
  my $nblocalbums = $localbh->execute;
  my @localbums = ();
  print("nb local albums = $nblocalbums \n");
  while($localb = $localbh->fetchrow_hashref){
    push @localbums, $localb;
  }
  $localbh->finish;

  #################################
  ### Get REMOTE album records
  if (length($pattern)>0){
    $remalbh = $remdbh->prepare("SELECT * FROM album WHERE cddbid LIKE '".$pattern."%' ORDER BY cddbid");
  }
  else{
    $remalbh = $remdbh->prepare("SELECT * FROM album ORDER BY cddbid");
  }
  my $nbremalbums = $remalbh->execute;
  my @remalbums = ();
  print("nb remote albums = $nbremalbums \n");
  while($remalb = $remalbh->fetchrow_hashref){
    push @remalbums, $remalb;
  }
  $remalbh->finish;



  #################################
  ### pairwise compare album records
  my $li=0;
  my $ri=0;
  while (($li < $nblocalbums) || ($ri < $nbremalbums)){
    #print "  comparing l: ".$localbums[$li]->{cddbid}." - r: ".$remalbums[$ri]->{cddbid}."\n";
    print " comparing l: ".$localbums[$li]->{cddbid}." - r: ".$remalbums[$ri]->{cddbid}."      \r";

    ####################
    ### local < remote ?
    if (     ($li < $nblocalbums)   # li valid
         && (($ri >=$nbremalbums)   # ri not valid or..
             || ($localbums[$li]->{cddbid} lt $remalbums[$ri]->{cddbid})
            )
        ){
      ### album exists locally but not remotely
      if ($dellocal){
      	print "delete album on local host:   ";
        delete_album_record($locdbh, $localbums[$li]);
        $locdeleted++;
      }
      else{
        print "create new album on remote host:   ";
        copy_album_record($localbums[$li], $remdbh, "");
        $remcreated++;
      }
      print $localbums[$li]->{artist}." / ".$localbums[$li]->{title}."\n";

      $li++;
    }
    ####################
    ### remote < local ?
    elsif (  ($ri < $nbremalbums)   # ri valid
         && (($li >=$nblocalbums)   # li not valid or..
             || ($remalbums[$ri]->{cddbid} lt $localbums[$li]->{cddbid})
            )
        ){
      ### album exists remotely but not locally
      if ($delremote){
      	print "delete album on remote host:   ";
        delete_album_record($remdbh, $remalbums[$li]);
        $remdeleted++;
      }
      else{
        print "create new album on local host:   ";
        copy_album_record($remalbums[$ri], $locdbh, "");
        $loccreated++;
      }
      print $remalbums[$ri]->{artist}." / ".$remalbums[$ri]->{title}."\n";

      $ri++;
    }
    ####################
    ### local = remote !
    else{
      ### album exists on both sides - compare them
      if(   ($localbums[$li]->{artist} ne $remalbums[$ri]->{artist})
         || ($localbums[$li]->{title}  ne $remalbums[$ri]->{title})
         || ($localbums[$li]->{cddbid} ne $remalbums[$ri]->{cddbid})
         || ($localbums[$li]->{coverimg} ne $remalbums[$ri]->{coverimg})
         || ($localbums[$li]->{covertxt} ne $remalbums[$ri]->{covertxt})
        ){
      	print "\n    album records for "
      	.$localbums[$li]->{artist}." / ".$localbums[$li]->{title}." differ: \n    copy ";
      	# which one is newer?
        if ($localbums[$li]->{modified} lt $remalbums[$ri]->{modified}){
          $locupdates++;
          print "remote -> local   \n";
          copy_album_record($remalbums[$ri], $locdbh, $localbums[$li]->{cddbid});
        }
        else{
          $remupdates++;
          print "local -> remote   \n";
          copy_album_record($localbums[$li], $remdbh, $remalbums[$ri]->{cddbid});
        }
      }
      else{
      	#print "album records dont differ\n";
      }

      $li++;
      $ri++;
    }    

  }

  ########################################################################
  ### print statistics
  print "\n\nStatistics (Album sync)\n----------\n";
  print "local albums updated:      $locupdates\n";
  print "local albums created:      $loccreated\n";
  print "local albums deleted:      $locdeleted\n";
  print "remote albums updated:     $remupdates\n";
  print "remote albums created:     $remcreated\n";
  print "remote albums deleted:     $remdeleted\n";
  print "\n\n";
}



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


sub delete_album_record{ 
  my ($dbh, $record) = @_;
  if (!$test){
    $dbh->do("DELETE FROM album WHERE cddbid='".$record->{cddbid}."'");
  }
}

sub copy_album_record{ 
  my ($srcrec, $targh, $targetid) = @_;

  # copies the album record 'srcrec' to target.
  # if 'targetid' is empty, a new record on the target host is created
  
  ### Copy source record to target
  my $copycmd="";
  
  $copycmd .= "artist=".  $targh->quote($srcrec->{artist}).",";
  $copycmd .= "title=".   $targh->quote($srcrec->{title}).",";
  $copycmd .= "coverimg=".$targh->quote($srcrec->{coverimg}).",";
  $copycmd .= "covertxt=".$targh->quote($srcrec->{covertxt}).",";
  $copycmd .= "modified=".$targh->quote($srcrec->{modified})." ";

  if (length($targetid)>0){
    $copycmd = "UPDATE album SET ".$copycmd
      ."  WHERE cddbid=".$targh->quote($srcrec->{cddbid});
  }
  else{
    $copycmd = "INSERT album SET ".$copycmd
            .", cddbid=".$targh->quote($srcrec->{cddbid})." ";
  }
  if (!$test){
    $targh->do($copycmd);
  }
}



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

###################
### sync tracks ###
###################

sub sync_tracks{ #
  my ($locdbh, $remdbh, $pattern) = @_;

  my ($loctrkh, $loctrk, $remtrkh, $remtrk);
  my ($remupdates, $locupdates) = (0, 0);
  my ($loccreated, $remcreated) = (0, 0);
  my ($locdeleted, $remdeleted) = (0, 0);

  #################################
  ### Get LOCAL track records
  print "getting local track records\n";
  if (length($pattern)>0){
    $loctrkh = $locdbh->prepare("SELECT * FROM tracks WHERE mp3file LIKE 'tr_x".$pattern."%' ORDER BY mp3file");
  }
  else{
    $loctrkh = $locdbh->prepare("SELECT * FROM tracks ORDER BY mp3file");
  }
  my $nbloctracks = $loctrkh->execute;
  my @loctracks = ();
  print("nb local tracks = $nbloctracks \n");
  while($loctrk = $loctrkh->fetchrow_hashref){
    push @loctracks, $loctrk;
  }
  $loctrkh->finish;

  print "getting local file informations\n";
  my %locflist = get_file_informations("$gdbase/bin/gdinfo.pl -l");


  #################################
  ### Get REMOTE track records
  print "getting remote track records\n";
  if (length($pattern)>0){
    $remtrkh = $remdbh->prepare("SELECT * FROM tracks WHERE mp3file LIKE 'tr_x".$pattern."%' ORDER BY mp3file");
  }
  else{
    $remtrkh = $remdbh->prepare("SELECT * FROM tracks ORDER BY mp3file");
  }
  my $nbremtracks = $remtrkh->execute;
  my @remtracks = ();
  print("nb remote tracks = $nbremtracks \n");
  while($remtrk = $remtrkh->fetchrow_hashref){
    push @remtracks, $remtrk;
  }
  $remtrkh->finish;

  print "getting remote file informations\n";
  my %remflist = get_file_informations("$remshell $remhost \"$gdbase/bin/gdinfo.pl -l\"");


  ##################################
  ### pairwise compare track records
  my $li=0;
  my $ri=0;
  while (($li < $nbloctracks) || ($ri < $nbremtracks)){
#    print "  comparing l: ".$loctracks[$li]->{mp3file}." - r: ".$remtracks[$ri]->{mp3file}."\n";
    print " comparing l: ".$loctracks[$li]->{mp3file}." - r: ".$remtracks[$ri]->{mp3file}."     \r";

    ####################
    ### local-id < remote-id ?
    if (     ($li < $nbloctracks)   # li valid
         && (($ri >=$nbremtracks)   # ri not valid or..
             || ($loctracks[$li]->{mp3file} lt $remtracks[$ri]->{mp3file})
            )
        ){
      ### track exists locally but not remotely
      if ($dellocal){
      	print "delete track on local host:   ".$loctracks[$li]->{artist}." / ".$loctracks[$li]->{title}."\n";
        delete_track_record($locdbh, $loctracks[$li]);
        delete_local_trackfile($loctracks[$li]);
        $locdeleted++;
      }
      else{
        print "create new track on remote host:   ".$loctracks[$li]->{artist}." / ".$loctracks[$li]->{title}."\n";
        copy_track_record($loctracks[$li], $remdbh, "");
        copy_trackfile_local2remote($remhost, $loctracks[$li]);
        $remcreated++;
      }

      $li++;
    }
    ####################
    ### remote-id < local-id ?
    elsif (  ($ri < $nbremtracks)   # ri valid
         && (($li >=$nbloctracks)   # li not valid or..
             || ($remtracks[$ri]->{mp3file} lt $loctracks[$li]->{mp3file})
            )
        ){
      ### track exists remotely but not locally
      if ($delremote){
      	print "delete track on remote host:   ".$remtracks[$ri]->{artist}." / ".$remtracks[$ri]->{title}."\n";
        delete_track_record($remdbh, $remtracks[$ri]);
        delete_remote_trackfile($remhost, $remtracks[$ri]);
        $remdeleted++;
      }
      else{
        print "create new track on local host:   ".$remtracks[$ri]->{artist}." / ".$remtracks[$ri]->{title}."\n";
        copy_track_record($remtracks[$ri], $locdbh, "");
        copy_trackfile_remote2local($remhost, $remtracks[$ri]);
        $loccreated++;
      }

      $ri++;
    }
    ####################
    ### local-id = remote-id !
    else{
      ### track exists on both sides - compare them
      if(!tracks_equal($loctracks[$li], $remtracks[$ri])){
      	print "\n    track records for "
      	.$loctracks[$li]->{artist}." / ".$loctracks[$li]->{title}." differ: \n    copy ";
      	# which one is newer?
        if ($loctracks[$li]->{modified} lt $remtracks[$ri]->{modified}){
          $locupdates++;
          print "record remote -> local   \n";
          copy_track_record($remtracks[$ri], $locdbh, $loctracks[$li]->{id});
        }
        else{
          $remupdates++;
          print "record local -> remote   \n";
          copy_track_record($loctracks[$li], $remdbh, $remtracks[$ri]->{id});
        }
      }
      else{
      	#print "track records dont differ\n";
      }

      ##################################################
      ### synchronize associated audio files
      if (    scalar($locflist{$loctracks[$li]->{mp3file}}) # local file exists
           && scalar($remflist{$remtracks[$ri]->{mp3file}}) # remote file exists
          ){
      	### both files exist
      	if (    abs( $locflist{$loctracks[$li]->{mp3file}}{fsize} 
      	           - $remflist{$remtracks[$ri]->{mp3file}}{fsize}) > $sizethres){
      	  ### they differ
print "      	  both files exist and they differ\n";
      	  if ($locflist{$loctracks[$li]->{mp3file}}{mtime} > $remflist{$remtracks[$ri]->{mp3file}}{mtime}) {
      	    ### the local file is newer
            copy_trackfile_local2remote($remhost, $loctracks[$li]);
          }
      	  else{ 
      	    ### the remote file is newer
            copy_trackfile_remote2local($remhost, $remtracks[$ri]);
      	  }
      	}
      }
      else{
        if (scalar($locflist{$loctracks[$li]->{mp3file}}) ){ # local file exists
          ### local file exists but not remote
print "          ### local file exists but not remote                    \n";
          copy_trackfile_local2remote($remhost, $loctracks[$li]);
        }
        else{
          if (scalar($remflist{$remtracks[$ri]->{mp3file}}) ){ # remote file exists
            ### remote file exists but not local
print "          ### remote file exists but not local                    \n";
            copy_trackfile_remote2local($remhost, $remtracks[$ri]);
          }
          else{
            if (!gdgentools::is_mp3stream($loctracks[$li]->{mp3file})){
              print ("\n\nWARNING: no associated audio file found for   ");
              print $loctracks[$li]->{artist}." / ".$loctracks[$li]->{title}." !\n"
            }
          }
        }
      }
      

      $li++;
      $ri++;
    }    
  }

  ########################################################################
  ### print statistics
  print "\n\nStatistics (Tracks sync)\n----------\n";
  print "local tracks updated:      $locupdates\n";
  print "local tracks created:      $loccreated\n";
  print "local tracks deleted:      $locdeleted\n";
  print "remote tracks updated:     $remupdates\n";
  print "remote tracks created:     $remcreated\n";
  print "remote tracks deleted:     $remdeleted\n";
  print "\n\n";
}




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

###########################
### sync booklet images ###
###########################

sub sync_images{ #

  my ($remupdates, $locupdates) = (0, 0);
  my ($loccreated, $remcreated) = (0, 0);
  my ($locdeleted, $remdeleted) = (0, 0);

  #################################
  ### Get LOCAL images
  print "getting local booklet image files\n";
  my @locflist = get_file_informations_ord("$gdbase/bin/gdinfo.pl -i");
  my $curfile;
  my $nblocimgs= scalar(@locflist);


  #################################
  ### Get REMOTE images
  print "getting remote booklet image files\n";
  my @remflist = get_file_informations_ord("$remshell $remhost \"$gdbase/bin/gdinfo.pl -i\"");
  my $nbremimgs= scalar(@remflist);


  ##################################
  ### pairwise compare image files
  my $li=0;
  my $ri=0;
  
  while (($li < $nblocimgs) || ($ri < $nbremimgs)){
    print " comparing l: ".$locflist[$li]->{fname}." - r: ".$remflist[$ri]->{fname}."                   \r";
    
    ####################
    ### local-img < remote-img ?
    if (     ($li < $nblocimgs)   # li valid
         && (($ri >=$nbremimgs)   # ri not valid or..
             || ($locflist[$li]->{fname} lt $remflist[$ri]->{fname})
            )
        ){
      ### image exists locally but not remotely
      if ($dellocal){
      	print "delete image on local host:   ".$locflist[$li]->{fname}."\n";
      	print "\nDELETE IMAGE NOT IMPLEMENTED YET\n";
#        delete_local_file($locflist[$li]->{fname});
        $locdeleted++;
      }
      else{
        print "copy image to remote host:   ".$locflist[$li]->{fname}."\n";
        copy_file_local2remote($remhost, $locflist[$li]->{fname});
        $remcreated++;
      }

      $li++;
    }
    ####################
    ### remote-img < local-img ?
    elsif (  ($ri < $nbremimgs)   # ri valid
         && (($li >=$nblocimgs)   # li not valid or..
             || ($remflist[$ri]->{fname} lt $locflist[$li]->{fname})
            )
        ){
      ### image exists remotely but not locally
      if ($delremote){
      	print "delete image on remote host:   ".$remflist[$ri]->{fname}."\n";
      	print "\nDELETE IMAGE NOT IMPLEMENTED YET\n";
#        delete_remote_file($remhost, $remflist[$ri]->{fname});
        $remdeleted++;
      }
      else{
        print "copy image to local host:   ".$remflist[$ri]->{fname}."\n";
        copy_file_remote2local($remhost, $remflist[$ri]->{fname});
        $loccreated++;
      }

      $ri++;
    }
    ####################
    ### local-img = remote-img !
    else{
      ### image exists on both sides - compare them
      if($locflist[$li]->{fsize} != $remflist[$ri]->{fsize}){
      	print "\n    images files have different size: \n    copy ";
      	# which one is newer?
        if ($locflist[$li]->{mtime} lt $remflist[$ri]->{mtime}){
          $locupdates++;
          print "remote -> local   \n";
          copy_file_remote2local($remhost, $remflist[$ri]->{fname});
        }
        else{
          $remupdates++;
          print "local -> remote   \n";
          copy_file_local2remote($remhost, $locflist[$li]->{fname});
        }
      }
      else{
      	#print "image files don't differ\n";
      }

      $li++;
      $ri++;
    }    

  }    


  ########################################################################
  ### print statistics
  print "\n\nStatistics (Cover imgage sync)\n----------\n";
  print "local images updated:      $locupdates\n";
  print "local images created:      $loccreated\n";
  print "local images deleted:      $locdeleted\n";
  print "remote images updated:     $remupdates\n";
  print "remote images created:     $remcreated\n";
  print "remote images deleted:     $remdeleted\n";
  print "\n\n";
}


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

sub copy_trackfile_local2remote{
### checks if the target has enough free disk space and in which directory
#   and copies the file. exits with an error message if not enough disk
#   space available
  my ($remhost, $loctrack) = @_;
  print "copy file to remote host: ".$loctrack->{mp3file}." (".$loctrack->{artist}."/".$loctrack->{title}.")\n" ;
  copy_file_local2remote ($remhost, $loctrack->{mp3file});
}

sub copy_file_local2remote{
### checks if the target has enough free disk space and in which directory
#   and copies the file. exits with an error message if not enough disk
#   space available
  my ($remhost, $locfile) = @_;

  my $remdestdir = `$remshell $remhost "$gdbase/bin/gdinfo.pl -d $freespace"`;
  chop($remdestdir);  # chop off trailing newline
  if (length($remdestdir)>2){
    if (length($locfile)>8 && !$test){
      #print "$remshell $remhost \"rm -f $gdbase/??/$locfile\"\n"; 
      #print "$remcopy $gdbase/??/$locfile \"$remhost:$remdestdir\"\n";
      system "$remshell $remhost \"rm -f $gdbase/??/$locfile\""; 
      system "$remcopy $gdbase/??/$locfile \"$remhost:$remdestdir\"";
    }
  }
  else{
    print "remote host: disc full. Not enough place to store $freespace Mbytes\n";
  }
}

########################################################################
sub copy_trackfile_remote2local{
### checks if the target has enough free disk space and in which directory
#   and copies the file. exits with an error message if not enough disk
#   space available
  my ($remhost, $remtrack) = @_;
  print "copy file to local host: ".$remtrack->{mp3file}." (".$remtrack->{artist}."/".$remtrack->{title}.")\n" ;
  copy_file_remote2local ($remhost, $remtrack->{mp3file});
}

sub copy_file_remote2local{
### checks if the target has enough free disk space and in which directory
#   and copies the file. exits with an error message if not enough disk
#   space available
  my ($remhost, $remfile) = @_;

  my $locdestdir = `$gdbase/bin/gdinfo.pl -d $freespace`;
  chop($locdestdir);  # chop off trailing newline
  if (length($locdestdir)>2){
    if (length($remfile)>8 && !$test){
      #print "rm -f $gdbase/??/".$remfile."\n"; 
      #print "$remcopy \"$remhost:$gdbase/??/$remfile\" $locdestdir\n";
      system "rm -f $gdbase/??/".$remfile; # del local first, to prevent from double copies
      system "$remcopy \"$remhost:$gdbase/??/$remfile\" $locdestdir";
    }
  }
  else{
    print "local host: disc full. Not enough place to store $freespace Mbytes\n";
  }
}

########################################################################
sub delete_local_trackfile{
  my ($loctrack) = @_;
  print "delete local file: ".$loctrack->{mp3file}." (".$loctrack->{artist}."/".$loctrack->{title}.")\n" ;

  if (length($loctrack->{mp3file})>8 && !$test){
    system "rm $gdbase/??/".$loctrack->{mp3file};
  }
}

########################################################################
sub delete_remote_trackfile{
  my ($remhost, $remtrack) = @_;
  print "delete remote file: ".$remtrack->{mp3file}." (".$remtrack->{artist}."/".$remtrack->{title}.")\n" ;

  if (length($remtrack->{mp3file})>8 && !$test){
#print "$remshell $remhost \"rm $gdbase/??/".$remtrack->{mp3file}."\"\n";
    system "$remshell $remhost \"rm $gdbase/??/".$remtrack->{mp3file}."\"";
  }
}




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


### Delete Track
sub delete_track_record{ 
  my ($dbh, $record) = @_;
  if (!$test){
    $dbh->do("DELETE FROM tracks WHERE mp3file='".$record->{mp3file}."'");
  }
}


### Copy Track
sub copy_track_record{ 
  my ($srcrec, $targh, $targetid) = @_;

  # copies the track record 'srcrec' to target.
  # if 'targetid' is empty, a new record on the target host is created
  
  ### Copy source record to target
  my $copycmd="";
  
  $copycmd .= "artist=".  $targh->quote($srcrec->{artist}).",";
  $copycmd .= "title=".   $targh->quote($srcrec->{title}).",";
  $copycmd .= "genre1=".  $targh->quote($srcrec->{genre1}).",";
  $copycmd .= "genre2=".  $targh->quote($srcrec->{genre2}).",";
  $copycmd .= "year=".                  $srcrec->{year}.",";
  $copycmd .= "lang=".    $targh->quote($srcrec->{lang}).",";
  $copycmd .= "type=".                  $srcrec->{type}.",";
  $copycmd .= "rating=".                $srcrec->{rating}.",";
  $copycmd .= "length=".                $srcrec->{length}.",";
  $copycmd .= "source=".                $srcrec->{source}.",";
  $copycmd .= "sourceid=".$targh->quote($srcrec->{sourceid}).",";
  $copycmd .= "tracknb=".               $srcrec->{tracknb}.",";
  $copycmd .= "mp3file=". $targh->quote($srcrec->{mp3file}).",";
  $copycmd .= "condition=".             $srcrec->{condition}.",";
  $copycmd .= "voladjust=".             $srcrec->{voladjust}.",";
  $copycmd .= "lengthfrm=".             $srcrec->{lengthfrm}.",";
  $copycmd .= "startfrm=".              $srcrec->{startfrm}.",";
  $copycmd .= "bpm=".                   $srcrec->{bpm}.",";
  $copycmd .= "lyrics=".  $targh->quote($srcrec->{lyrics}).",";
  $copycmd .= "bitrate=". $targh->quote($srcrec->{bitrate}).",";
  $copycmd .= "modified=".$targh->quote($srcrec->{modified}).", ";
  $copycmd .= "created=". $targh->quote($srcrec->{created})." ";

  if (length($targetid)>0){
    $copycmd = "UPDATE tracks SET ".$copycmd."  WHERE id=".$targetid;
  }
  else{
    $copycmd = "INSERT tracks SET ".$copycmd." ";
  }

  if (!$test){
    $targh->do($copycmd);
  }
}


### Compare Tracks
sub tracks_equal{ 
  my ($rec1, $rec2) = @_;

  return (
           ($rec1->{artist}     eq   $rec2->{artist})
        && ($rec1->{title}      eq   $rec2->{title})
        && ($rec1->{genre1}     eq   $rec2->{genre1})
        && ($rec1->{genre2}     eq   $rec2->{genre2})
        && ($rec1->{year}       eq   $rec2->{year})
        && ($rec1->{lang}       eq   $rec2->{lang})
        && ($rec1->{type}       eq   $rec2->{type})
        && ($rec1->{rating}     eq   $rec2->{rating})
        && ($rec1->{source}     eq   $rec2->{source})
        && ($rec1->{sourceid}   eq   $rec2->{sourceid})
        && ($rec1->{tracknb}    eq   $rec2->{tracknb})
        && ($rec1->{mp3file}    eq   $rec2->{mp3file})
        && ($rec1->{condition}  eq   $rec2->{condition})
        && ($rec1->{voladjust}  eq   $rec2->{voladjust})
        && ($rec1->{startfrm}   eq   $rec2->{startfrm})
        && ($rec1->{bpm}        eq   $rec2->{bpm})
        && ($rec1->{lyrics}     eq   $rec2->{lyrics})
        && ($rec1->{bitrate}    eq   $rec2->{bitrate})
          );
}


### Get file informations
sub get_file_informations()
# returns a hash of hashes
{
  my ($command) =  @_;
  my $line;
  my ($mtime, $fsize, $fname);
  my %flist;
  open (FILEINFO, $command." |");  # open pipe
  while ($line = <FILEINFO>){
    chop $line;
    ($mtime, $fsize, $fname) = split / /, $line;
    $flist{$fname}{"mtime"} = $mtime;
    $flist{$fname}{"fsize"} = $fsize;
  }
  close FILEINFO;
  return %flist;
}

sub get_file_informations_ord()
# returns an array of hashes (ordered)
# ie. $list[$i]{"fname"}, $list[$i]{"mtime"}, $list[$i]{"fsize"}, 
{
  my ($command) =  @_;
  my $line;
  my ($mtime, $fsize, $fname);
  my @flist;
  my $i=0;
  open (FILEINFO, $command." |");  # open pipe
  while ($line = <FILEINFO>){
    chop $line;
    ($mtime, $fsize, $fname) = split / /, $line;
    $flist[$i]->{fname} = $fname;
    $flist[$i]->{fsize} = $fsize;
    $flist[$i]->{mtime} = $mtime;
    $i++;
  }
  close FILEINFO;

  return sort { $a->{fname} cmp $b->{fname} } @flist;
}

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

sub genre_to_file{
  my ($dbhandle, $filename) = @_;
  my ($genh, $genrec);
  $genh = $dbhandle->prepare("SELECT * FROM genre ORDER BY genre");
  $genh->execute;

  open OUTFILE, ">".$filename;  
  while($genrec = $genh->fetchrow_hashref){
    print OUTFILE $genrec->{id}."\t".$genrec->{genre}."\n";
  }  
  close OUTFILE;
  $genh->finish;
}

sub compare_genres{
  my ($locdbh, $remdbh) = @_;

  my $locfn = "$gdbase/tmp/genlocal.txt";
  my $remfn = "$gdbase/tmp/genremote.txt";
  print "compare genre tables of local and remote host\n";
  genre_to_file ($locdbh, $locfn);
  print "... and remote host\n";
  genre_to_file ($remdbh, $remfn);
  my $diffres = `diff $locfn $remfn`;
  if (length($diffres)>0){
    print $diffres;
    print "\n\nThe genre tables differ!\n";
    print "Before synchronizing the tracks, the local and remote genre tables\n";
    print "have to equalized.\n\n";
    print "Use the options --gencprl to copy the remote genres to the local\n";
    print "host (--gencplr to copy local genres to remote host).\n";
    exit;
  }
  else{
    print "The local and remote genre tables are equal\n\n";
  }
}

sub copy_genres{ # Parameters: source_dbh, target_dbh
  my ($source_dbh, $target_dbh) = @_;
  
  ### Delete genres of target host
  $target_dbh->do("DELETE FROM genre");
  
  ### Copy source record to target
  my ($genh, $genrec);
  $genh = $source_dbh->prepare("SELECT * FROM genre ORDER BY genre");
  $genh->execute;

  print "COPY:\n ";
  my $quotedgenre;
  while($genrec = $genh->fetchrow_hashref){
    $quotedgenre = $target_dbh->quote($genrec->{genre});
    print $quotedgenre."  ";
    $target_dbh->do("INSERT INTO genre SET "
      ."id='".   $genrec->{id}."', "
      ."genre=".$quotedgenre." ");
    if(length($genrec->{id3genre})>0){ # id3genre is not NULL?
      $target_dbh->do("UPDATE genre "
        ."SET id3genre=".$genrec->{id3genre}." "
        ."WHERE id='".$genrec->{id}."'");
    }    
  }  
  print "\n\n";
  $genh->finish;
}

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




