package Distribution::Manager;

use strict;

use lib '/app/tools/common/lib';
use Common::Assert;

use lib '/app/tools/distribution/lib';
use Distribution::DB::Item::DistributionJob;
use Distribution::DB::Item::ProductDistribution;
use Distribution::DB::Item::ServiceAccount;
use Distribution::DB::Item::GenreServiceMap;

use Distribution::Job::Parameters;

use Data::Dumper;
use Digest::MD5;
use File::Copy;
use File::Find;
use File::Path;
use Audio::FLAC;
use Template;

use constant PACKAGE_BASE  => 'g:/packages';
use constant XSLT_BASE     => 'c:/app/tools/distribution/xslt';
use constant TEMPLATE_BASE => 'c:/app/tools/distribution/templates';

#my $ENCODER_LAME = 'd:/lame/lame.exe';

my @countries =
  qw(AD AE AF AG AI AL AM AN AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CS CU CV CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER ES ET EU FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR ST SV SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS YE YT ZA ZM ZW);

sub new {
    my $class = shift;
    my %args  = @_;

    assert( $args{client_id} );
    assert( $args{service_id} );
    assert( $args{distribution_job_id} );

    my $self = {
        ClientID          => $args{client_id},
        ServiceID         => $args{service_id},
        DistributionJobID => $args{distribution_job_id}
    };

    bless $self, $class;

    $self->_init();

    return $self;
}

sub run {
    my $self = shift;

    unless ( $self->build_package() ) {

        # log failure
        print STDERR "build_package failed\n";
        return 0;
    }

    ## no albums to deliver, nothing else to do
    ## however, technically successful (could have been all denied albums, etc.)
    return 1 if ( scalar @{ $self->_complete_albums() } == 0 );

    unless ( $self->deliver_package() ) {

        # log failure
        print STDERR "deliver_package failed\n";
        return 0;
    }

    unless ( $self->finalize() ) {

        # log failure
        print STDERR "finalize failed\n";
        return 0;
    }

    return 1;
}

# these are essentially private - should only be called by the sub classes

sub dist_dir {
    my $self = shift;

    if (@_) {
        $self->{DistDirName} = shift;
    }

    return join( '/', $self->{TempDir}, $self->{DistDirName} );
}

sub build_package {
    my $self = shift;

    # to be overridden
    return 0;
}

# to be overridden if not using SFTP
sub deliver_package {
    my $self = shift;
    my %args = @_;

    $args{src} ||= $self->{DistDirName};
    $args{src} .= '/*' if $args{omit_base_dir};
    my $dont_retry = $args{dont_retry} || 0;

    my $sftpObj = Distribution::Delivery::SFTP->new(
        base_path    => $self->{TempDir},
        package_path => $args{src},
        account_name => $self->{AcctInfo}{sfxacct},
        remote_path  => join( '/', $self->{AcctInfo}{basepath}, $args{dest} ),
        dont_retry   => $dont_retry,
    );

    return $sftpObj->deliver();
}

sub finalize {
    my $self = shift;
    foreach my $product_distribution_id ( @{ $self->_complete_albums() } ) {
        Distribution::DB::Item::ProductDistribution->setDeliveredDate($product_distribution_id);
    }
    return 1;
}

sub albums {
    my $self = shift;
    return $self->{XMLObj}{album};
}

# die's on any mkdir errors
sub make_path {
    my $self = shift;
    my $path = shift;
    return if ( -d $path );

    my $dir = '';
    if ( -d $self->{TempDir} && $path =~ s/^$self->{TempDir}// ) {
        $dir = $self->{TempDir};
    } else {
        if ( $path =~ s/^([a-z]:)// ) {
            $dir = join( '/', $1, $dir );
        }
        $path =~ s/^\///;
    }

    foreach my $sub_dir ( split '/', $path ) {
        $dir .= '/' . $sub_dir;
        unless ( -d $dir ) {
            mkdir $dir or die "can't mkdir $dir: $!";
        }
    }
}

