Downloading Directories with Dropbox via API
TaskWarrior tells me:
š» ā jacoby@oz 16:56 83Ā°F ļ“ ļ“ ~
$ task +dropbox
[task next ( +dropbox )]
ID Age Tag Description Urg
5 4mo dropbox sync full directory with Dropbox via API 1.61
1 task
I do not have a strong memory as to why Past Dave thought that was important, but oh well.
I did get to the point where, using WebService::Dropbox, I could upload and download files, but thatās where I left it.
I mean, I get it. My tendency is to ln -s Dropbox/bin bin
and lib
and a few others, so I keep my collection of tools around where I want them, with only a quick sync away from everything, should I rewrite my sudoku solver or the thing that randomly tweets a picture of Snoopy playing saxophone. Yāknow, the important things.
And, there are some places I run that I run headless without permissions and canāt really install the full Dropbox setup. Maybe my lib/
?
Anyway, lack of urgent fires lead to me cleaning out my TaskWarrior tasks, and I noticed the above, and decided to finish it. It is not clean, but I havenāt posted to the blog recently enough, so here goes.
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use feature qw{ postderef say signatures state };
no warnings qw{ experimental::postderef experimental::signatures };
use Carp;
use File::Path qw{make_path};
use Getopt::Long;
use IO::File;
use Pod::Usage;
use WebService::Dropbox;
use YAML qw{LoadFile};
my $json = JSON->new->canonical->pretty;
my $config = config();
my $dropbox = WebService::Dropbox->new(
{
key => $config->{key},
secret => $config->{secret},
}
);
# Authorization
if ( $config->{token} ) {
$dropbox->access_token( $config->{token} );
}else {
my $url = $dropbox->authorize;
print "Please Access URL and press Enter: $url\n";
print "Please Input Code: ";
chomp( my $code = <STDIN> );
unless ( $dropbox->token($code) ) {
die $dropbox->error;
}
print "Successfully authorized.\nYour AccessToken: ",$dropbox->access_token, "\n";
}
if ( $config->{directory} ) {
my $remote = '/' . $config->{directory};
get_dir( $remote, $dropbox );
}
sub get_dir( $remote, $dropbox ) {
my $local = join '', $ENV{HOME}, '/Dropbox', $remote;
if ( !-d $local ) { make_path($local) }
my $result = $dropbox->list_folder($remote);
for my $e ( $result->{entries}->@* ) {
if ( $e->{'.tag'} eq 'folder' ) {
my $next = $e->{path_display};
get_dir( $next, $dropbox );
}
if ( $e->{'.tag'} eq 'file' ) {
my $file = $e->{path_display};
get_file( $file, $dropbox );
}
}
}
sub get_file( $remote, $dropbox ) {
my $local = join '', $ENV{HOME}, '/Dropbox', $remote;
say $remote ;
say $local ;
say '';
my $fh = IO::File->new( $local, '>' );
my $response = $dropbox->download( $remote, $fh );
say $json->encode($response);
}
exit;
sub config () {
my $config_file = $ENV{HOME} . '/.dropbox.yml';
croak 'No Config' unless -f $config_file;
my $config = LoadFile($config_file);
$config->{download} = 0;
$config->{upload} = 0;
GetOptions(
'help' => \$config->{help},
'man' => \$config->{man},
'directory=s' => \$config->{directory},
);
pod2usage( -verbose => 2, -exitval => 1 ) if $config->{man};
pod2usage( -verbose => 1, -exitval => 1 ) if $config->{help};
pod2usage( -verbose => 1, -exitval => 1 )
unless $config->{directory} =~ /\w/;
delete $config->{help};
delete $config->{man};
return $config;
}
exit;
=head1 NAME
dropbox_copy.pl -- This documentation is for last time and needs updating so clipping
=head1 LICENSE
This is released under the Artistic
License. See L<perlartistic>.
=head1 AUTHOR
Dave Jacoby L<jacobydavid@live.com>
=cut
The hard part of using web APIs like this, I find, is token management. Once you get that, itās using LWP
or Mojo::UserAgent
or curl
or whatever, but the task of getting permissions in the first place is always what hinders me. I recall the process under WebService::Dropbox
being easy.
I feel I should note the modules that make this easy
Carp is a module that supplants die
and warn
, due to antisocial behavior from those commands. Those I kinda use because I know itās the Perly way.
File::Path gives make_path
, which is essentially mkdir -p
without shelling out. Similarly, IO::File is a thing I normally handle with open my $fh, '>', $file
or whatever, but the module suggested this and I like well enough.
There are many ways to get options, but Getopt::Long is in Core and is my favorite.
JSON and YAML are kinda pairs, and I use YAML to store config data and JSON to handle the output of APIs. The cool kids are moving to Cpanel::JSON::XS, which I often use as well.
Perl uses POD, or Plain Old Documentation as their default user documentation, and Pod::Usage allows me to tie it to -h
and -m
, allowing me to easily create both short-form and long-form user documentation, for when I go back after years and wonder āWhat is this and why am I running it?ā
Observant readers will notice that the task says sync while this simply copies from Dropbox. This might be fixed, but it might not.
If you have any questions or comments, I would be glad to hear it. Ask me on Twitter or make an issue on my blog repo.