From 9cce3e225daff5ca7215de0a8d2e1d73b1ca9042 Mon Sep 17 00:00:00 2001 From: Cullum Smith Date: Thu, 14 Nov 2024 20:36:44 -0500 Subject: initial commit --- README.md | 4 ++ music/export.sh | 57 ++++++++++++++++++++++++ music/import.sh | 75 ++++++++++++++++++++++++++++++++ pictures/import.sh | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 README.md create mode 100755 music/export.sh create mode 100755 music/import.sh create mode 100755 pictures/import.sh 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) -- cgit v1.2.3