# is a given delivery name (directory name) in use for another process/client?
sub delivery_name_exists {
    my $self   = shift;
    my $search = shift;

    my $start_dir = PACKAGE_BASE . '/' . $self->{ServiceID};

    opendir( D1, $start_dir ) || die "can't opendir - $!";
    foreach my $cid ( readdir(D1) ) {
        opendir( D2, "$start_dir/$cid" ) || die "can't opendir - $!";
        foreach my $key ( readdir(D2) ) {
            opendir( D3, "$start_dir/$cid/$key" ) || die "can't opendir - $!";
            if ( grep /^$search$/, readdir(D3) ) {
                closedir(D3);
                closedir(D2);
                closedir(D1);

                return 1;
            }
            closedir(D3);
        }
        closedir(D2);
    }
    closedir(D1);

    return 0;
}

# private methods

sub _init {
    my $self = shift;

    unless ( Distribution::DB::Item::ProductDistribution->uniqueServiceID( $self->{DistributionJobID}, $self->{ServiceID} ) ) {
        die "$self->{ServiceID} doesn't match all items in the job\n";
    }

    $self->{Timestamp} = time();

    # get the job info
    my $djItem = Distribution::DB::Item::DistributionJob->LookupByJobID( $self->{DistributionJobID} );
    $self->{XMLRaw} = $djItem->xml_data;
    $self->{XMLObj} = Distribution::Job::Parameters->new( XML => $self->{XMLRaw} );

    #print Dumper($self->{XMLObj});

    # get the acct info
    $self->{AcctInfo} =
      Distribution::DB::Item::ServiceAccount->GetAccountInfo( service_id => $self->{ServiceID}, client_id => $self->{ClientID} );

    # setup the tmp dir for dist'ing
    $self->{TempDir} = $self->_create_tmp_dir();

    $self->{SuccessList} = [];

    $self->{TerritoriesDeniedMsg} =
      "Album cannot be delivered here because the album's territory restrictions conflict with retailer's supported sales area.";
}

# some_dir/[svc_id]/[cli_id]/[timestamp]_[process_id]
sub _create_tmp_dir {
    my $self = shift;

    my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime( $self->{Timestamp} );
    my $date_pid = sprintf( "%4d%02d%02d%02d%02d%02d_%d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $$ );
    my $tmp_dir = join( '/', PACKAGE_BASE, $self->{ServiceID}, $self->{ClientID}, $date_pid );

    die "$tmp_dir already exists" if ( -e $tmp_dir );    # houston, we have a problem!
    $self->make_path($tmp_dir);

    return $tmp_dir;
}

sub _windoze_path {
    my $self = shift;
    my $path = shift;

    # for now, always return f:
    # soon we'll need to use initial part of path to determine drive letter
    return 'f:' . $path;
}

sub _get_file_checksum {
    my $self = shift;
    my $file = shift;

    my $fh;
    open( $fh, $file ) or return undef;
    binmode $fh or return undef;

    my $md5 = Digest::MD5->new();
    eval { $md5->addfile(*$fh) } or return undef;
    my $digest = $md5->hexdigest;

    close($fh);

    return $digest;
}

sub _get_file_size {
    my $self = shift;
    my $file = shift;

    my $filesize = -s $file;
    return $filesize;
}

my $IMG_CONVERT = 'd:/imagemagick/convert <src> -resize <xy> -density <dpi> <dest>';

# calls an ImageMagick command line tool
sub _convert_image {
    my $self = shift;
    my %args = @_;

    my $cmd = $IMG_CONVERT;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    return system($cmd) ? 0 : 1;
}

my $DBPA      = 'd:/dBpoweramp/CoreConverter -noidtag -infile="<src>" -outfile="<dest>" -convert_to=';
my $DBPA_MP3  = $DBPA . '"mp3 (Lame)" -b <bit_rate>';
my $DBPA_MP3V = $DBPA . '"mp3 (Lame)" -V <var_rate>';
my $DBPA_WMA  = $DBPA . '"Windows Media Audio 10" -codec="Windows Media Audio 9.1" -settings="<bit_rate> kbps, <freq> kHz, <signal> CBR"';

sub _flac_to_wma {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );
    assert( $args{bit_rate} );
    assert( $args{freq} );
    assert( $args{signal} );

    my $temp = $self->{TempDir} . "/temp.flac";

    unless ( File::Copy::copy( $args{src}, $temp ) ) {
        print STDERR "flac copy failed for $args{src}";
        return 0;
    }

    $args{src} = $temp;
    $args{bit_rate} = ' ' . $args{bit_rate} if ( $args{bit_rate} < 100 );

    my $cmd = $DBPA_WMA;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    my $retval = system($cmd) ? 0 : 1;
    unlink $temp or die "can't unlink $temp: $!";
    return $retval;
}

