#!/usr/bin/perl use strict; use warnings; use Gtk2 '-init'; use Image::ExifTool; use Date::Calc; use utf8; use Date::Language; my $glade_file='__DATA__'; my $exif=Image::ExifTool->new(); my $gui=GUI->new($glade_file); # Tags in denen Datuminformationen stehen # Es werden nacheinander alle in der Liste Getestet # bei einem Erfolg wird die Suche beendet. my @try_tags=qw( DateTime ModifyDate DateTimeOriginal CreateDate FileModifyDate ); # Tags in denen die Datuminformation gespeichert werden soll # erst wird versucht in ein vorhandenes zu speichern # dann wird das erste anlelegt # bei einem Fehler das zweit, dan das dritte usw. my @save_tags=qw( ModifyDate DateTime DateTimeOriginal CreateDate ); my @date=(-1,-1,-1,-1,-1,-1); $gui->signal_connect('file_select',sub { my $file=pop(); $gui->set_info("lade EXIF Info von $file"); if($exif->ExtractInfo($file)) { my @d=(); my $date='???'; my $tag=''; for (@try_tags) { $tag=$_; my $val=$exif->GetValue($_); if(defined($val)) { @d=parse_date($val); last if(@d); $gui->set_info(qq#Kann "$date" aus $tag nicht parsen!#); $date=$val; } } if(@d) { @date=@d; $date=format_date(@d); $gui->set_info("Datum aus $tag von $file gelesen"); } else { @date=(-1,-1,-1,-1,-1,-1); } $gui->load_image($file); $gui->set_exif($date); return 1; } $gui->set_info("Kann EXIF Info nicht lesen!"); my $err=$exif->GetValue('Error'); warn($err."\n") if($err); return 1; }); $gui->signal_connect('exif_update', sub { my @v=@_[1,2,3]; if(( grep{$_ == -1}@date ) == 6) { $gui->warning("Datum Konnte nicht gelesen werden.\n Keine Änderungen möglich!"); return 1; } my @t=qw(Stunde Minute Sekunde); my $msg=''; for(@v) { my $v=shift(@t); next if(length($_)==0); $msg.="$v ist nicht korrekt Formatiert ($_)!\n" unless(/^[+-]?\d+$/); } if($msg) { $msg.="\n Die Einträge dürfen folgende Formate haben:\n". "+ => Zum Basiswert diesen Wert addieren\n". "- => Vom Basiswert diesen Wert subtrahieren\n". ' => Diesen Wert setzen'; $gui->warning($msg); return 1; } my $pos=0; @v=map{[$_,$pos++]}@v; @v=sort{$a->[0] cmp $b->[0]}@v; for(@v) { my $val=$_->[0]; my $pos=$_->[1]; next if(length($val)==0); if($val=~/^[+-]/) { my @diff=(0,0,0,0); $diff[$pos+1]=$val; @date = Date::Calc::Add_Delta_DHMS(@date,@diff); } else { $date[$pos+3]=$val; } } $gui->set_exif(format_date(@date)); return 1; }); $gui->signal_connect('file_save', sub { my $file=pop(); my $date=format_date(@date); my $ok=0; my $err=undef; for my $tag (@save_tags) { next unless($exif->GetValue($tag)); (undef,$err)=$exif->SetNewValue($tag,$date); unless($err) { $ok=1; last; } $gui->set_info($err); } unless($ok) { for my $tag (@save_tags) { (undef,$err)=$exif->SetNewValue($save_tags[0],$date); last unless($err); $gui->set_info($err); } } $ok=$exif->WriteInfo($file); unless($ok) { $gui->warning(qq#Can't write "$file"\n$!#); return 1; } if($ok==2) { $gui->set_info("$file unverändert geschrieben!"); return 1; } $gui->set_info("$file geschrieben"); }); $gui->run(); ######################################################################## sub parse_date { my $date=shift // ''; my @r; for(qw(English German French Italian)) { my $lang = Date::Language->new($_); my @r = $lang->strptime($date); last if(@r); } return @r[5,4,3,2,1,0] if(@r); return ($1,$2,$3,$4,$5,$6) if($date=~/(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/); return (); } sub now_date{ return format_date(Date::Calc::Today_and_Now()); } sub format_date { sprintf('%04u:%02u:%02u %02u:%02u:%02u',@_); } ######################################################################## ######################################################################## { package GUI; use strict; use warnings; use Gtk2; use Glib qw(TRUE FALSE); our $VERSION='0.1'; use Glib::Object::Subclass Gtk2::Builder::, signals => { file_select => { flags => [qw/run-last/], param_types => [qw/Glib::String/], }, file_save => { flags => [qw/run-last/], param_types => [qw/Glib::String/], }, exif_update => { flags => [qw/run-last/], param_types => [qw/Glib::String/,qw/Glib::String/,qw/Glib::String/], }, quit => { flags => [qw/run-last/], param_types => [], }, }, properties => []; sub new { my $class=shift; my $file=shift // '__DATA__'; my $self=$class->SUPER::new(); if($file eq '__DATA__') { local $/=undef; return undef unless($self->add_from_string()); close(main::DATA); } else { return undef unless($self->add_from_file($file)); } my $filter=Gtk2::FileFilter->new(); $filter->add_mime_type('image/*'); $self->img_chose->set_filter($filter); $self->connect_signals($self); $self->main_win->show_all(); return $self; } sub GUI { return $_[0]; } sub run { Gtk2->main; } sub main_win { return $_[0]->get_object('main_win'); } sub img_name { return $_[0]->get_object('image_name'); } sub img_date { return $_[0]->get_object('image_date'); } sub img_prev { return $_[0]->get_object('image_preview'); } sub img_scrl { return $_[0]->get_object('image_scrolled'); } sub img_chose{ return $_[0]->get_object('image_choose'); } sub entry_std{ return $_[0]->get_object('entry_stunde'); } sub entry_min{ return $_[0]->get_object('entry_minute'); } sub entry_sek{ return $_[0]->get_object('entry_sekude'); } sub status { return $_[0]->get_object('statusbar'); } sub load_image { my $self=shift; my $file=shift; my $pixbuf=eval{ Gtk2::Gdk::Pixbuf->new_from_file($file) }; if($@) { (my $msg=$@)=~s/\s+/ /gs; $msg=~s/\s+at.+?$//; warn "$msg\n"; $self->set_info("$msg"); $pixbuf=Gtk2::Gdk::Pixbuf->new_from_data(pack('C*',255,255,255,255), 'rgb', 1, 8, 1, 1, 4); } $self->{pixbuf}=$pixbuf; $self->img_prev->set_from_pixbuf($pixbuf); $self->img_name->set_label($file); } sub set_exif { my $self=shift; my $date=shift // '???'; $self->img_date->set_label($date); } sub set_info { my $self=shift; my $text=shift; my $stb=$self->status(); my $id=$stb->get_context_id('Info'); $stb->pop($id); $stb->push($id, $text); } sub warning { my $self=shift; my $text=shift // ''; my $dialog = Gtk2::Dialog->new ('WARNUNG', $self->main_win, 'GTK_DIALOG_MODAL', 'gtk-ok' => 'ok'); my $l=Gtk2::Label->new($text); $dialog->get_content_area()->add($l); $dialog->set_default_response ('ok'); $dialog->show_all(); $dialog->run; $dialog->destroy; } #----------------------------------------------------------------------- sub on_update_date { my $self=pop(); my $std=$self->entry_std->get_text(); my $min=$self->entry_min->get_text(); my $sek=$self->entry_sek->get_text(); $self->signal_emit('exif_update',$std,$min,$sek); } sub on_save_image { my $self=pop(); my $file=$self->img_name->get_label(); $self->signal_emit('file_save',$file); } sub on_select_file { my $self=pop(); my $file=$self->img_chose->get_filename(); $self->signal_emit('file_select',$file); } sub on_quit { my $self=pop(); $self->signal_emit('quit'); Gtk2->main_quit; } 1;} package main; __DATA__ 850 500 True vertical True True 5 500 True 5 vertical False True True vertical True True 1 5 Image Name: False 0 True 0 ??? 1 False 0 100 100 True True True queue True gtk-missing-image 1 True vertical True 4 2 True True 1 2 3 4 True True 1 2 2 3 True True 1 2 1 2 True 1 5 Sekunde: 3 4 GTK_FILL GTK_FILL True 1 5 Minute: 2 3 GTK_FILL GTK_FILL True 1 5 Stunde: 1 2 GTK_FILL GTK_FILL True 0 ??? 1 2 True 1 5 Datum: GTK_FILL False 0 True 5 end gtk-apply True True True True False False 0 gtk-save True True True True False False 1 gtk-quit True True True True False False 2 False 5 1 False 2 True True 0 True 2 False 1