Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
unRAID - automatically move photos to array
#!/bin/bash
PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin
## Usage (after configuration):
## 1. Insert camera's memory card into a USB port on your unRAID system
## 2. The system will automatically move (or copy) any images/videos from the memory card to the array
## If jhead was installed, it will automatically rotate images according to the exif data
## 3. Wait for the imperial theme to play, then remove the memory card
## Preparation:
## 1. Install jhead (to automatically rotate photos) using the Nerd Pack plugin
## 2. Install the "Unassigned Devices" plugin
## 3. Use that plugin to set this script to run *in the background* when a memory card is inserted
## 4. Configure variables in this script as described below
## Warning:
## The newperms script has a bug in unRAID 6.1.3 - 6.1.7
## see https://lime-technology.com/forum/index.php?topic=43388.0
## --- BEGIN CONFIGURATION ---
## SET THIS FOR YOUR CAMERAS:
## array of directories under /DCIM/ that contain files you want to move (or copy)
## can contain regex
VALIDDIRS=("/DCIM/[0-9][0-9][0-9]___[0-9][0-9]" "/DCIM/[0-9][0-9][0-9]CANON" "/DCIM/[0-9][0-9][0-9]_FUJI")
## SET THIS FOR YOUR SYSTEM:
## location to move files to. use date command to ensure unique dir
DESTINATION="/mnt/user/Photos/To Be Filed/incoming/$(date +"%Y-%m-%d-%H-%M-%S-%N")/"
## SET THIS FOR YOUR SYSTEM:
## change to "move" when you are confident everything is working
MOVE_OR_COPY="copy"
## set this to 1 and check the syslog for additional debugging info
DEBUG=""
## Available variables:
# AVAIL : available space
# USED : used space
# SIZE : partition size
# SERIAL : disk serial number
# ACTION : if mounting, ADD; if unmounting, REMOVE
# MOUNTPOINT : where the partition is mounted
# FSTYPE : partition filesystem
# LABEL : partition label
# DEVICE : partition device, e.g /dev/sda1
# OWNER : "udev" if executed by UDEV, otherwise "user"
# PROG_NAME : program name of this script
# LOGFILE : log file for this script
log_all() {
log_local "$1"
logger "$PROG_NAME-$1"
}
log_local() {
echo "`date` $PROG_NAME-$1"
echo "`date` $PROG_NAME-$1" >> $LOGFILE
}
log_debug() {
if [ ${DEBUG} ]
then
log_local "$1"
fi
}
beep_imperial() {
for i in $(seq 1 ${1}); do
beep -f 392 -l 450 -r 3 -D 150 -n -f 311.13 -l 400 -D 50 \
-n -f 466.16 -l 100 -D 50 -n -f 392 -l 500 -D 100 \
-n -f 311.13 -l 400 -D 50 -n -f 466.16 -l 100 -D 50 \
-n -f 392 -l 600 -D 600 -n -f 587.33 -l 450 -r 3 -D 150 \
-n -f 622.25 -l 400 -D 50 -n -f 466.16 -l 100 -D 50 \
-n -f 369.99 -l 500 -D 100 -n -f 311.13 -l 400 -D 50 \
-n -f 466.16 -l 100 -D 50 -n -f 392 -l 500 -D 100
sleep 2
done
}
case $ACTION in
'ADD' )
#
# Beep that the device is plugged in.
#
beep -l 200 -f 600 -n -l 200 -f 800
sleep 2
if [ -d $MOUNTPOINT ]
then
if [ "$OWNER" = "udev" ]
then
log_all "Started"
log_debug "Logging to $LOGFILE"
RSYNCFLAG=""
MOVEMSG="copying"
if [ ${MOVE_OR_COPY} == "move" ]
then
RSYNCFLAG=" --remove-source-files "
MOVEMSG="moving"
fi
# only operate on USB disks that contain a /DCIM directory, everything else will simply be mounted
if [ -d "${MOUNTPOINT}/DCIM" ]
then
log_debug "DCIM exists ${MOUNTPOINT}/DCIM"
# loop through all the subdirs in /DCIM looking for dirs defined in VALIDDIRS
for DIR in ${MOUNTPOINT}/DCIM/*; do
if [ -d "${DIR}" ]; then
log_debug "checking ${DIR}"
for element in "${VALIDDIRS[@]}"; do
if [[ ${DIR} =~ ${element} ]]; then
# process this dir
log_local "${MOVEMSG} ${DIR}/ to ${DESTINATION}"
rsync -a ${RSYNCFLAG} "${DIR}/" "${DESTINATION}"
# remove empty directory from memory card
if [ ${MOVE_OR_COPY} == "move" ]; then
rmdir ${DIR}
fi
fi
done
fi
done
# files were moved (or copied). rotate images, fix permissions
if [ -d "${DESTINATION}" ]; then
if [ -e "/usr/bin/jhead" -a -e "/usr/bin/jpegtran" ]; then
log_debug "running jhead on ${DESTINATION}"
jhead -autorot -ft "${DESTINATION}"*.[jJ][pP][gG]
fi
log_debug "fixing permissions on ${DESTINATION}"
newperms "${DESTINATION}"
fi
# sync and unmount USB drive
sync
/usr/local/sbin/rc.unassigned umount $DEVICE
# send notification
beep_imperial 1
/usr/local/emhttp/webGui/scripts/notify -e "unRAID Server Notice" -s "Photo Import" -d "Photo Import completed" -i "normal"
fi # end check for DCIM directory
else
log_all "Photo Import drive not processed, owner is $OWNER"
fi # end check for valid owner
else
log_all "Mountpoint doesn't exist $MOUNTPOINT"
fi # end check for valid mountpoint
;;
'REMOVE' )
#
# Beep that the device is unmounted.
#
beep -l 200 -f 800 -n -l 200 -f 600
log_all "Photo Import drive unmounted, can safely be removed"
;;
esac
@groggu

This comment has been minimized.

Copy link

commented May 29, 2017

Thanks, this was a great help. I've now got my raw images and videos importing directly to UnRaid. From there I can bring them into Lightroom or where ever.

Here's the update I made to support Olympus, Sony (RAW and MP3 Video), and Go Pro

VALIDDIRS=("/MP_ROOT/[0-9][0-9][0-9]ANV01" "/DCIM/[0-9][0-9][0-9]MSDCF" "/DCIM/[0-9][0-9][0-9]OLYMP" "/DCIM/[0-9][0-9][0-9]MEDIA" "/DCIM/[0-9][0-9][0-9]GOPRO")

Suggestion for other users, if you need to support another camera maker, just look at directories on the SD card and modify or add the VALIDDIRS that match your vendor's directory format.

@ljm42

This comment has been minimized.

Copy link
Owner Author

commented Jun 17, 2017

Thanks for the feedback! Wasn't sure if anyone else was using it, although it makes my life a lot easier.

I'm not sure about the directory that starts with '/MP_ROOT/', it seems like the code would skip it since it is hard coded to assume everything will start with /DCIM/. Which camera uses /MP_ROOT/, and do the files actually get copied?

@evenwebb

This comment has been minimized.

Copy link

commented Jan 22, 2019

Thanks for the feedback! Wasn't sure if anyone else was using it, although it makes my life a lot easier.

I'm not sure about the directory that starts with '/MP_ROOT/', it seems like the code would skip it since it is hard coded to assume everything will start with /DCIM/. Which camera uses /MP_ROOT/, and do the files actually get copied?

Hi @ljm42, is it possible to add the Camera Make/Model as a folder in the destination? I don't know much about scripting etc. I try to get by but I would be worried about not correctly error checking it and damaging my files :)

Thank you in advance

@ljm42

This comment has been minimized.

Copy link
Owner Author

commented Feb 6, 2019

Hmm, I don't seem to get notified when someone comments here.

This script only has access to filenames, so it wouldn't have the camera details. As written, it creates a new folder based on the current date/time so you don't have to worry much about it stomping on your existing files.

@ljm42

This comment has been minimized.

Copy link
Owner Author

commented Feb 6, 2019

Oh maybe you mean you don't want to modify the list of VALIDDIRS. Well, the good news is that MOVE_OR_COPY is set to "copy" by default. You can try it out, and only switch it to "move" when you are confident the files are going to the right place.

@Blindside995

This comment has been minimized.

Copy link

commented May 1, 2019

@ljm42
Love your script been using it a while now. Just wanted to clarify a couple of things.
Should I be letting the script mount the SD card or should I have unassigned devices mounting the drive? Currently, I have it set to automount and the only issue I get then is that the user is "user" instead of "udev"
2. evenwebb was saying would it be possible to have folders for different cameras. A parent dir for canon, GoPro, etc. Personally, I haven't been able to come up with a way of doing that without messing things up. But I'm not the best with script work anyway.
3. Anyone that sees this comment and uses Panasonic cameras I got it to work with "/DCIM/[0-9][0-9][0-9]_PANA"
Thank you for the script! Importing pictures from my camera have never been this easy even with the weird permission issue that happens ever now and again.

@Blindside995

This comment has been minimized.

Copy link

commented May 1, 2019

You can ignore #2 you already addressed that it seems. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.