From a8ee129f11180538c6799d62e4dae185f3a3a572 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sun, 8 Feb 2015 20:12:11 +0100 Subject: [PATCH] Extended attributes and import of meta-data The format of gallery.list has been extended to allow multi-line entries containing further attributes of photos. This can be used to provide extra pieces of meta-data like geographical coordinates, which are further propagated to cache files via gal-gen. Also, gal-scan can be fed partial gallery.list entries on its stdin, which makes it easier to import photos from external programs including selected meta-data. Finally, "gal-scan --add" can be used to add more photos to an existing gallery. --- gal/FORMAT | 18 +++++++++++++++ gal/UCW/Gallery.pm | 56 +++++++++++++++++++++++++++++++++++++--------- gal/bin/gal-gen | 15 ++++++++----- gal/bin/gal-scan | 36 +++++++++++++++++------------ 4 files changed, 95 insertions(+), 30 deletions(-) diff --git a/gal/FORMAT b/gal/FORMAT index a7a4463..059f2af 100644 --- a/gal/FORMAT +++ b/gal/FORMAT @@ -13,6 +13,24 @@ One photo per line, tab-separated columns: Lines starting with "#" are ignored. +Lines starting with a tab add further attributes to the previous photo. +The following attributes are recognized: + + lat geographic latitude in degrees north of equator + lon geographic longitude in degrees east of Greenwich + alt geographic altitude in meters + t time when the photo was taken (2014-01-25 09:40:12) + + +Importing meta-data from other sources +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Occasionally, you want to add photos from a source which already provides +some meta-data. In this case, you can construct a gallery.list with a subset +of fields, or feed the stdin of "gal scan" with such a list. + +For convenience, orientation, transformation and title can be also given +as named attributes. They are called "orientation", "xf", and "title". + Photo meta-data (PhotoDir/gallery.meta or CacheDir/cache.meta) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gal/UCW/Gallery.pm b/gal/UCW/Gallery.pm index 1e7bba3..748fe3c 100644 --- a/gal/UCW/Gallery.pm +++ b/gal/UCW/Gallery.pm @@ -1,5 +1,5 @@ # Simple Photo Gallery -# (c) 2003--2014 Martin Mares +# (c) 2003--2015 Martin Mares package UCW::Gallery; @@ -101,6 +101,18 @@ sub get_config_keys($) { return keys %{$self->{cfg}}; } +our %list_attrs = ( + 'file' => 0, # 0 = not permitted as extended attribute + 'id' => 0, + 'orientation' => 1, # 1 = aliases for normal attributes + 'title' => 1, + 'xf' => 2, # 2 = ... and propagated to gal-gen + 'lat' => 3, # 3 = normal extended attributes, propagated to gal-gen + 'lon' => 3, + 'alt' => 3, + 't' => 3, +); + sub write_list($$$) { my ($self, $file, $images) = @_; open my $fh, '>:utf8', "$file.new" or die "Cannot create $file.new: $!\n"; @@ -109,31 +121,55 @@ sub write_list($$$) { $i->{file}, $i->{id}, $i->{orientation}, - $i->{xfrm}, + $i->{xf}, ($i->{title} eq '' ? '-' : $i->{title}), ), "\n"; + for my $k (keys %$i) { + print $fh "\t$k=", $i->{$k}, "\n" if $list_attrs{$k} >= 3; + } } close $fh; rename "$file.new", $file or die "Cannot rename $file.new to $file: $!\n"; } -sub read_list($$) { - my ($self, $file) = @_; +sub read_list_fh($$) { + my ($self, $fh) = @_; my @images = (); - open my $fh, '<:utf8', $file or return; while (<$fh>) { chomp; /^$/ and next; /^#/ and next; - my $i = {}; - ($i->{file}, $i->{id}, $i->{orientation}, $i->{xfrm}, $i->{title}) = split /\t/; - if ($i->{title} eq '-') { $i->{title} = ""; } - push @images, $i; + if (/^\t/) { + @images or die "Misplaced continuation line before first image\n"; + if (my ($k, $v) = /^\t+(.*?)=(.*)/) { + # Continutation of previous line + my $i = $images[-1]; + if ($list_attrs{$k}) { + $i->{$k} = $v; + } else { + print STDERR "Ignoring unknown attribute $k for ", $i->{file}, "\n"; + } + } else { + die "Invalid continuation line. Expecting 'key=value'.\n"; + } + } else { + my $i = {}; + ($i->{file}, $i->{id}, $i->{orientation}, $i->{xf}, $i->{title}) = split /\t/; + if (!defined $i->{title} || $i->{title} eq '-') { $i->{title} = ""; } + push @images, $i; + } } - close $fh; return \@images; } +sub read_list($$) { + my ($self, $file) = @_; + open my $fh, '<:utf8', $file or return; + my $list = $self->read_list_fh($fh); + close $fh; + return $list; +} + sub write_meta($$) { my ($self, $file, $meta) = @_; open my $fh, '>', "$file.new" or die "Cannot create $file.new: $!\n"; diff --git a/gal/bin/gal-gen b/gal/bin/gal-gen index dfd434f..1ee10b0 100755 --- a/gal/bin/gal-gen +++ b/gal/bin/gal-gen @@ -1,6 +1,6 @@ #!/usr/bin/perl # UCW Gallery: Generate published photos -# (c) 2004--2014 Martin Mares +# (c) 2004--2015 Martin Mares use strict; use warnings; @@ -58,7 +58,10 @@ sub get_meta_basic($$$) { $h = int($h + .5); $m->{o} = $rotate; - $m->{xf} = $f->{xfrm}; + for my $k (keys %UCW::Gallery::list_attrs) { + next if $UCW::Gallery::list_attrs{$k} < 2; + $m->{$k} = $f->{$k} if defined $f->{$k}; + } $m->{w0} = $w0; $m->{h0} = $h0; $m->{w} = $w; @@ -108,15 +111,15 @@ sub get_meta_exif($$) { # printf "[GEO: lat=%s lon=%s alt=%s] ", $lat // '?', $lon // '?', $alt // '?'; if ($lat && $lon) { - $m->{lat} = $lat; - $m->{lon} = $lon; + $m->{lat} //= $lat; + $m->{lon} //= $lon; } - $m->{alt} = $alt if $alt; + $m->{alt} //= $alt if $alt; my $time = $i->{image}->{'Image Created'}; if ($time) { if ($time =~ m{^(\d{4}):(\d{2}):(\d{2}) (\d{2}:\d{2}:\d{2})$}) { - $m->{t} = "$1-$2-$3 $4"; + $m->{t} //= "$1-$2-$3 $4"; # print "[TIME: ", $m->{t}, "] "; } else { print "[EXIF: unable to parse time $time] "; diff --git a/gal/bin/gal-scan b/gal/bin/gal-scan index 4424cc3..a976d5d 100755 --- a/gal/bin/gal-scan +++ b/gal/bin/gal-scan @@ -13,14 +13,19 @@ use Getopt::Long; if (@ARGV && $ARGV[0] eq '--help') { die < - or: gal scan --update +Usage: cat list | gal scan + or: gal scan + or: gal scan --update + +Options: +--add Keep existing images and add new ones AMEN } +my $add; my $update; GetOptions( + 'add!' => \$add, 'update!' => \$update, ) or die "Try gal scan --help\n"; @@ -54,10 +59,8 @@ if ($update) { } } } else { - while () { - chomp; - push @source, { file => $_ }; - } + binmode STDIN, ':utf8'; + @source = @{$gal->read_list_fh(\*STDIN)}; } my $hashes = UCW::Gallery::Hashes->new($gal); @@ -102,8 +105,8 @@ foreach my $src (@source) { } print " ori=", $src->{orientation}; - defined $src->{xfrm} or $src->{xfrm} = $gal->get('ScanDefaultTransform'); - print " xfrm=", $src->{xfrm}; + defined $src->{xf} or $src->{xf} = $gal->get('ScanDefaultTransform'); + print " xfrm=", $src->{xf}; $src->{title} //= ''; push @images, $src; @@ -120,12 +123,17 @@ if (!$update) { my $id = $o->{id}; my $i = $new_by_id{$id}; if (!$i) { - print "\t$id: removed\n"; - next; + if ($add) { + print "\t$id: kept\n"; + push @result, $o; + } else { + print "\t$id: removed\n"; + } + } else { + print "\t$id: kept\n"; + push @result, $o; + delete $new_by_id{$id}; } - print "\t$id: updated\n"; - push @result, $o; - delete $new_by_id{$id}; } for my $i (@images) { my $id = $i->{id}; -- 2.39.2