sub _flac_to_mp3 {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );

    my $temp = $self->{TempDir} . "/temp.flac";

    unless ( File::Copy::copy( $args{src}, $temp ) ) {
        print STDERR "flac copy failed for $args{src}";
        return 0;
    }

    $args{src} = $temp;

    my $cmd;
    if ( $args{var_rate} ) {
        $cmd = $DBPA_MP3V;
    } else {
        assert( $args{bit_rate} );
        $cmd = $DBPA_MP3;
    }

    $cmd =~ s/<(\w+)>/$args{$1}/g;

    my $retval = system($cmd) ? 0 : 1;
    unlink $temp or die "can't unlink $temp: $!";
    return $retval;
}

sub _wav_to_mp3 {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );
    assert( $args{bit_rate} );

    my $cmd = $DBPA_MP3;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    return system($cmd) ? 0 : 1;
}

sub _wav_to_wma {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );
    assert( $args{bit_rate} );
    assert( $args{freq} );
    assert( $args{signal} );

    my $cmd = $DBPA_WMA;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    return system($cmd) ? 0 : 1;
}

my $SOX      = 'd:/sox/sox.exe';
my $SOX_CLIP = $SOX . ' <src> -t wav <dest> trim <start> <stop>';

sub _wav_to_wav_clip {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );
    assert( defined $args{start} );    ## usually 0
    assert( $args{stop} );

    my $cmd = $SOX_CLIP;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    return system($cmd) ? 0 : 1;
}

sub _flac_to_mp3_preview {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );
    assert( $args{length} );
    assert( $args{bit_rate} );

    my $temp_full    = $self->{TempDir} . "/temp.wav";
    my $temp_preview = $self->{TempDir} . "/temp_preview.wav";
    my $retval;

    ## decode to wav
    $retval = $self->_flac_to_wav( src => $args{src}, dest => $temp_full );
    return $retval unless ($retval);

    ## sox it
    $retval = $self->_wav_to_wav_clip( src => $temp_full, dest => $temp_preview, start => 0, stop => $args{length} );
    return $retval unless ($retval);

    ## mp3 it
    $retval = $self->_wav_to_mp3( src => $temp_preview, dest => $args{dest}, bit_rate => $args{bit_rate} );
    return $retval unless ($retval);

    unlink $temp_full    or die "can't unlink $temp_full: $!";
    unlink $temp_preview or die "can't unlink $temp_preview: $!";
    return 1;
}

sub _flac_to_wma_preview {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );
    assert( $args{length} );
    assert( $args{bit_rate} );
    assert( $args{freq} );
    assert( $args{signal} );

    my $temp_full    = $self->{TempDir} . "/temp.wav";
    my $temp_preview = $self->{TempDir} . "/temp_preview.wav";
    my $retval;

    ## decode to wav
    $retval = $self->_flac_to_wav( src => $args{src}, dest => $temp_full );
    return $retval unless ($retval);

    ## sox it
    $retval = $self->_wav_to_wav_clip( src => $temp_full, dest => $temp_preview, start => 0, stop => $args{length} );
    return $retval unless ($retval);

    ## wma it
    $retval = $self->_wav_to_wma(
        src      => $temp_preview,
        dest     => $args{dest},
        bit_rate => $args{bit_rate},
        freq     => $args{freq},
        signal   => $args{signal}
    );
    return $retval unless ($retval);

    unlink $temp_full    or die "can't unlink $temp_full: $!";
    unlink $temp_preview or die "can't unlink $temp_preview: $!";
    return 1;
}

