#!/usr/bin/perl -w use FindBin; # to find where we are located. use Gtk; # gui toolkit use strict; set_locale Gtk; init Gtk; my $false = 0; my $true = 1; ####################################################################################### # # # # PHOTOBASE # # Digital camera image download automation tool # # VERSION 0.2.2 # # AUTHOR CONTACT: # LEIAVOIA -at- COMCAST -dot- NET # # ABSTRACT: # PhotoBase is (at it's current phase of growth) an image downloading utility. # It makes it easy to grab images en mass from a card reader or similar mounted # file system. It performs auto-rotation of JPEG images, conversion of RAW files # to a variety of file formats including JPEG, PPM, PSD, TIFF 24bit and 48bit, # and several other workflow enhancing features. It was designed to speed up the # boring side of digital imaging by automating common tasks involved in acquiring # images. In the future, PhotoBase may become a database frontend for storing and # organizing photos. # # FEATURES: # - Downloads all images off your card or camera with a single button press. # - Automatically mounts and unmounts any mountable filesystem (like a card reader) # - Automatic image rotation (if your camera can mark images with orientation data.) # - RAW conversion into PPM, TIFF, PSD, and JPEG. # - JPEG resampling to save space. # - Saves and zips backup copies. # - Extracts JPEG images embedded in RAW files. # - Progress statistics. # - Sharpens images. # # LICENSE: # This software is free and may be copied, distributed, sold, or whatever. # This software comes with zero warranty. Not responsible for damage, theft, # birth defects, or unexplained fires. This program is not proven to prevent, # cure, or treat any condition or disease. Any positive effects are purely # coincidental. # # USAGE: # Step 1) You need to have the following programs installed on your system: # Gtk-perl (for the GUI) # ImageMagick # jhead (for EXIF data transplantation) # exiftags (for EXIF data output) # dcraw [optional] (for converting from RAW) # parse.c (from Dave Coffin's page - for extracting RAW embedded JPG images) # Step 2) from the command line, run # $ perl photobase.pl # -or- # Set this script's executable bits and execute by any other means with: # $ photobase.pl # Step 3) Before downloading, you need to configure this program's options from the # live setup menu. # Step 4) Click download with your card or camera plugged in. Any errors *should* # be reported to you, although error checking is not this program's specialty. # # CAVEATS: # - Currently, PhotoBase is not trained to download TIFF images from the camera. # - PhotoBase seems to have some weirdness when you try two downloads in a row. # It's recommended that you quit and start it again instead. # - Only works on Linux at this point. # # NOTES: # - Image rotation is automatic if you have an orientation sensor in your camera. # - PhotoBase will automatically mount a mountable file system (such as a USB # card reader or camera.) You do not need to manually mount it. # - This program is still in development. Don't use it for mission critical anything. # If it eats your images, it's your own fault for using this program to begin with. # - If you appreciate this script, send the author a thank you. It's the least you could do. # - If you can incorporate this code into a larger project or would like to add features # to it, please don't fight the urge. Send updates or patches or bug reports to # the author please. # # # ######################################################################################## # CHANGE LOG: # 0.2.2 December 14, 2003 # - fixed TIFF48 bug # 0.2.1 December 4, 2003 # - fixed multiple download bug # - fixed other minor bugs # 0.2 Late November # - second release. # - added more RAW options # - added axtract JPEG from RAW # 0.1 Mid November # - First release # TO DO LIST ++++++++++++++++++++++++++++++++++ # other features besides download: # raw convert after download # color text output # ====== BASIC SETUP & DECLARATIONS =====================\/========================= # Main UI vars my $window; my $download_btn; my $quit_btn; my $mainbox; my $btn_pane; my $progbar; my $msgbox; my $msgbox_pane; my $msgbox_slider; my $statlist; my $open_configs_btn; # configs window my $conf_window; my $conf_notebook; my $buffer_label; my $conf_close_btn; my $conf_mainbox; my $buffer_adj; # locations tab my $conf_locs_box; my $conf_src_dir_lab; my $conf_src_dir; my $conf_dest_dir_lab; my $conf_dest_dir; my $conf_mogrify_loc_lab; my $conf_mogrify_loc; my $conf_convert_loc_lab; my $conf_convert_loc; my $conf_jhead_loc_lab; my $conf_jhead_loc; my $conf_dcraw_loc_lab; my $conf_dcraw_loc; my $conf_exiftags_loc_lab; my $conf_exiftags_loc; my $conf_parseraw_loc_lab; my $conf_parseraw_loc; # general tab my $conf_genset_box; my $conf_preserve_orig; my $conf_zip_orig; my $conf_del_src; my $conf_per_session_dump; # jpg tab my $conf_jpg_box; my $conf_jpg_rotate; my $conf_jpg_sharpen; my $conf_jpg_sharpen_amount_lab; my $conf_jpg_sharpen_amount; my $conf_jpg_resample; my $conf_jpg_resample_q_lab; my $conf_jpg_resample_q; # raw tab my $conf_raw_box; my $conf_raw_xjpg; my $conf_raw_conv_jpg; my $conf_raw_conv_ppm; my $conf_raw_conv_ppm48; my $conf_raw_conv_tiff; my $conf_raw_conv_tiff48; my $conf_raw_conv_psd; my $conf_raw_bright; my $conf_raw_bright_lab; my $conf_raw_wb; my $conf_raw_normalize; # Files to exclude. Files are NOT full path names: my @excludefiles = ('.' , '..', '.xvpics', 'dump'); # per-session destination dump folder # this is different from the one in the user configs. # This one gets modified as the program runs. the user configs one is the base folder. my $dest_dir; # 1 if the source dir is a FS that needed mounting - remembers to umount after program finishes my $mounted_fs = 0; # config file name - do not include dots or slashes # This is a relative path, no exceptions. must be in same dir as script. my $config_file_name = 'photobase.conf'; # file probe logbook my @fslog = (); # list of downloaded files to work through my @files_todo = (); #file probe stats my %stats = ( 'jpgs_found' => 0, 'raws_found' => 0, 'totalfiles_found' => 0, 'jpgbytes_found' => 0, 'rawbytes_found' => 0, 'totalbytes_found' => 0, 'jpgs_trans' => 0, 'raws_trans' => 0, 'totalfiles_trans' => 0, 'jpgbytes_trans' => 0, 'rawbytes_trans' => 0, 'totalbytes_trans' => 0, 'jpgs_proc' => 0, 'raws_proc' => 0, 'totalfiles_proc' => 0, 'jpgbytes_proc' => 0, 'rawbytes_proc' => 0, 'totalbytes_proc' => 0 ); # configs w/ defaults if no config file my %configs = ( 'RAW->JPG' => $true, 'RAW->PPM' => $false, 'RAW->PPM48' => $false, 'RAW->TIFF' => $false, 'RAW->TIFF48' => $false, 'RAW->PSD' => $false, 'raw_brightness' => 10.0, 'raw_use_wb' => $true, 'raw_normalize' => $true, 'raw_xjpg' => $true, 'jpg_rotate' => $true, 'jpg_sharpen' => $false, 'jpg_sharpen_amount' => 40, 'jpg_resample' => $false, 'jpg_resample_quality' => 90, 'preserve_orig' => $true, 'zip_orig' => $false, 'del_src_files' => $false, 'per_session_dump' => $true, 'src_dir' => "/mnt/cam", 'dest_dir' => "/home/leiavoia/graphics/photos/incoming", 'jhead_loc' => "/usr/bin/jhead", 'mogrify_loc' => "/usr/bin/mogrify", 'convert_loc' => "/usr/bin/convert", 'dcraw_loc' => "/usr/bin/dcraw", 'exiftags_loc' => "/usr/bin/exiftags", 'parseraw_loc' => "/usr/bin/parseraw" ); my @DirTracker; # ====== BASIC SETUP & DECLARATIONS =====================/\========================= #===== FLOW CHART =========================\/=============================== LoadConfigFile() || DumpConfigFile(); # create one now if there isn't one already - defaults are loaded above InitUI(); # START! main Gtk; #===== FLOW CHART =========================/\=============================== #===== UI FUNCTIONS =========================\/=============================== sub InitUI { # Create the window $window = new Gtk::Window( "toplevel" ); $window->signal_connect( "delete_event", sub { Gtk->exit( 0 ); } ); $window->title( "PhotoBase (v0.2)" ); $window->border_width( 20 ); #$window->set_default_size( 500,300 ); # main vertical packbox $mainbox = new Gtk::VBox( $false, 20 ); $window->add( $mainbox ); # button pane $btn_pane = new Gtk::HBox( $false, 20 ); $mainbox->pack_start( $btn_pane, $true, $false, 0 ); $btn_pane->show(); # Download $download_btn = new Gtk::Button( "Download" ); $download_btn->signal_connect( "clicked", \&StartDownloadProcess, "Download" ); $btn_pane->pack_start( $download_btn, $false, $false, 0 ); $download_btn->show(); # Configs $open_configs_btn = new Gtk::Button( "Settings" ); $open_configs_btn->signal_connect( "clicked", \&OpenConfigWindow, "Settings" ); $btn_pane->pack_start( $open_configs_btn, $false, $false, 0 ); $open_configs_btn->show(); # Save and Quit $quit_btn = new Gtk::Button( "Quit" ); $quit_btn->signal_connect( "clicked", sub { DumpConfigFile(); Gtk->exit( 0 ); } ); $btn_pane->pack_start( $quit_btn, $false, $false, 0 ); $quit_btn->show(); #stat list my @titles = ('STAT','FOUND','COPIED', 'PROCESSED'); $statlist = new_with_titles Gtk::CList( @titles ); $statlist->append( 'JPEGs','0','0','0' ); $statlist->append( 'RAWs','0','0','0' ); $statlist->append( 'Total Files','0','0','0' ); $statlist->append( 'JPEG Bytes','0','0','0' ); $statlist->append( 'RAW Bytes','0','0','0' ); $statlist->append( 'Total Bytes','0','0','0' ); $statlist->column_titles_passive(); $statlist->set_column_width( 0, 100 ); $statlist->set_column_width( 1, 100 ); $statlist->set_column_width( 2, 100 ); $statlist->set_column_width( 3, 100 ); $statlist->show(); $mainbox->pack_start( $statlist, $true, $true, 0 ); # message box $msgbox = new Gtk::Text( undef, undef ); $msgbox->set_word_wrap( $true ); $msgbox->insert( undef, undef, undef, "Click Download to begin downloading from the card reader.\n" ); $msgbox->show(); # Add a vertical scrollbar to the msgbox $msgbox_slider = new Gtk::VScrollbar( $msgbox->vadj ); $msgbox_slider->show(); # load and show msgbox pane $msgbox_pane = new Gtk::HBox( $false, 10 ); $msgbox_pane->pack_start( $msgbox, $true, $false, 0 ); $msgbox_pane->pack_start( $msgbox_slider, $true, $false, 0 ); $msgbox->set_usize( 400, 200 ); $mainbox->pack_start( $msgbox_pane, $true, $true, 0 ); $msgbox_pane->show(); # progress bar $progbar = new Gtk::ProgressBar(); $progbar->set_bar_style( 'continuous' ); $progbar->set_show_text( $true ); $mainbox->pack_start( $progbar, $false, $false, 0 ); $progbar->show(); $mainbox->show(); $window->show(); } sub OpenConfigWindow { # Create the window $conf_window = new Gtk::Window( "toplevel" ); $conf_window->signal_connect( "delete_event", sub { Gtk->exit( 0 ); } ); $conf_window->title( "PhotoBase Configuration" ); $conf_window->border_width( 20 ); #$window->set_default_size( 500,300 ); # main configs box $conf_mainbox = new Gtk::VBox( $false, 20 ); $conf_mainbox->show(); # close window button $conf_close_btn = new Gtk::Button( "Commit Changes" ); $conf_close_btn->signal_connect( "clicked", sub { SaveConfigChanges(); DumpConfigFile(); $conf_window->destroy(); } ); $conf_close_btn->show(); $conf_mainbox->pack_start( $conf_close_btn, $true, $false, 0 ); # notebook $conf_notebook = new Gtk::Notebook(); $conf_notebook->show(); $conf_mainbox->pack_start( $conf_notebook, $true, $false, 0 ); $conf_window->add( $conf_mainbox ); # LOCATIONS TAB -------------------------\/------------------ # packbox $conf_locs_box = new Gtk::VBox( $false, 10 ); $conf_locs_box->set_border_width(20); $conf_locs_box->show(); # source dir $conf_src_dir_lab = new Gtk::Label( "Source Directory" ); $conf_src_dir_lab->set_alignment( 0, 0 ); $conf_src_dir_lab->show(); $conf_src_dir = new Gtk::Entry(); $conf_src_dir->set_text( $configs{'src_dir'} ); #" $conf_src_dir->show(); # destination dir $conf_dest_dir_lab = new Gtk::Label( "Destination Directory (file dump)" ); $conf_dest_dir_lab->set_alignment( 0, 0 ); $conf_dest_dir_lab->show(); $conf_dest_dir = new Gtk::Entry(); $conf_dest_dir->set_text( $configs{'dest_dir'} ); #" $conf_dest_dir->show(); # mogrify $conf_mogrify_loc_lab = new Gtk::Label( "ImageMagick \"mogrify\" utility location" ); $conf_mogrify_loc_lab->set_alignment( 0, 0 ); $conf_mogrify_loc_lab->show(); $conf_mogrify_loc = new Gtk::Entry(); $conf_mogrify_loc->set_text( $configs{'mogrify_loc'} ); #" $conf_mogrify_loc->show(); # convert $conf_convert_loc_lab = new Gtk::Label( "ImageMagick \"convert\" utility location" ); $conf_convert_loc_lab->set_alignment( 0, 0 ); $conf_convert_loc_lab->show(); $conf_convert_loc = new Gtk::Entry(); $conf_convert_loc->set_text( $configs{'convert_loc'} ); #" $conf_convert_loc->show(); # jhead $conf_jhead_loc_lab = new Gtk::Label( "jhead (EXIF decoder) location" ); $conf_jhead_loc_lab->set_alignment( 0, 0 ); $conf_jhead_loc_lab->show(); $conf_jhead_loc = new Gtk::Entry(); $conf_jhead_loc->set_text( $configs{'jhead_loc'} ); #" $conf_jhead_loc->show(); # dcraw $conf_dcraw_loc_lab = new Gtk::Label( "dcraw (RAW conversion utility) location" ); $conf_dcraw_loc_lab->set_alignment( 0, 0 ); $conf_dcraw_loc_lab->show(); $conf_dcraw_loc = new Gtk::Entry(); $conf_dcraw_loc->set_text( "$configs{'dcraw_loc'}" ); #" $conf_dcraw_loc->show(); # exiftags $conf_exiftags_loc_lab = new Gtk::Label( "exiftags (EXIF decoder) location" ); $conf_exiftags_loc_lab->set_alignment( 0, 0 ); $conf_exiftags_loc_lab->show(); $conf_exiftags_loc = new Gtk::Entry(); $conf_exiftags_loc->set_text( $configs{'exiftags_loc'} ); #" $conf_exiftags_loc->show(); # raw parser $conf_parseraw_loc_lab = new Gtk::Label( "RAW Parser (RAW embedded JPG extractor) location" ); $conf_parseraw_loc_lab->set_alignment( 0, 0 ); $conf_parseraw_loc_lab->show(); $conf_parseraw_loc = new Gtk::Entry(); $conf_parseraw_loc->set_text( $configs{'parseraw_loc'} ); #" $conf_parseraw_loc->show(); $conf_locs_box->pack_start( $conf_src_dir_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_src_dir, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_dest_dir_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_dest_dir, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_mogrify_loc_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_mogrify_loc, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_convert_loc_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_convert_loc, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_jhead_loc_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_jhead_loc, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_dcraw_loc_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_dcraw_loc, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_exiftags_loc_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_exiftags_loc, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_parseraw_loc_lab, $true, $false, 0 ); $conf_locs_box->pack_start( $conf_parseraw_loc, $true, $false, 0 ); # LOCATIONS TAB -------------------------/\------------------ # GENERAL SETTINGS TAB -------------------------\/------------------ # packbox $conf_genset_box = new Gtk::VBox( $false, 10 ); $conf_genset_box->set_border_width(20); $conf_genset_box->show(); $conf_preserve_orig = new Gtk::CheckButton( "Preserve original images" ); $conf_preserve_orig->set_active( $true ) if ($configs{'preserve_orig'}==1); $conf_preserve_orig->show(); $conf_zip_orig = new Gtk::CheckButton( "Zip original images to conserve space" ); $conf_zip_orig->set_active( $true ) if ($configs{'zip_orig'}==1); $conf_zip_orig->show(); $conf_del_src = new Gtk::CheckButton( "Delete source (CF card) files" ); $conf_del_src->set_active( $true ) if ($configs{'del_src_files'}==1); $conf_del_src->show(); $conf_per_session_dump = new Gtk::CheckButton( "Create a new folder for each download" ); $conf_per_session_dump->set_active( $true ) if ($configs{'per_session_dump'}==1); $conf_per_session_dump->show(); $conf_genset_box->pack_start( $conf_preserve_orig, $true, $false, 0 ); $conf_genset_box->pack_start( $conf_zip_orig, $true, $false, 0 ); $conf_genset_box->pack_start( $conf_del_src, $true, $false, 0 ); $conf_genset_box->pack_start( $conf_per_session_dump, $true, $false, 0 ); # GENERAL SETTINGS TAB -------------------------/\------------------ # IMAGE MANIPULATION TAB -------------------------\/------------------ # packbox $conf_jpg_box = new Gtk::VBox( $false, 10 ); $conf_jpg_box->set_border_width(20); $conf_jpg_box->show(); $conf_jpg_rotate = new Gtk::CheckButton( "Automagically rotate all images" ); $conf_jpg_rotate->set_active( $true ) if ($configs{'jpg_rotate'}==1); $conf_jpg_rotate->show(); $conf_jpg_sharpen = new Gtk::CheckButton( "Sharpen all images" ); $conf_jpg_sharpen->set_active( $true ) if ($configs{'jpg_sharpen'}==1); $conf_jpg_sharpen->show(); $conf_jpg_sharpen_amount_lab = new Gtk::Frame( "Sharpening Radius" ); $conf_jpg_sharpen_amount_lab->show(); $buffer_adj = new Gtk::Adjustment( $configs{'jpg_sharpen_amount'}, 0, 20, 0.2, 1, 0 ); $conf_jpg_sharpen_amount = new Gtk::HScale( $buffer_adj ); $conf_jpg_sharpen_amount->set_value_pos( 'right' ); $conf_jpg_sharpen_amount->set_usize(150, 40); $conf_jpg_sharpen_amount->show(); $conf_jpg_sharpen_amount_lab->add($conf_jpg_sharpen_amount); $conf_jpg_resample = new Gtk::CheckButton( "Resample JPG images to conserve space" ); $conf_jpg_resample->set_active( $true ) if ($configs{'jpg_resample'}==1); $conf_jpg_resample->show(); $conf_jpg_resample_q_lab = new Gtk::Frame( "JPG Resampling Quality (100 = best)" ); $conf_jpg_resample_q_lab->show(); $buffer_adj = new Gtk::Adjustment( $configs{'jpg_resample_quality'}, 0, 100, 1, 10, 0 ); $conf_jpg_resample_q = new Gtk::HScale( $buffer_adj ); $conf_jpg_resample_q->set_value_pos( 'right' ); $conf_jpg_resample_q->set_usize(150, 40); $conf_jpg_resample_q->show(); $conf_jpg_resample_q_lab->add($conf_jpg_resample_q); $conf_jpg_box->pack_start( $conf_jpg_rotate, $true, $false, 0 ); $conf_jpg_box->pack_start( $conf_jpg_sharpen, $true, $false, 0 ); $conf_jpg_box->pack_start( $conf_jpg_sharpen_amount_lab, $true, $false, 0 ); $conf_jpg_box->pack_start( $conf_jpg_resample, $true, $false, 0 ); $conf_jpg_box->pack_start( $conf_jpg_resample_q_lab, $true, $false, 0 ); # JPEG TAB -------------------------/\------------------ # RAW TAB -------------------------\/------------------ # packbox $conf_raw_box = new Gtk::VBox( $false, 10 ); $conf_raw_box->set_border_width(20); $conf_raw_box->show(); $conf_raw_xjpg = new Gtk::CheckButton( "Extract Embedded JPEGs" ); $conf_raw_xjpg->set_active( $true ) if ($configs{'raw_xjpg'}==1); $conf_raw_xjpg->show(); $conf_raw_conv_jpg = new Gtk::CheckButton( "Convert to 24bit JPEG" ); $conf_raw_conv_jpg->set_active( $true ) if ($configs{'RAW->JPG'}==1); $conf_raw_conv_jpg->show(); $conf_raw_conv_ppm = new Gtk::CheckButton( "Convert to 24bit PPM" ); $conf_raw_conv_ppm->set_active( $true ) if ($configs{'RAW->PPM'}==1); $conf_raw_conv_ppm->show(); $conf_raw_conv_ppm48 = new Gtk::CheckButton( "Convert to 48bit PPM" ); $conf_raw_conv_ppm48->set_active( $true ) if ($configs{'RAW->PPM48'}==1); $conf_raw_conv_ppm48->show(); $conf_raw_conv_tiff = new Gtk::CheckButton( "Convert to 24bit TIFF" ); $conf_raw_conv_tiff->set_active( $true ) if ($configs{'RAW->TIFF'}==1); $conf_raw_conv_tiff->show(); $conf_raw_conv_tiff48 = new Gtk::CheckButton( "Convert to 48bit TIFF" ); $conf_raw_conv_tiff48->set_active( $true ) if ($configs{'RAW->TIFF48'}==1); $conf_raw_conv_tiff48->show(); $conf_raw_conv_psd = new Gtk::CheckButton( "Convert to 48bit PSD (Photoshop)" ); $conf_raw_conv_psd->set_active( $true ) if ($configs{'RAW->PSD'}==1); $conf_raw_conv_psd->show(); $conf_raw_wb = new Gtk::CheckButton( "Use in-camera's white balancing" ); $conf_raw_wb->set_active( $true ) if ($configs{'raw_use_wb'}==1); $conf_raw_wb->show(); $conf_raw_normalize = new Gtk::CheckButton( "Normalize Exposure" ); $conf_raw_normalize->set_active( $true ) if ($configs{'raw_normalize'}==1); $conf_raw_normalize->show(); $conf_raw_bright_lab = new Gtk::Frame( "Brightness Adjustment" ); $conf_raw_bright_lab->show(); $buffer_adj = new Gtk::Adjustment( $configs{'raw_brightness'}, 0, 20.0, 0.1, 8.0, 0 ); $conf_raw_bright = new Gtk::HScale( $buffer_adj ); $conf_raw_bright->set_value_pos( 'right' ); $conf_raw_bright->set_usize(150, 40); $conf_raw_bright->show(); $conf_raw_bright_lab->add($conf_raw_bright); $conf_raw_box->pack_start( $conf_raw_xjpg, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_conv_jpg, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_conv_tiff, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_conv_tiff48, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_conv_ppm, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_conv_ppm48, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_conv_psd, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_wb, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_normalize, $true, $false, 0 ); $conf_raw_box->pack_start( $conf_raw_bright_lab, $true, $false, 0 ); # RAW TAB -------------------------/\------------------ $buffer_label = new Gtk::Label( "Locations" ); $buffer_label->show(); $conf_notebook->append_page( $conf_locs_box, $buffer_label ); $buffer_label = new Gtk::Label( "General Settings" ); $buffer_label->show(); $conf_notebook->append_page( $conf_genset_box, $buffer_label ); $buffer_label = new Gtk::Label( "Image Manipulation" ); $buffer_label->show(); $conf_notebook->append_page( $conf_jpg_box, $buffer_label ); $buffer_label = new Gtk::Label( "RAW Settings" ); $buffer_label->show(); $conf_notebook->append_page( $conf_raw_box, $buffer_label ); $conf_window->show(); } #===== UI FUNCTIONS =========================/\=============================== #===== SUBROUTINES =========================\/=============================== sub StartDownloadProcess { # zero everything so we don't run over the last download ResetAllStats(); # make sure all programs are accessable and readable if (ScriptSelfTest() == $true) { return; } #Start the Directory Tracker. #Push and pop to modify the current branch of scanning. $configs{'src_dir'} =~ s/\/$//; #/# strip trailing slash if any @DirTracker = ( $configs{'src_dir'} ); #global variable # set the global base destination directory for this download: $dest_dir = $configs{'dest_dir'}; # mount the file system if needed if ( MountFS() == 1) { LogMsg( "*** DOWNLOAD ABORTED ****\n"); return; } # probe for contents ReadDir(); # don't do any more unless we actually found something usefull unless (scalar(@fslog) == 0) { # if per session dump is on, reroute the dest dir to new time-specific folder if ($configs{'per_session_dump'} == $true) { $dest_dir = $dest_dir . '/' . GetTimeStamp(); #' mkdir($dest_dir, 0777); #LogMsg("per session dump directory created at:\n " . $dest_dir . "\n"); } # if preserve_orig is on, make an originals folder too if ($configs{'preserve_orig'} == $true) { # check if there already is one unless (-e "$dest_dir/originals") { mkdir("$dest_dir/originals", 0777); } } # switch to download phase: update progress bar $progbar->set_format_string( "Downloading Phase - %p%% of files downloaded" ); # force redraw Gtk->main_iteration while ( Gtk->events_pending ); # download all files foreach my $i (@fslog) { DownloadFile($i); } # switch to post-processing phase: update progress bar $progbar->update(0); $progbar->set_format_string( "Post Processing Phase - %p%% of files processed" ); # force redraw Gtk->main_iteration while ( Gtk->events_pending ); # post process each file foreach my $i (@files_todo) { HandleFile($i); } # zip the originals if needed if ( $configs{'zip_orig'} == $true ) { LogMsg("Zipping original files..."); # zipping: 9 = better slower zipping, j = no pathnames system("zip -jm9 $dest_dir/originals $dest_dir/originals/*"); system("rmdir $dest_dir/originals"); LogMsg(" done.\n"); } } # unount FS UMountFS(); LogMsg("\n---{ DOWNLOAD COMPLETE }---\n\n"); } sub DownloadFile { # downloads a file to a directory # takes: the path to the source file # FILE NAMES ----------------------------------- # long src file (full path) my $file_long = $_[0]; # short file name my @temp = split( /\//, $file_long ); my $file_short = pop(@temp); # full path to new file name my $file_new = "$dest_dir/$file_short"; # full path to new "originals" folder file name my $file_new_orig = $dest_dir . '/' . 'originals' . '/' . $file_short; # DOWNLOAD ACTION ----------------------------------- # copy file to dump system("cp $file_long $file_new"); LogMsg("$file_short Copied to destination folder.\n"); # copy to originals folder if needed if ($configs{'preserve_orig'} == $true) { system("cp $file_new $file_new_orig"); LogMsg("$file_short Copied to originals folder.\n"); } # delete from source if needed if ($configs{'del_src_files'} == $true) { system("rm $file_long"); LogMsg("$file_short Deleted file from source directory\n"); } # add to the post processing to-do list if it's a full image. if ( IsJPG($file_short) || IsRAW($file_short) ) { push(@files_todo, $file_new); } # GUI UPDATE ----------------------------------- # update in-memory stats: if ( IsJPG($file_short) ) { $stats{'jpgs_trans'}++; $stats{'totalfiles_trans'}++; $stats{'jpgbytes_trans'} += (-s $file_long); $stats{'totalbytes_trans'} += (-s $file_long); } if ( IsRAW($file_short) ) { $stats{'raws_trans'}++; $stats{'totalfiles_trans'}++; $stats{'rawbytes_trans'} += (-s $file_long); $stats{'totalbytes_trans'} += (-s $file_long); } # update the live stats window: $statlist->freeze(); $statlist->set_text( 0, 2, $stats{'jpgs_trans'} ); $statlist->set_text( 1, 2, $stats{'raws_trans'} ); $statlist->set_text( 2, 2, $stats{'totalfiles_trans'} ); $statlist->set_text( 3, 2, FormatByteSize( $stats{'jpgbytes_trans'} ) ); $statlist->set_text( 4, 2, FormatByteSize( $stats{'rawbytes_trans'} ) ); $statlist->set_text( 5, 2, FormatByteSize( $stats{'totalbytes_trans'} ) ); $statlist->set_text( 0, 3, sprintf("%.0f%%", $stats{'jpgs_percent'}) ); $statlist->set_text( 1, 3, sprintf("%.0f%%", $stats{'raws_percent'}) ); $statlist->set_text( 2, 3, sprintf("%.0f%%", $stats{'totalfiles_percent'}) ); $statlist->set_text( 3, 3, sprintf("%.0f%%", $stats{'jpgbytes_percent'}) ); $statlist->set_text( 4, 3, sprintf("%.0f%%", $stats{'rawbytes_percent'}) ); $statlist->set_text( 5, 3, sprintf("%.0f%%", $stats{'totalbytes_percent'}) ); $statlist->thaw(); # update progress bar my $percent = ($stats{'totalfiles_trans'} / $stats{'totalfiles_found'}) if ($stats{'totalfiles_found'}); $progbar->update( $percent ); # force redraw Gtk->main_iteration while ( Gtk->events_pending ); } sub MountFS { # return 1 if problem occured my $source_dir = $configs{'src_dir'}; # scan fstab to see if the source dir needs mounting local $/ = undef; #/ slurp mode open(FILE, "/etc/fstab") or die("could not open fstab"); my $fstab = ; close(FILE); if ( $fstab =~ /\s+($source_dir)\s+/ ) { LogMsg( "Detected mountable file system.\n"); $mounted_fs = 1; # yes, it's a mountable FS # scan mtab to see if the source dir is mounted already local $/ = undef; #/ slurp mode open(FILE, "/etc/mtab") or die("could not open mtab"); my $mtab = ; close(FILE); my $already_mounted = 0; # if already mounted if ( $mtab =~ /\s+($source_dir)\s+/ ) { $already_mounted = 1; LogMsg("[Already mounted]\n"); } # if not mounted if (!$already_mounted) { LogMsg("Attempting to mount \"$source_dir\"...\n"); my $sys_str = "mount " . $configs{'src_dir'} . ' 2>&1'; my $output = `$sys_str`; # check to see if it worked # scan mtab to see if the source dir is still mounted local $/ = undef; #/ slurp mode open(FILE, "/etc/mtab") or die("could not open mtab"); my $mtab = ; close(FILE); # if not mounted if ( $mtab !~ /\s+($source_dir)\s+/ ) { LogMsg("Problems mounting:\n"); LogMsg( $output . "\n"); return 1; } else { LogMsg("Successfully mounted.\n"); } } } if ($mounted_fs == 0) { LogMsg("Detected local file system. No need to mount.\n"); } return 0; } sub UMountFS { # return 1 if problem occured my $source_dir = $configs{'src_dir'}; if ($mounted_fs == 1) { LogMsg("Attempting to unmount \"" . $configs{'src_dir'} . "\"...\n"); my $sys_str = "umount " . $configs{'src_dir'} . ' 2>&1'; my $output = `$sys_str`; # check to see if it worked # scan mtab to see if the source dir is still mounted local $/ = undef; #/ slurp mode open(FILE, "/etc/mtab") or die("could not open mtab"); my $mtab = ; close(FILE); # if already mounted if ( $mtab =~ /\s+($source_dir)\s+/ ) { LogMsg("Problems unmounting:\n"); LogMsg( $output . "\n"); return 1; } else { LogMsg("Successfully unmounted\n"); } } } sub ReadDir { # Recursive engine for scanning dirs. # dumps all scanned files into @fslog # mush DirTracker together for a good directory name: my $thisdir = join ('/', @DirTracker); LogMsg("[ Now scanning $thisdir . . . ] \n"); # get the current dir's contents: local(*CURRENT); opendir(CURRENT, "$thisdir"); my @thisdirinfo = readdir CURRENT; closedir CURRENT; # decide what to do with what we find foreach my $i (@thisdirinfo) { if( Exclude($i) ) {next} #skip if it matches an exclude file # put it on the search list and probe if it's a directory elsif (-d "$thisdir/$i") { push (@DirTracker, $i); ReadDir(); } # deal with it if it's a file elsif (-f "$thisdir/$i") { LogFile( "$thisdir/$i" ); } } # Remove this directory from the tracker to go back up a level: pop @DirTracker; } sub LogFile { # takes: full path to file my $file_long = $_[0]; my @temp = split( /\//, $file_long ); my $file_short = pop(@temp); # screen file. don't download extra junk if ( !IsJPG($file_short) && !IsRAW($file_short) && !IsTHM($file_short) ) { return; } # put the file in the log push(@fslog, $file_long); # calc stats ------------> # if JPG if ($file_short =~ /\.(jpg|JPG|jpeg|JPEG)$/ ) { $stats{'jpgs_found'}++; $stats{'totalfiles_found'}++; $stats{'jpgbytes_found'} += (-s $file_long); $stats{'totalbytes_found'} += (-s $file_long); } #if RAW elsif ($file_short =~ /\.(crw|CRW|raw|RAW)$/ ) { $stats{'raws_found'}++; $stats{'totalfiles_found'}++; $stats{'rawbytes_found'} += (-s $file_long); $stats{'totalbytes_found'} += (-s $file_long); } # update the live stats window: $statlist->freeze(); $statlist->set_text( 0, 1, $stats{'jpgs_found'} ); $statlist->set_text( 1, 1, $stats{'raws_found'} ); $statlist->set_text( 2, 1, $stats{'totalfiles_found'} ); $statlist->set_text( 3, 1, FormatByteSize( $stats{'jpgbytes_found'} ) ); $statlist->set_text( 4, 1, FormatByteSize( $stats{'rawbytes_found'} ) ); $statlist->set_text( 5, 1, FormatByteSize( $stats{'totalbytes_found'} ) ); $statlist->thaw(); } sub HandleFile { # takes: full path to file to manipulate # long src file (full path) my $file_long = $_[0]; # short file name my @temp = split( /\//, $file_long ); my $file_short = pop(@temp); # ----------------------------------------------- LogMsg("[ For $file_short: ]\n"); #if JPG if ($file_short =~ /\.(jpg|JPG|jpeg|JPEG)$/ ) { HandleJPG($file_long); # update in-memory stats: $stats{'jpgs_proc'}++; $stats{'totalfiles_proc'}++; $stats{'jpgbytes_proc'} += (-s $file_long); $stats{'totalbytes_proc'} += (-s $file_long); } #if RAW elsif ($file_short =~ /\.(crw|CRW|raw|RAW)$/ ) { HandleRAW($file_long); $stats{'raws_proc'}++; $stats{'totalfiles_proc'}++; $stats{'rawbytes_proc'} += (-s $file_long); $stats{'totalbytes_proc'} += (-s $file_long); } # if other file type else { LogMsg("Skipped\n"); } LogMsg("\n"); # update the live stats window: $statlist->freeze(); $statlist->set_text( 0, 2, $stats{'jpgs_trans'} ); $statlist->set_text( 1, 2, $stats{'raws_trans'} ); $statlist->set_text( 2, 2, $stats{'totalfiles_trans'} ); $statlist->set_text( 3, 2, FormatByteSize( $stats{'jpgbytes_trans'} ) ); $statlist->set_text( 4, 2, FormatByteSize( $stats{'rawbytes_trans'} ) ); $statlist->set_text( 5, 2, FormatByteSize( $stats{'totalbytes_trans'} ) ); $statlist->set_text( 0, 3, $stats{'jpgs_proc'} ); $statlist->set_text( 1, 3, $stats{'raws_proc'} ); $statlist->set_text( 2, 3, $stats{'totalfiles_proc'} ); $statlist->set_text( 3, 3, FormatByteSize( $stats{'jpgbytes_proc'} ) ); $statlist->set_text( 4, 3, FormatByteSize( $stats{'rawbytes_proc'} ) ); $statlist->set_text( 5, 3, FormatByteSize( $stats{'totalbytes_proc'} ) ); $statlist->thaw(); # update progress bar my $percent = ($stats{'totalfiles_proc'} / $stats{'totalfiles_found'}) if ($stats{'totalfiles_found'}); $progbar->update( $percent ); # force redraw Gtk->main_iteration while ( Gtk->events_pending ); } sub HandleJPG { # takes: full path to file # long destination file (full path) my $file_long = $_[0]; # apply JPG transformations if ($configs{'jpg_rotate'} == $true) { RotateImg( $file_long, GetRotationDegrees($file_long) ); } ApplyMogrify($file_long); # TODO: update stats } sub HandleRAW { # takes: full path to file # long destination file (full path) my $file_long = $_[0]; # short file name my @temp = split( /\//, $file_long ); my $file_short = pop(@temp); # full path to new THM file my $file_new_thm = $file_long; $file_new_thm =~ s/\.(crw|CRW|raw|RAW)$/.thm/; # full path to new extracted JPG file my $file_new_xjpg = $file_long . '.jpg'; # full path to new JPG name (for RAWs) my $file_new_jpg = $file_long; $file_new_jpg =~ s/\.(crw|CRW|raw|RAW)$/.jpg/; # full path to new TIFF name (for RAWs) my $file_new_tiff = $file_long; $file_new_tiff =~ s/\.(crw|CRW|raw|RAW)$/-24bit.tif/; # full path to new TIFF 48bit name (for RAWs) my $file_new_tiff48 = $file_long; $file_new_tiff48 =~ s/\.(crw|CRW|raw|RAW)$/-48bit.tif/; # full path to new PPM name (for RAWs) my $file_new_ppm = $file_long; $file_new_ppm =~ s/\.(crw|CRW|raw|RAW)$/.ppm/; # full path to new 48bit PPM name (for RAWs) my $file_new_ppm48 = $file_long; $file_new_ppm48 =~ s/\.(crw|CRW|raw|RAW)$/-48bit.ppm/; # full path to new PSD name (for RAWs) my $file_new_psd = $file_long; $file_new_psd =~ s/\.(crw|CRW|raw|RAW)$/.psd/; # ----------------------------------------------- # extract JPG from RAW? if ( $configs{'raw_xjpg'} == $true ) { # extract: LogMsg("Extracting RAW-embedded JPEG ...\n"); my $sys_str = $configs{'parseraw_loc'} . ' ' . $file_long; system($sys_str); # transplant EXIF from thumbnail LogMsg("Transplanting EXIF info from thumbnail...\n"); system( $configs{'jhead_loc'} . " -te $file_new_thm $file_new_xjpg"); # rotate and mogrify: if ($configs{'jpg_rotate'} == $true) { RotateImg( $file_new_xjpg, GetRotationDegrees($file_new_thm) ); } ApplyMogrify($file_new_xjpg); } # do we need to decode RAW? if ($configs{'RAW->PPM48'} || $configs{'RAW->TIFF48'} || $configs{'RAW->TIFF'} || $configs{'RAW->PPM'} || $configs{'RAW->JPG'} || $configs{'RAW->PSD'}) { # hooks my $wb_hook = ' -w ' if $configs{'raw_use_wb'}; my $brightness_hook = ' -b ' . $configs{'raw_brightness'} . ' '; my $norm_hook = ' -normalize ' if $configs{'raw_normalize'}; # Default: decode RAW -> PPM48 LogMsg("Converting RAW to 48bit PPM ...\n"); system($configs{'dcraw_loc'} . " -4 $wb_hook $brightness_hook -o $file_new_ppm48 $file_long"); # rotate image and Mogrify before converting to other formats if ($configs{'jpg_rotate'} == $true) { RotateImg( $file_new_ppm48, GetRotationDegrees($file_new_thm) ); } ApplyMogrify($file_new_ppm48); # convert to PSD? if ($configs{'RAW->PSD'} == $true) { LogMsg("Converting RAW to 48bit PSD ...\n"); system($configs{'dcraw_loc'} . " -3 $wb_hook $brightness_hook $file_long"); } # convert to TIFF 48bit? if ($configs{'RAW->TIFF48'} == $true) { LogMsg("Converting 48bit PPM to 48bit TIFF ...\n"); system($configs{'convert_loc'} . " $norm_hook $file_new_ppm48 $file_new_tiff48"); } # convert to TIFF 24bit? if ($configs{'RAW->TIFF'} == $true) { LogMsg("Converting 48bit PPM to 24bit TIFF ...\n"); system($configs{'convert_loc'} . " -depth 24 $norm_hook $file_new_tiff48 $file_new_tiff"); } # convert to PPM 24bit? if ($configs{'RAW->PPM'} == $true) { LogMsg("Converting 48bit PPM to 24bit PPM ...\n"); system($configs{'convert_loc'} . " -depth 24 $norm_hook $file_new_ppm48 $file_new_ppm"); } # convert to JPG? if ($configs{'RAW->JPG'} == $true) { LogMsg("Converting 48bit PPM to JPEG ...\n"); system($configs{'convert_loc'} . " -quality 100 $norm_hook $file_new_ppm48 $file_new_jpg"); # Transplant EXIF from thumbnail LogMsg("Transplanting EXIF info from thumbnail...\n"); system( $configs{'jhead_loc'} . " -te $file_new_thm $file_new_jpg"); #ApplyMogrify($file_new_jpg); } # keep PPM? unless ( $configs{'RAW->PPM48'} == $true ) { system("rm $file_new_ppm48"); } # remove working RAW+THM files if preserve_orig if ($configs{'preserve_orig'} == $true) { system("rm $file_long"); system("rm $file_new_thm"); LogMsg("Working RAW and .THM files deleted.\n"); } # TODO: update stats } } sub Exclude { #returns 1 if exclude file was matched, 0 if not. foreach my $i (@excludefiles) { if ($i eq $_[0]) {return 1} } return 0; } sub GetTimeStamp { return FormatTime( GetTime() ); } sub FormatTime { # gets a time and returns it in the proper string format # takes: $date_format type (a number) # $time (a stringified version of the 7 elements time list) # returns: string formated output ("April 15th 2002") # the time format to use when storing dates is: (just a space-seperated list returned by the time function): # seconds-minutes-hours-dayofmonth-month-year-dayofweek-dayofyear-daylightsavings? #my $date_format = $_[0]; my @rawtime = split( " ", $_[0] ); my $formatdate; my $adjustedDOM = $rawtime[4]+1; my $adjustedyear = $rawtime[5]+1900; $formatdate = sprintf("%02d-%02d-%d,%02d:%02d:%02d", $adjustedDOM, $rawtime[3], $adjustedyear, $rawtime[2], $rawtime[1], $rawtime[0]); return($formatdate); } sub GetTime { # just gets the time and returns in a space seperated string of 7 elements my $rawdate = join( " ", gmtime() ); return($rawdate); } sub LogMsg { # prints messages out to the right place. # takes: # [string MESSAGE], # [optional int FLAG] 0=normal text, 1=important(bold), 2=debug, 3=error my ($string,$flag) = @_; if ( !defined($flag) ) { $flag = 0; } $msgbox->insert( undef, undef, undef, $string ); print $string; } sub FormatByteSize { #takes an int (number of bytes); my $dispnum; #convert to GB if($_[0] > 999999999) { my $thisnum = $_[0] / 1000000000; #/ $dispnum = sprintf("%.2f GB", $thisnum); } #convert to MB elsif($_[0] > 999999) { my $thisnum = $_[0] / 1000000; #/ $dispnum = sprintf("%.2f MB", $thisnum); } #convert to KB elsif($_[0] > 999) { my $thisnum = $_[0] / 1000; #/ $dispnum = sprintf("%.2f KB", $thisnum); } #convert to B else { $dispnum = sprintf("%d", $_[0]); } return $dispnum; } sub SaveConfigChanges { $configs{'raw_xjpg'} = $conf_raw_xjpg->active; $configs{'RAW->JPG'} = $conf_raw_conv_jpg->active; $configs{'RAW->PPM'} = $conf_raw_conv_ppm->active; $configs{'RAW->PPM48'} = $conf_raw_conv_ppm48->active; $configs{'RAW->TIFF'} = $conf_raw_conv_tiff->active; $configs{'RAW->TIFF48'} = $conf_raw_conv_tiff48->active; $configs{'RAW->PSD'} = $conf_raw_conv_psd->active; $configs{'raw_brightness'} = sprintf("%.2f", $conf_raw_bright->get_adjustment->value); $configs{'raw_use_wb'} = $conf_raw_wb->active; $configs{'raw_normalize'} = $conf_raw_normalize->active; $configs{'jpg_rotate'} = $conf_jpg_rotate->active; $configs{'jpg_sharpen'} = $conf_jpg_sharpen->active; $configs{'jpg_sharpen_amount'} = sprintf("%.2f", $conf_jpg_sharpen_amount->get_adjustment->value); $configs{'jpg_resample'} = $conf_jpg_resample->active; $configs{'jpg_resample_quality'} = sprintf("%d", $conf_jpg_resample_q->get_adjustment->value); $configs{'preserve_orig'} = $conf_preserve_orig->active; $configs{'zip_orig'} = $conf_zip_orig->active; $configs{'del_src_files'} = $conf_del_src->active; $configs{'per_session_dump'} = $conf_per_session_dump->active; $configs{'src_dir'} = $conf_src_dir->get_text(); $configs{'dest_dir'} = $conf_dest_dir->get_text(); $configs{'jhead_loc'} = $conf_jhead_loc->get_text(); $configs{'mogrify_loc'} = $conf_mogrify_loc->get_text(); $configs{'convert_loc'} = $conf_convert_loc->get_text(); $configs{'dcraw_loc'} = $conf_dcraw_loc->get_text(); $configs{'exiftags_loc'} = $conf_exiftags_loc->get_text(); $configs{'parseraw_loc'} = $conf_parseraw_loc->get_text(); } sub LoadConfigFile { my $path = $FindBin::RealBin; open (FILE, "$path/$config_file_name") || return 0; while() { chomp; # newlines s/#.*//; # comments s/^\s+//; #leading space s/\s+$//; # trailing space next unless length; # anything left? my ($x,$y) = split(/\s*=\s*/, $_, 2); $configs{$x} = $y; } close FILE; } sub DumpConfigFile { my $path = $FindBin::RealBin; open (FILE, ">$path/$config_file_name"); print FILE "# PHOTOBASE CONFIGURATION FILE - edit by hand if needed, just don't break anything\n\n"; foreach my $key (keys %configs) { print FILE $key . '=' . $configs{$key} . "\n\n"; } close FILE; } sub GetRotationDegrees { # figures the rotation degree to rotate an image, based on EXIF data. # takes: full pathname of the file that contains the EXIF data. (the supplied file will not be rotated) # returns: degrees my $file = $_[0]; unless (-e $file) { return 0; } # exist? my $sys_str = $configs{'exiftags_loc'} . ' ' . $file; my $exifdata = `$sys_str`; my $rot = 0; # rotation *needed* if ($exifdata =~ 'Top, Left-Hand' || $exifdata =~ 'Left-Hand, Top') { return 0; } elsif ($exifdata =~ 'Top, Right-Hand' || $exifdata =~ 'Right-Hand, Top') { return 90; } elsif ($exifdata =~ 'Bottom, Right-Hand' || $exifdata =~ 'Right-Hand, Bottom') { return 180; } elsif ($exifdata =~ 'Bottom, Left-Hand' || $exifdata =~ 'Left-Hand, Bottom') { return 270; } } sub RotateImg { # rotates an image if needed. # takes: full pathname of the file, degrees to rotate my $file = $_[0]; my $rot = $_[1]; unless (-e $file) { return 0; } # exist? if ($rot == 0) { return 0; } # no rotation my $sys_str = ''; # jpg images need a jhead wrapper to preserve exif data if ( IsJPG($file) ) { $sys_str .= $configs{'jhead_loc'} . ' -cmd ' . '"'; } # mogrify location $sys_str .= $configs{'mogrify_loc'}; #rotation $sys_str .= ' -rotate ' . " $rot "; # file name $sys_str .= " $file"; # jpg images need a jhead wrapper to preserve exif data if ( IsJPG($file) ) { $sys_str .= "\" $file"; } LogMsg("Rotating ... "); system($sys_str); LogMsg("done.\n"); } sub ApplyMogrify { # applies sharpening, etc, all in one whack. # takes: full pathname of the file my $file = $_[0]; # start making the command string if needed if ($configs{'jpg_resample'} || $configs{'jpg_sharpen'}) { LogMsg("Applying selected conversion options..."); my $sys_str = ''; # jpg images need a jhead wrapper to preserve exif data if ( IsJPG($file) ) { $sys_str .= $configs{'jhead_loc'} . ' -cmd ' . '"'; } # mogrify location $sys_str .= $configs{'mogrify_loc'}; # resampling if ( IsJPG($file) && $configs{'jpg_resample'} == $true) { $sys_str .= ' -quality ' . sprintf("%d",$configs{'jpg_resample_quality'}); } # sharpening if ($configs{'jpg_sharpen'} == $true) { $sys_str .= ' -sharpen ' . sprintf("%.2f",$configs{'jpg_sharpen_amount'}); } # file name $sys_str .= " $file"; # jpg images need a jhead wrapper to preserve exif data if ( IsJPG($file) ) { $sys_str .= "\" $file"; } #LogMsg($sys_str . "\n"); system("$sys_str"); LogMsg(" done.\n"); } } sub IsJPG { # tells you if a file is a jpg or not # takes: pathname to file # returns: 1 or yes, 0 for no. if ( $_[0] =~ /\.(jpg|JPG|jpeg|JPEG)$/ ) {return 1;} else { return 0;} } sub IsRAW { # tells you if a file is a RAW or not # takes: pathname to file # returns: 1 or yes, 0 for no. if ( $_[0] =~ /\.(crw|CRW|raw|RAW)$/ ) {return 1;} else { return 0;} } sub IsTHM { # tells you if a file is a .thm or not # takes: pathname to file # returns: 1 or yes, 0 for no. if ( $_[0] =~ /\.(THM|thm)$/ ) {return 1;} else { return 0;} } sub ResetAllStats { $stats{'jpgs_found'} = 0; $stats{'raws_found'} = 0; $stats{'totalfiles_found'} = 0; $stats{'jpgbytes_found'} = 0; $stats{'rawbytes_found'} = 0; $stats{'totalbytes_found'} = 0; $stats{'jpgs_trans'} = 0; $stats{'raws_trans'} = 0; $stats{'totalfiles_trans'} = 0; $stats{'jpgbytes_trans'} = 0; $stats{'rawbytes_trans'} = 0; $stats{'totalbytes_trans'} = 0; $stats{'jpgs_proc'} = 0; $stats{'raws_proc'} = 0; $stats{'totalfiles_proc'} = 0; $stats{'jpgbytes_proc'} = 0; $stats{'rawbytes_proc'} = 0; $stats{'totalbytes_proc'} = 0; # update the live stats window: $statlist->freeze(); $statlist->set_text( 0, 1, 0 ); $statlist->set_text( 1, 1, 0 ); $statlist->set_text( 2, 1, 0 ); $statlist->set_text( 3, 1, 0 ); $statlist->set_text( 4, 1, 0 ); $statlist->set_text( 5, 1, 0 ); $statlist->set_text( 0, 2, 0 ); $statlist->set_text( 1, 2, 0 ); $statlist->set_text( 2, 2, 0 ); $statlist->set_text( 3, 2, 0 ); $statlist->set_text( 4, 2, 0 ); $statlist->set_text( 5, 2, 0 ); $statlist->set_text( 0, 3, '0'); $statlist->set_text( 1, 3, '0'); $statlist->set_text( 2, 3, '0'); $statlist->set_text( 3, 3, '0'); $statlist->set_text( 4, 3, '0'); $statlist->set_text( 5, 3, '0'); $statlist->thaw(); # update progress bar $progbar->update( 0.0 ); # force redraw Gtk->main_iteration while ( Gtk->events_pending ); } sub ScriptSelfTest { # checks to make sure everything is where it should be before starting # returns 1 on error, 0 on go for launch # source and destination directories get tested in MountFS() unless (-e $configs{'jhead_loc'}) { LogMsg( $configs{'jhead_loc'} . " could not be found.\n *** ABORTED ***\n"); return 1; } unless (-R $configs{'jhead_loc'}) { LogMsg( $configs{'jhead_loc'} . " is not readable (check your file permissions).\n *** ABORTED ***\n"); return 1; } unless (-X $configs{'jhead_loc'}) { LogMsg( $configs{'jhead_loc'} . " is not executable (check your file permissions)\n *** ABORTED ***\n"); return 1; } unless (-e $configs{'mogrify_loc'}) { LogMsg( $configs{'mogrify_loc'} . " could not be found.\n *** ABORTED ***\n"); return 1; } unless (-R $configs{'mogrify_loc'}) { LogMsg( $configs{'mogrify_loc'} . " is not readable (check your file permissions).\n *** ABORTED ***\n"); return 1; } unless (-X $configs{'mogrify_loc'}) { LogMsg( $configs{'mogrify_loc'} . " is not executable (check your file permissions)\n *** ABORTED ***\n"); return 1; } unless (-e $configs{'convert_loc'}) { LogMsg( $configs{'convert_loc'} . " could not be found.\n *** ABORTED ***\n"); return 1; } unless (-R $configs{'convert_loc'}) { LogMsg( $configs{'convert_loc'} . " is not readable (check your file permissions).\n *** ABORTED ***\n"); return 1; } unless (-X $configs{'convert_loc'}) { LogMsg( $configs{'convert_loc'} . " is not executable (check your file permissions)\n *** ABORTED ***\n"); return 1; } unless (-e $configs{'exiftags_loc'}) { LogMsg( $configs{'exiftags_loc'} . " could not be found.\n *** ABORTED ***\n"); return 1; } unless (-R $configs{'exiftags_loc'}) { LogMsg( $configs{'exiftags_loc'} . " is not readable (check your file permissions).\n *** ABORTED ***\n"); return 1; } unless (-X $configs{'exiftags_loc'}) { LogMsg( $configs{'exiftags_loc'} . " is not executable (check your file permissions)\n *** ABORTED ***\n"); return 1; } if ( $configs{'RAW->JPG'} || $configs{'RAW->PPM'} || $configs{'RAW->TIFF'} || $configs{'RAW->PPM48'} || $configs{'RAW->PSD'} ) { unless (-e $configs{'dcraw_loc'}) { LogMsg( $configs{'dcraw_loc'} . " could not be found.\n *** ABORTED ***\n"); return 1; } unless (-R $configs{'dcraw_loc'}) { LogMsg( $configs{'dcraw_loc'} . " is not readable (check your file permissions).\n *** ABORTED ***\n"); return 1; } unless (-X $configs{'dcraw_loc'}) { LogMsg( $configs{'dcraw_loc'} . " is not executable (check your file permissions)\n *** ABORTED ***\n"); return 1; } } if ( $configs{'raw_xjpg'} ) { unless (-e $configs{'parseraw_loc'}) { LogMsg( $configs{'parseraw_loc'} . " could not be found.\n *** ABORTED ***\n"); return 1; } unless (-R $configs{'parseraw_loc'}) { LogMsg( $configs{'parseraw_loc'} . " is not readable (check your file permissions).\n *** ABORTED ***\n"); return 1; } unless (-X $configs{'parseraw_loc'}) { LogMsg( $configs{'parseraw_loc'} . " is not executable (check your file permissions)\n *** ABORTED ***\n"); return 1; } } if ( $configs{'zip_orig'} == $true ) { unless (-e '/usr/bin/zip') { LogMsg( '/usr/bin/zip' . " could not be found.\n *** ABORTED ***\n"); return 1; } unless (-R '/usr/bin/zip') { LogMsg( '/usr/bin/zip' . " is not readable (check your file permissions).\n *** ABORTED ***\n"); return 1; } unless (-X '/usr/bin/zip') { LogMsg( '/usr/bin/zip' . " is not executable (check your file permissions)\n *** ABORTED ***\n"); return 1; } } return 0; } #===== SUBROUTINES =========================/\===============================