1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
#!/usr/bin/perl
# ifuse-photos-to-tmp -- import photos from iPhone/iPad/etc. using ifuse
# Copyright (C) 2019 Sean Whitton
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
use 5.028;
use strict;
use warnings;
use Const::Fast;
use DateTime;
use DateTime::TimeZone::Local;
use DateTime::Format::Epoch;
use File::Copy;
use File::Find;
use File::Path qw(make_path);
use File::Spec::Functions;
use File::stat qw(stat);
use Image::ExifTool qw(ImageInfo);
use Syntax::Keyword::Try;
# should be absolute paths
const my $mount => "$ENV{HOME}/mnt/hermes";
const my $dest => "$ENV{HOME}/tmp";
try {
make_path($mount, $dest);
}
catch {
die "could not mkdir $mount and/or $dest";
}
unless (grep { /ifuse on $mount/ } `mount`) {
system "ifuse $mount";
die "could not mount $mount with ifuse" unless ($? == 0);
}
my $epoch = DateTime->new(year => 1970, month => 1, day => 1);
my $epoch_formatter = DateTime::Format::Epoch->new(epoch => $epoch, );
find(sub {
my $file = $_;
# we handle any file, images or not, falling back to mtime if
# we can't extract EXIF data
return unless (-f $file);
my $info = ImageInfo($file, 'FileType', 'CreateDate', 'DateCreated');
my $ext; my $dt;
my $no_exif = 0;
# a closure, in particular over $info and $dt
my $try_apple_exif = sub {
my $key = shift;
if ($info->{$key} =~
/([0-9]+):([0-9]+):([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/) {
$dt = DateTime->new(year => $1, month => $2, day => $3,
hour => $4, minute => $5, second => $6);
}
};
if (exists $info->{FileType} && exists $info->{CreateDate}
&& $info->{FileType} eq "JPEG") {
$ext = "jpg";
$try_apple_exif->("CreateDate");
} elsif (exists $info->{FileType} && exists $info->{DateCreated}
&& $info->{FileType} eq "PNG") {
$ext = "png";
$try_apple_exif->("DateCreated");
} elsif (exists $info->{FileType} && exists $info->{MediaCreateDate}
&& $info->{FileType} eq "MOV") {
$ext = "mov";
$try_apple_exif->("MediaCreateDate");
} elsif (exists $info->{FileType} && exists $info->{CreateDate}
&& $info->{FileType} eq "MOV") {
$ext = "mov";
$try_apple_exif->("CreateDate");
} else {
$no_exif = 1;
$file =~ /\.([^.]+\z)/;
$ext = lc($1) if defined $1;
$dt = $epoch_formatter->parse_datetime(stat($file)->mtime);
$dt->set_time_zone(DateTime::TimeZone::Local->TimeZone());
}
if (defined $ext && defined $dt) {
my $target =
catfile($dest,
$dt->strftime('%Y-%m-%d %H.%M.%S') . "." . $ext);
my $counter = 1;
$target =~ s/\.$ext\z/-1.$ext/ if -e $target;
while (-e $target) {
$counter++;
$target =~ s/-[0-9]+\.$ext\z/-$counter.$ext/;
}
move($file, $target);
say STDERR "fell back to mtime for $file $target" if $no_exif;
} else {
warn "not touching $file because could not determine target name";
}
}, catfile($mount, "DCIM"));
# this causes the device to regenerate its knowledge of what photos it
# has; otherwise they'll still appear despite the files having been moved
unlink glob "$mount/PhotoData/Photos*";
unlink "$mount/PhotoData/com.apple.photos.caches_metadata.plist";
system "fusermount -u $mount";
warn "failed to unmount $mount" unless ($? == 0);
exec ("xdg-open", "$dest")
or warn "failed to open $dest in a graphical file manager";
|