my $DECODE_FLAC = 'd:/flac/flac -d -o <dest> <src>';
my $ENCODE_AAC  = 'd:/aac/aacPlusEnc -chmode stereo -silent -bsformat m4a -codec AAC -br <bit_rate> -if <src> -of <dest>';

sub _flac_to_aac {
    my $self = shift;
    my %args = @_;

    my $dest = $args{dest};                                # save for later
    my $temp = join( '/', $self->{TempDir}, 'tmp.wav' );
    $args{dest} = $temp;

    my $cmd = $DECODE_FLAC;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    return if ( system($cmd) );                            # success will return 0

    $args{src}  = $args{dest};                             # src is the tmp file just created
    $args{dest} = $dest;                                   # reset to orig
    $cmd        = $ENCODE_AAC;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    my $retval = system($cmd) ? 0 : 1;

    unlink($temp) or die "can't unlink $temp: $!";
    return $retval;
}

sub _flac_to_wav {
    my $self = shift;
    my %args = @_;

    assert( $args{src} );
    assert( $args{dest} );

    my $cmd = $DECODE_FLAC;
    $cmd =~ s/<(\w+)>/$args{$1}/g;

    return ( system($cmd) ) ? 0 : 1;    # success of $cmd will return 0
}

sub _get_track_seconds {
    my $self     = shift;
    my $file     = shift;
    my $flac     = Audio::FLAC->new($file);
    my $flacInfo = $flac->info();
    return 0 unless ($flacInfo);        ## TODO: should actually log error, no?
    my $totalSamples = $flacInfo->{TOTALSAMPLES};
    my $sampleRate   = $flacInfo->{SAMPLERATE};
    return int( $totalSamples / $sampleRate );
}

sub _seconds_to_iso8601 {
    my $self    = shift;
    my $seconds = shift;
    my $minutes = int( $seconds / 60 );
    $seconds = $seconds - ( $minutes * 60 );
    return "PT${minutes}M${seconds}S";
}

sub _translate_metadata {
    my $self = shift;
    my %args = @_;

    my $tempXML = join( '/', $self->{TempDir}, 'temp.xml' );
    my $xslPath = join( '/', XSLT_BASE,        $self->TRANSFORM() );

    open TEMPXML, '>', $tempXML or die "can't open $tempXML: $!";
    binmode( TEMPXML, ":utf8" );
    print TEMPXML $args{xmlSrc};
    close TEMPXML or warn "can't close $tempXML: $!";
    my $command = "java -jar d:/saxon/saxon9.jar -o $args{xmlDest} $tempXML $xslPath";

    #print STDERR "$command\n";
    my $result = `$command`;

    #print STDERR "$result\n";

    return 0 unless ( -s $args{xmlDest} );

    unlink $tempXML or warn "can't unlink $tempXML: $!";
    return 1;
}

sub _generate_metadata {
    my $self = shift;
    my %args = @_;

    assert( $args{dataSrc} );
    assert( $args{xmlDest} );
    my $ww_code = $args{wwCode} || "WW";

    my $ttPath = join( '/', TEMPLATE_BASE, $self->TEMPLATE() );

    my $tt = Template->new( { ABSOLUTE => 1, EVAL_PERL => 1 } );
    my $metadata = '';
    $tt->process( $ttPath, $self->_get_album_data_struct( origData => $args{dataSrc}, wwCode => $ww_code ), \$metadata );

    unless ($metadata) {
        print $tt->error;
        return 0;
    }

    open XMLOUT, ">" . $args{xmlDest} or die "can't open $args{xmlDest} for writing: $!";
    binmode( XMLOUT, ":utf8" );
    print XMLOUT $metadata;
    close XMLOUT or warn "can't close $args{xmlDest}: $!";

    return 1;
}

