aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rwxr-xr-xmusic/export.sh57
-rwxr-xr-xmusic/import.sh75
-rwxr-xr-xpictures/import.sh125
4 files changed, 261 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4657716
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+Scripts
+=======
+
+Misc scripts that I use for things.
diff --git a/music/export.sh b/music/export.sh
new file mode 100755
index 0000000..6f53793
--- /dev/null
+++ b/music/export.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+set -eu -o pipefail
+
+# I use this script to export my flac collection to a portable device,
+# which happens to require smaller embedded album art.
+
+MUSIC_DIR="/nfs/media/music/flac"
+ARTSIZE=${ARTSIZE:-320}
+
+sanitize(){
+ echo "$1" | tr -Cd 'A-Za-z0-9 _-'
+}
+
+if [ $# -ne 1 ]; then
+ echo 'usage: export.sh DST' 1>&2
+ exit 1
+fi
+DEST_DIR="$1"
+
+find "$MUSIC_DIR" -iname '*.flac' -type f -print | while read -r file; do
+ track=$(exiftool -s3 -TrackNumber "$file")
+ title=$(exiftool -s3 -Title "$file")
+ album=$(exiftool -s3 -Album "$file")
+ artist=$(exiftool -s3 -AlbumArtist "$file")
+ artist=${artist:-$(exiftool -s3 -Artist "$file")}
+
+ if [ -z "$artist" ]; then
+ echo "$file has no artist metadata, skipping" 1>&2
+ continue
+ fi
+
+ if [ -z "$album" ]; then
+ echo "$file has no album metadata, skipping" 1>&2
+ continue
+ fi
+
+ if [ -z "$title" ]; then
+ echo "$file has no title metadata, skipping" 1>&2
+ continue
+ fi
+
+ if [ -z "$track" ]; then
+ echo "$file has no track metadata, skipping" 1>&2
+ continue
+ fi
+
+ subpath="$(sanitize "$artist")/$(sanitize "$album")"
+ basename="$(printf '%03d' "$(expr "$track" + 0)") $(sanitize "$title")"
+
+ mkdir -p "${DEST_DIR}/${subpath}"
+
+ if [ ! -e "${DEST_DIR}/${subpath}/${basename}.flac" ]; then
+ echo "$artist / $album / $title"
+ ffmpeg -n -nostdin -loglevel error -i "$file" -map 0:a:0 -map 0:v:0 -filter:v "scale=w=${ARTSIZE}:h=${ARTSIZE},format=yuvj420p" -c:v mjpeg -c:a copy "${DEST_DIR}/${subpath}/${basename}.flac"
+ fi
+done
diff --git a/music/import.sh b/music/import.sh
new file mode 100755
index 0000000..2d5f5e5
--- /dev/null
+++ b/music/import.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+set -eu -o pipefail
+
+MUSIC_DIR=/nfs/media/music
+MP3_BITRATE=320k
+
+# Searches the given argument paths for .flac files, and copies them to
+# $MUSIC_DIR with the follow hierarchy:
+#
+# flac/${artist}/${album}/${tracknum} ${trackname}.flac
+#
+# It also transcodes the FLACs to MP3s and stores them in a similar way:
+#
+# mp3/${artist}/${album}/${tracknum} ${trackname}.flac
+#
+# The metadata is taken from the id3 tags in the flac file, santized to contain
+# only ASCII characters.
+
+sanitize(){
+ echo "$1" | tr -Cd 'A-Za-z0-9 _-'
+}
+
+if [ $# -ne 0 ] ; then
+ echo 'usage: import.sh PATH...' 1>&2
+ exit 1
+fi
+
+find "$@" -iname '*.flac' -type f -print | while read -r file; do
+ track=$(exiftool -s3 -TrackNumber "$file")
+ title=$(exiftool -s3 -Title "$file")
+ album=$(exiftool -s3 -Album "$file")
+ artist=$(exiftool -s3 -AlbumArtist "$file")
+ artist=${artist:-$(exiftool -s3 -Artist "$file")}
+
+ if [ -z "$artist" ]; then
+ echo "$file has no artist metadata, skipping" 1>&2
+ continue
+ fi
+
+ if [ -z "$album" ]; then
+ echo "$file has no album metadata, skipping" 1>&2
+ continue
+ fi
+
+ if [ -z "$title" ]; then
+ echo "$file has no title metadata, skipping" 1>&2
+ continue
+ fi
+
+ if [ -z "$track" ]; then
+ echo "$file has no track metadata, skipping" 1>&2
+ continue
+ fi
+
+ subpath="$(sanitize "$artist")/$(sanitize "$album")"
+ basename="$(printf '%03d' "$(expr "$track" + 0)") $(sanitize "$title")"
+
+ # flac
+ mkdir -p "${MUSIC_DIR}/flac/${subpath}"
+
+ cp "$file" "${MUSIC_DIR}/flac/${subpath}/${basename}.flac"
+ echo "flac :: $artist / $album / $title"
+
+ if ! [ -f "${MUSIC_DIR}/flac/${subpath}/folder.jpg" ]; then
+ ffmpeg -y -nostdin -loglevel error -i "$file" -an -c:v copy "${MUSIC_DIR}/flac/${subpath}/folder.jpg"
+ echo "aart :: $artist / $album"
+ fi
+
+ # mp3
+ mkdir -p "${MUSIC_DIR}/mp3/${subpath}"
+
+ ffmpeg -y -nostdin -loglevel error -i "$file" -ab "${MP3_BITRATE}" "${MUSIC_DIR}/mp3/${subpath}/${basename}.mp3"
+ echo " mp3 :: $artist / $album / $title"
+done
diff --git a/pictures/import.sh b/pictures/import.sh
new file mode 100755
index 0000000..3ef850f
--- /dev/null
+++ b/pictures/import.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+
+set -Eeu -o pipefail
+
+# Imports photos from a given location(s) to PHOTO_DIR, organized by calendar
+# year and with a filename matching the date the photo/video was taken.
+#
+# The date is determined by the following, in order of precedence:
+# 1. exif data from the image/video
+# 2. regex matching for a date-like string inside the filename
+# 3. the modification time of the souce file
+
+DATE_FORMAT='%Y-%m-%d-%H%M%S'
+
+DRY_RUN=0
+
+usage() {
+ echo 'usage: import.sh -cp|-mv PHOTO_DIR PATH...' 1>&2
+ exit 1
+}
+
+if (( $# < 3 )); then
+ usage
+fi
+
+if [ "$1" == -cp ]; then
+ MOVE_COMMAND='cp --preserve=timestamps'; shift
+elif [ "$1" == -mv ]; then
+ MOVE_COMMAND=mv; shift
+else
+ usage
+fi
+
+PHOTO_DIR=$1; shift
+
+date_from_exif() {
+ local date=$(exiftool -ee -quiet -tab -dateformat "$DATE_FORMAT" -json -DateTimeOriginal "$1" | jq --raw-output '.[].DateTimeOriginal.val')
+
+ if [ "$date" == 'null' ]; then
+ date=$(exiftool -ee -quiet -tab -dateformat "$DATE_FORMAT" -json -MediaCreateDate "$1" | jq --raw-output '.[].MediaCreateDate.val')
+ fi
+
+ if [[ "$date" =~ 20[0-9]{2}-[0-1][0-9]-[0-3][0-9]-[0-2][0-9][0-5][0-9][0-5][0-9] ]]; then
+ echo "$date"
+ fi
+}
+
+date_from_filename() {
+ if [[ "$1" =~ (20[0-9]{2})[_-]([0-1][0-9])[_-]([0-3][0-9])[_-]([0-2][0-9])[_-]([0-5][0-9])[_-]([0-5][0-9]) ]]; then
+ echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}${BASH_REMATCH[5]}${BASH_REMATCH[6]}"
+ elif [[ "$1" =~ (20[0-9]{2})[_-]([0-1][0-9])[_-]([0-3][0-9])[_-]([0-2][0-9][0-5][0-9][0-5][0-9]) ]]; then
+ echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}"
+ elif [[ "$1" =~ (20[0-9]{2})([0-1][0-9])([0-9]{2})[_-]([0-2][0-9][0-5][0-9][0-5][0-9]) ]]; then
+ echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}"
+ elif [[ "$1" =~ (20[0-9]{2})([0-1][0-9])([0-9]{2})([0-2][0-9][0-5][0-9][0-5][0-9]) ]]; then
+ echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}"
+ elif [[ "$1" =~ (20[0-9]{2})[_-]([0-1][0-9])[_-]([0-3][0-9]) ]]; then
+ echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-000000"
+ elif [[ "$1" =~ (20[0-9]{2})([0-1][0-9])([0-3][0-9]) ]]; then
+ echo "${BASH_REMATCH[1]}-${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-000000"
+ fi
+}
+
+date_from_mtime() {
+ date -r "$1" "+$DATE_FORMAT"
+}
+
+# Generates a "safe" destination filename. If "foo.jpg" already exists, then this function
+# will return "foo_2.jpg" (or "foo_3.jpg" if "foo_2.jpg" exists...etc)
+safe_filename() {
+ if (( $# > 1 )); then
+ local src=$1 dst=$2
+
+ if [ -d "$dst" ]; then
+ dst="${dst}/$(basename "$src")"
+ fi
+ else
+ local dst=$1
+ fi
+
+ local ext=${dst##*.}
+ local odst=$dst
+ local i=2
+
+ while [ -f "$dst" ]; do
+ dst="${odst%.*}_${i}.${ext}"
+ ((i++))
+ done
+
+ echo "$dst"
+}
+
+safe_mv() {
+ local src=$1 dst=$2
+ dst=$(safe_filename "$src" "$dst")
+
+ if ((DRY_RUN)); then
+ echo "[dry-run] $MOVE_COMMAND $src -> $dst"
+ else
+ $MOVE_COMMAND --no-clobber --verbose "$src" "$dst"
+ fi
+}
+
+
+while IFS='' read -r -d '' file; do
+ date=$(date_from_exif "$file")
+
+ if [ -z "$date" ]; then
+ date=$(date_from_filename "$file")
+ fi
+
+ if [ -z "$date" ]; then
+ date=$(date_from_mtime "$file")
+ fi
+
+ year=${date:0:4}
+ ext=${file##*.}
+ ext=${ext,,}
+
+ target="${PHOTO_DIR}/${year}/${date}.${ext}"
+
+ mkdir -p "${PHOTO_DIR}/${year}"
+ safe_mv "$file" "$target"
+
+done < <(find "$@" -type f \( -iname '*.jpg' -o -iname '*.png' -o -iname '*.mp4' \) -print0)