#!/usr/bin/env perl
# PODNAME: subsonic_sync_starred.pl
# ABSTRACT: Download and/or sync starred media to a target directory
use strict;
use warnings;

use CLI::Helpers qw(:output);
use Getopt::Long::Descriptive;
use List::Util qw( sum );
use Path::Tiny;
use Storable qw(dclone);
use WWW::Subsonic;

my ($opt,$usage) = describe_options('%c - %o <target_directory>',
    ["Subsonic API Details"],
    ['server|S=s',        "Subsonic Server name, default localhost", { default => 'localhost' } ],
    ['port|P:s',          "Subsonic Server port, default 4000", { default => "4000" }],
    ['username|user|u:s', "Subsonic Username, required." ],
    ['password-file|p:s', "File containing the password for the subsonic user, default: ~/.subsonic_password",
        { default => "$ENV{HOME}/.subsonic_password", callback => { 'must be a valid file' => sub { -f $_[0] } } }
    ],
    ["insecure|http",     "Use Insecure HTTP for communication"],
    [],
    ["Media Directories"],
    ["local-media-dir|local|l:s", "Local directory which might contain media files we can sync."],
    [],
    ["Starred Media Options"],
    ["all|A",     "Sync all starred media", { implies => [qw(artists albums songs)] }],
    ["artists|a", "Sync all songs from starred artists" ],
    ["albums|b",  "Sync all songs from starred albums" ],
    ["songs|s",   "Sync all starred songs" ],
    [],
    ['help', "Display this help", { shortcircuit => 1 }],
);
if( $opt->help ) {
    print $usage->text;
    exit 0;
}

# Grab Target Directory
my $target_directory = shift;
if( !defined $target_directory || !-d $target_directory ) {
    print $usage->text;
    output({stderr=>1,color=>'red',clear=>1},
        "Must specify a valid target directory that exists."
    );
    exit 1;
}

# Process the Password
my $password = path($opt->password_file)->slurp;
chomp($password);

# Build the API Object
my $subsonic = WWW::Subsonic->new(
    server   => $opt->server,
    port     => $opt->port,
    username => $opt->username,
    password => $password,
	protocol => $opt->insecure ? 'http' : 'https',
);

# This is where we'll store information
# about the files we've sync'd
my %SyncComplete = ();

# Start Wide, circle back
my $response = $subsonic->api_request('getStarred');
debug_var({data=>1}, $response);
if( exists $response->{starred} ) {
    my $starred = $response->{starred};
    debug("Processing starred items");
    processBranch($starred->{artist}) if exists $starred->{artist} && $opt->artists;
    processBranch($starred->{album})  if exists $starred->{album}  && $opt->albums;
    processBranch($starred->{song})   if exists $starred->{song}   && $opt->songs;
}
output({color=>'green'},
    sprintf "Sync complete with %d items, %d bytes.",
        keys %SyncComplete ? scalar(keys %SyncComplete) : 0,
        keys %SyncComplete ? sum(values %SyncComplete) : 0,
);

sub processBranch {
    my ($branch) = @_;

    foreach my $node (@{ $branch }) {
        verbose({level=>2,color=>'cyan'}, sprintf "Processing Branch: %s",
            join(' - ',
                map  { $node->{$_} }
                grep { exists $node->{$_} } qw( artist album title )
            ),
        );
        if( exists $node->{isDir} && $node->{isDir} eq 'true' ) {
            my $resp = $subsonic->api_request( getMusicDirectory => { id => $node->{id} } );
            processBranch( $resp->{directory}{child} ) if exists $resp->{directory}{child};
        }
        else {
            processLeaf($node);
        }
    }
}

sub processLeaf {
    my ($leaf) = @_;
    debug_var($leaf);

    if( $leaf->{path} ) {
        my $dst = path($target_directory)->child($leaf->{path});
        unless( $dst->exists ) {
            # Create the Directory
            $dst->parent->mkpath;
            if( $opt->local_media_dir ) {
                my $src = path($opt->local_media_dir)->child($leaf->{path});
                if( $src->exists ) {
                    # Copy the file
                    my $copied = $src->copy($dst->absolute->stringify);
                    # Record how much data we copied
                    my $bytes = $copied->stat->size;
                    verbose({color=>'green'},
                        sprintf "%s - %d bytes copied",
                            $copied->absolute->stringify,
                            $bytes,
                    );
                    $SyncComplete{$leaf->{path}} = $bytes;
                }
            }
            if( !exists $SyncComplete{$leaf->{path}} ) {
                my $data = $subsonic->api_request( download => { id => $leaf->{id} } );
                if( $data ) {
                    $dst->spew_raw($data);
                    my $bytes = length $data;
                    verbose({color=>'magenta'},
                        sprintf "%s - %d bytes downloaded",
                            $dst->absolute->stringify,
                            $bytes,
                    );
                    $SyncComplete{$leaf->{path}} = $bytes;
                }
            }
        }
        else {
            verbose({color=>'bright_black'}, $dst->absolute->stringify . " exists");
        }
    }
}

__END__

=pod

=encoding UTF-8

=head1 NAME

subsonic_sync_starred.pl - Download and/or sync starred media to a target directory

=head1 VERSION

version 0.002

=head1 AUTHOR

Brad Lhotsky <brad@divisionbyzero.net>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2017 by Brad Lhotsky.

This is free software, licensed under:

  The (three-clause) BSD License

=cut