sub _get_service_genre {
    my $self = shift;
    my %args = @_;

    return "" unless ( $args{genre_id} );
    return Distribution::DB::Item::GenreServiceMap->GetNewGenreName( service_id => $self->{ServiceID}, genre_id => $args{genre_id} );
}

sub _get_album_data_struct {
    my $self = shift;
    my %args = @_;

    assert( $args{origData} );
    my $orig = $args{origData};
    assert( $args{wwCode} );
    my $ww_code = $args{wwCode};

    my $album;
    my %albumAttrs = $orig->getXMLParams();

    #print Dumper($orig);

    $album->{client_id}      = $self->{ClientID};
    $album->{upc}            = $orig->{'upc'};
    $album->{title}          = $orig->{'title'};
    $album->{artist}         = $orig->{'display-artist'};
    $album->{label}          = $orig->{'label'};
    $album->{release_date}   = $orig->{'release-date'};
    $album->{cline}          = $orig->{'cline'};
    $album->{pline}          = $orig->{'pline'};
    $album->{genre}          = $orig->{'genre'};
    $album->{image_filename} = $orig->{'image-filename'};
    $album->{image_checksum} = $orig->{'image-checksum'};
    $album->{advisory}       = $albumAttrs{'is-explicit'};
    $album->{explicit}       = ( $album->{advisory} eq "explicit" ) ? "true" : "false";
    $album->{image_filesize} = $orig->{'image-filesize'} if ( exists $orig->{'image-filesize'} );
    $album->{batch_name}     = $orig->{'batch-name'} if ( exists $orig->{'batch-name'} );

    ## ugly territories stuff
    my @territories;
    if ( exists $orig->{'territories'}->{allowed} ) {
        if ( ref( $orig->{'territories'}->{allowed} ) eq "ARRAY" ) {
            foreach my $territory ( @{ $orig->{'territories'}->{allowed} } ) {
                push @territories, $territory;
            }
        } else {
            my $territory = $orig->{'territories'}->{allowed};
            push @territories, $territory;
        }
    } elsif ( exists $orig->{'territories'}->{denied} ) {
        my %territories;
        foreach my $country (@countries) {
            $territories{$country} = 1;
        }
        if ( ref( $orig->{'territories'}->{denied} ) eq "ARRAY" ) {
            foreach my $territory ( @{ $orig->{'territories'}->{denied} } ) {
                delete( $territories{$territory} ) if ( exists $territories{$territory} );
            }
        } else {
            my $territory = $orig->{'territories'}->{denied};
            delete( $territories{$territory} ) if ( exists $territories{$territory} );
        }
        @territories = keys %territories;
    } else {
        ## worldwide
        push @territories, $ww_code;
    }
    $album->{territories} = \@territories;

    if ( exists $orig->{'vendor-id'} ) {
        $album->{vendor_id} = $orig->{'vendor-id'};
    }
    if ( exists $orig->{'catalog-number'} ) {
        $album->{catalog_number} = $orig->{'catalog-number'};
    }

    my @volumes;
    $album->{track_count}  = 0;
    $album->{volume_count} = 0;
    $album->{duration}     = 0;
    foreach my $vol_orig ( @{ $orig->{volume} } ) {
        my $volume;
        my @tracks;
        my %volAttrs = $vol_orig->getXMLParams();
        $volume->{sequence} = $volAttrs{'id'};
        $volume->{duration} = 0;
        foreach my $track_orig ( @{ $vol_orig->{track} } ) {
            my %trackAttrs = $track_orig->getXMLParams();
            my $track;
            $track->{title}          = $track_orig->{'title'};
            $track->{artist}         = $track_orig->{'display-artist'};
            $track->{isrc}           = $track_orig->{'isrc'};
            $track->{sequence}       = $track_orig->{'sequence'};
            $track->{length}         = $track_orig->{'length'};
            $track->{duration}       = $track_orig->{'duration'};
            $track->{audio_filename} = $track_orig->{'audio-filename'};
            $track->{audio_checksum} = $track_orig->{'audio-checksum'};
            $track->{audio_filesize} = $track_orig->{'audio-filesize'} if ( exists $track_orig->{'audio-filesize'} );
            $track->{advisory}       = $trackAttrs{'is-explicit'};
            $track->{explicit}       = ( $track->{advisory} eq "explicit" ) ? "true" : "false";
            $volume->{duration} += $track->{duration};
            $album->{duration}  += $track->{duration};
            $album->{track_count}++;
            push @tracks, $track;
        }
        $volume->{tracks} = \@tracks;
        $album->{volume_count}++;
        $volume->{length} = $self->_seconds_to_iso8601( $volume->{duration} );
        push @volumes, $volume;
    }
    $album->{length}  = $self->_seconds_to_iso8601( $album->{duration} );
    $album->{volumes} = \@volumes;

    #print "\n\n------------------------\n\n";
    #print Dumper($album);

    return $album;
}

