211 lines
5.9 KiB
Perl
211 lines
5.9 KiB
Perl
use strict;
|
|
use warnings;
|
|
use Path::Tiny;
|
|
use JSON::XS; # JSON parser module
|
|
use DDP;
|
|
use v5.36;
|
|
use DateTime::Format::ISO8601;
|
|
use Try::Catch;
|
|
use Getopt::Long;
|
|
use Image::MetaData::JPEG;
|
|
use File::stat;
|
|
|
|
GetOptions(
|
|
"pull-versions" => \my $get_versions,
|
|
"restore" => \my $do_restores,
|
|
"verbose" => \my $verbose,
|
|
"print" => \my $print,
|
|
"copy" => \my $copy,
|
|
"overwrite" => \my $overwrite,
|
|
"folder=s" => \my $folder_name,
|
|
"update-ts" => \my $update_ts,
|
|
"since" => \my $since,
|
|
"match" => \my $match,
|
|
) or die("Error in command line arguments\n");
|
|
|
|
|
|
$match = '.*' unless $match;
|
|
my %restores;
|
|
|
|
my %stats = (
|
|
exists => 0,
|
|
restored => 0,
|
|
failed => 0,
|
|
selected => 0,
|
|
);
|
|
|
|
my %folders = (
|
|
'koku_downloads' => {
|
|
'id' => 'm7ko5-adoga',
|
|
'path' => '/mnt/nasty/koku_downloads',
|
|
},
|
|
'koku_documents' => {
|
|
'id' => 'krvxc-ajsfv',
|
|
'path' => '/mnt/nasty/Koku',
|
|
},
|
|
'koku_desktop' => {
|
|
'id' => 'vhimk-o3gwx',
|
|
'path' => '/mnt/nasty/koku_desktop',
|
|
},
|
|
'shared_pictures' => {
|
|
'id' => 'p6jqq-nouth',
|
|
'path' => '/mnt/nasty/Media/Pictures'
|
|
},
|
|
'koku_music' => {
|
|
'id' => 'mdhgr-gd7rv',
|
|
'path' => '/mnt/nasty/Media/Music/Natalia'
|
|
},
|
|
'project-conquer' => {
|
|
'id' => 'qypth-g6xkb',
|
|
'path' => '/mnt/nasty/project-conquer'
|
|
},
|
|
'highpoint' => {
|
|
'id' => 'jekig-3yzvw',
|
|
'path' => '/mnt/nasty/highpoint'
|
|
},
|
|
);
|
|
|
|
my $folder_id = $folders{$folder_name}{'id'};
|
|
my $target_dir = $folders{$folder_name}{'path'};
|
|
my $source_dir = $folders{$folder_name}{'path'} . "/.stversions";
|
|
my $version_data = "$folder_name.json";
|
|
|
|
sub get_versions() {
|
|
my $curl = qq{curl -k -X GET -H "X-API-KEY: oemTvSg94cShmEymeuct66GRsyutLTGg" https://dri.tz:8090/rest/folder/versions?folder=$folder_id > $folder_name.json};
|
|
system($curl);
|
|
say $curl;
|
|
}
|
|
|
|
sub get_json() {
|
|
my $json_file = path($version_data);
|
|
my $decoded_json;
|
|
if (-e $json_file) {
|
|
my $json_data = $json_file->slurp;
|
|
$decoded_json = decode_json($json_data);
|
|
}
|
|
else {
|
|
die "JSON file not found at $json_file";
|
|
}
|
|
return $decoded_json;
|
|
};
|
|
|
|
sub generate_restores($path, $version_date, $version_time, $restore_path) {
|
|
if ($restores{$path}) {
|
|
say "Path exists, checking for latest verson" if $verbose;
|
|
if ($version_date > $restores{$path}{'version_time'}) {
|
|
say "Later version found" if $verbose;
|
|
$restores{$path} = {
|
|
'version_date' => $version_date,
|
|
'version_time' => $version_time,
|
|
'restore_path' => $restore_path,
|
|
};
|
|
}
|
|
else {
|
|
say "Keeping current version" if $verbose;
|
|
}
|
|
}
|
|
else {
|
|
say "Inserting new path" if $verbose;
|
|
$restores{$path} = {
|
|
'version_date' => $version_date,
|
|
'version_time' => $version_time,
|
|
'restore_path' => $restore_path,
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
sub restore_versions() {
|
|
|
|
for my $path (sort keys %restores) {
|
|
my $source = path("$source_dir/" . $restores{$path}{'restore_path'});
|
|
my $dest = path("$target_dir/$path");
|
|
say "Source: $source" if $verbose;
|
|
say "Dest: $dest" if $verbose;
|
|
my $res;
|
|
|
|
unless (-d $dest->parent) {
|
|
$dest->parent->mkdir;
|
|
}
|
|
|
|
if (! -f $dest || $overwrite) {
|
|
try {
|
|
$res = 'restored';
|
|
# my $restore_copy = $source->copy($dest) if $copy; # Doesn't preserve timestamps
|
|
my $restore_copy = system('cp', '-p', $source, $dest) if $copy;
|
|
say "$restore_copy successfully restored" if $verbose;
|
|
++$stats{'restored'};
|
|
}
|
|
catch {
|
|
$res = 'failed ';
|
|
say "$source -> $dest failed to restore" if $verbose;
|
|
++$stats{'failed'};
|
|
}
|
|
}
|
|
else {
|
|
$res = 'exists ';
|
|
++$stats{'exists'};
|
|
}
|
|
|
|
if ($update_ts) {
|
|
update_timestamp($path);
|
|
say "updated timestamp" if $verbose;
|
|
}
|
|
|
|
my $processed = $stats{'restored'} + $stats{'exists'} + $stats{'failed'};
|
|
my $selected = $stats{'selected'};
|
|
say sprintf('%.2f', $processed * 100 / $selected) . "% $processed/$selected $res\t$dest" if $print;
|
|
}
|
|
}
|
|
|
|
sub do_restores() {
|
|
my $decoded_json = get_json();
|
|
foreach my $path (sort keys %$decoded_json) {
|
|
# Perform your operations on each item
|
|
# For example, print the item
|
|
|
|
for my $version (@{$decoded_json->{$path}}) {
|
|
my $ts = DateTime::Format::ISO8601->parse_datetime($version->{'versionTime'});
|
|
|
|
if ($ts ge $since) {
|
|
my $version_date = $ts->ymd('');
|
|
my $version_time = $ts->hms('');
|
|
my $fullpath = path($path);
|
|
|
|
$fullpath =~ /(.*)(\.\w{2,4}$)/;
|
|
my $base = $1;
|
|
my $ext = $2;
|
|
say $path unless ($1 && $2);
|
|
unless ($1 && $2) {
|
|
++$stats{'skipped'};
|
|
next;
|
|
};
|
|
++$stats{'selected'};
|
|
my $restore_path = path("$base~${version_date}-${version_time}${ext}");
|
|
generate_restores($path, $version_date, $version_time, $restore_path);
|
|
say "$path: $restore_path" if $verbose;
|
|
}
|
|
}
|
|
}
|
|
restore_versions();
|
|
p % stats;
|
|
}
|
|
|
|
sub update_timestamp($path) {
|
|
# return unless $path =~ /\.jpe?g$/i;
|
|
try {
|
|
my $source = path("$source_dir/" . $restores{$path}{'restore_path'});
|
|
my $dest = path("$target_dir/$path");
|
|
my $stat = stat($source);
|
|
utime($stat->atime, $stat->mtime, $dest);
|
|
}
|
|
catch {
|
|
say "unable to update timestamp";
|
|
}
|
|
}
|
|
|
|
|
|
get_versions() if $get_versions;
|
|
do_restores() if $do_restores;
|
|
|