sub _force_countries_allowed {
    my $self  = shift;
    my $album = shift;

    if ( exists $album->{'territories'}->{denied} ) {
        my %territories;
        foreach my $country (@countries) {
            $territories{$country} = 1;
        }
        if ( ref( $album->{'territories'}->{denied} ) eq "ARRAY" ) {
            foreach my $territory ( @{ $album->{'territories'}->{denied} } ) {
                delete( $territories{$territory} ) if ( exists $territories{$territory} );
            }
        } else {
            my $territory = $album->{'territories'}->{denied};
            delete( $territories{$territory} ) if ( exists $territories{$territory} );
        }
        delete $album->{'territories'}->{denied};
        @{ $album->{'territories'}->{allowed} } = keys %territories;
    }

    return 1;
}

sub _can_be_sold_in {
    my $self         = shift;
    my $album        = shift;
    my $country_list = shift;

    assert($album);
    assert($country_list);

    if ( exists $album->{'territories'}->{allowed} ) {
        if ( ref( $album->{'territories'}->{allowed} ) eq "ARRAY" ) {
            foreach my $t1 ( @{ $album->{'territories'}->{allowed} } ) {
                foreach my $t2 (@$country_list) {
                    if ( $t1 eq $t2 ) {
                        ## goes to at least one country that service is at
                        return 1;
                    }
                }
            }
        } else {
            my $t1 = $album->{'territories'}->{allowed};
            foreach my $t2 (@$country_list) {
                if ( $t1 eq $t2 ) {
                    return 1;
                }
            }
        }
        return 0;    ## found no countries in common
    } elsif ( exists $album->{'territories'}->{denied} ) {
        my $count = 0;
        if ( ref( $album->{'territories'}->{denied} ) eq "ARRAY" ) {
            foreach my $t1 ( @{ $album->{'territories'}->{denied} } ) {
                foreach my $t2 (@$country_list) {
                    if ( $t1 eq $t2 ) {
                        $count++;
                    }
                }
            }
        } else {
            my $t1 = $album->{'territories'}->{denied};
            foreach my $t2 (@$country_list) {
                if ( $t1 eq $t2 ) {
                    $count++;
                }
            }
        }
        ## release doesn't go to ANY countries that service is at
        return 0 if ( $count == scalar @$country_list );
    }

    return 1;
}

sub _complete_albums {
    my $self  = shift;
    my $album = shift;

    if ($album) {
        push( @{ $self->{SuccessList} }, $album->{'product-distribution-id'} );
    }

    return $self->{SuccessList};
}

sub _album_denied {
    my $self  = shift;
    my $album = shift;
    my $msg   = shift;

    assert( exists $album->{'product-distribution-id'} );
    assert($msg);

    Distribution::DB::Item::ProductDistribution->setDenied( $album->{'product-distribution-id'}, $msg );

    return 1;
}

sub _cleanup_dir {
    my $self = shift;
    my $dir  = shift;
    rmtree( $dir, { error => \my $err } );
    if ( ref $err eq 'ARRAY' && scalar @$err > 0 ) {
        die "couldn't clean up $dir: $!";    # more verbose info?
    }
    return 1;
}

1;
