#!/bin/bash - #=============================================================================== # # FILE: do_pictures # # USAGE: ./do_pictures [options] # # DESCRIPTION: Process pictures associated with a show. # # If there are pictures associated with an episode they will # have been found amongst the show media, including unpacking # any archives. Their names will have been written to a file # which is called '.pictures' in the show directory. The files # themselves will have been moved to a directory called # 'uploads' in the show directory. # {{{ --- lengthy details --- # We no longer write files to the previous directory 'hpr' # because we will not make a tar file any more but will upload # directly to the server (elsewhere). We need the names because # we want to process them from the '.pictures' file. Processing # consists of renaming by replacing spaces with underscores and # using 'gm convert' to remove EXIF data. If the picture size is # too large a thumbnail is created, called # '_thumbnail.'. If the notes are plain text (which # will be without links) then we will use `.pictures' as an # ordered list as we place them into the Markdown we create as # TT² macros. The list is reordered by asking the user to # resequence it with a list of reference numbers. # # Also for plain text and Markdown variant notes we create # a file called '.pictures.tt2' which contains the TT² commands # to add the pictures. When editing the notes this list is # shown in another window so the contents can be copied and # pasted. There are two TT² macros, 'pic1' for stand-alone # pictures and 'pic2' for pictures with thumbnails (clicking the # thumbnail causes the larger picture to be opened). # # Running `do_pandoc` on plain text and Markdown variant notes # will assemble the TT² macros from 'pic_definitions.tpl', and # the notes containing the statements in '.pictures.tt2' placed # as required. # # If the notes are not plain text then different paths are # taken: # # Markdown variants: any links will require editing to refer to # any filename changes, and if the picture needs a thumbnail # then this will need to be catered for. There will be no need # for `.pictures', `.pictures.tt2' or 'pic_definitions.tpl' # unless ther are no links already. The notes will be processed # with 'do_pandoc' which will cater for these differences. # # HTML: again, links might need adjusting, and as for Markdown, # no supplementary files will be required. The modified notes # will not be further processed before uploading. # }}} # # OPTIONS: --- # REQUIREMENTS: --- # BUGS: --- # NOTES: --- # AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com # VERSION: 0.2.3 # CREATED: 2020-05-25 13:20:18 # REVISION: 2024-02-22 14:32:03 # #=============================================================================== set -o nounset # Treat unset variables as an error SCRIPT=${0##*/} # DIR=${0%/*} VERSION="0.2.3" STDOUT="/dev/fd/2" # # Load library functions (make_file_list, range_parse, cleanup_temp) # LIB="$HOME/bin/function_lib.sh" [ -e "$LIB" ] || { echo "$SCRIPT: Unable to source functions"; exit 1; } # shellcheck source=/home/cendjm/bin/function_lib.sh source "$LIB" # # Colour codes # define_colours # {{{ -- _usage -- #=== FUNCTION ================================================================ # NAME: _usage # DESCRIPTION: Report usage # PARAMETERS: None # RETURNS: Nothing #=============================================================================== _usage () { cat >$STDOUT <<-endusage Usage: ./${SCRIPT} [-h] [-d] [-D] shownumber Version: $VERSION Processes pictures associated with an HPR show. The script detects any pictures downloaded with an episode, looking for JPEG and PNG. It copies them to a sub-directory called 'hpr' (since that's the name of the directory where where they'll go on the server). As it copies the files the script renames the copies if necessary, mainly by replacing spaces with underscores. The copying process uses 'gm convert' and removes EXIF data along the way. It also generates a thumbnail for each picture if the size warrants it, called 'NAME_thumbnail.ext'. The script also makes a list of the newly named pictures in a file called '.pictures' which is used to refer to them in the notes (by position in the list). The order in which the pictures are processed and the list built up can be controlled by the user by the ordering of the presented enumerated list. A file called '.pictures.tt2' contains the TT² commands to add the pictures. When editing the notes this list is shown in another window so the contents can be copied and pasted. There are two TT² macros, 'pic1' for stand-alone pictures and 'pic2' for pictures with thumbnails (clicking the thumbnail causes the larger picture to be opened). Running 'do_pandoc' on the notes will assemble the TT² macros from 'pic_definitions.tpl', and the notes containing the statements in '.pictures.tt2' which have been placed as required. An 'index.html' file is created to allow the images to be visualised outside the notes. Options: -h Print this help -d Select dry run mode -D Select debug mode (works the same; more output) -s Silent mode; suppress as many output messages as possible Arguments: shownumber Examples ./${SCRIPT} -h ./${SCRIPT} -d 3112 ./${SCRIPT} -D -d 3112 ./${SCRIPT} 3112 ./${SCRIPT} -s 3112 endusage exit } # }}} # {{{ -- _silent -- _dryrun -- _DEBUG -- #=== FUNCTION ================================================================ # NAME: _silent # DESCRIPTION: Output a message unless we're being silent # PARAMETERS: List of messages # RETURNS: Nothing #=============================================================================== _silent () { [ "$SILENT" == 1 ] && return for msg in "$@"; do printf '%s\n' "$msg" done } #=== FUNCTION ================================================================ # NAME: _dryrun # DESCRIPTION: Output a dry run message # PARAMETERS: List of messages # RETURNS: Nothing #=============================================================================== _dryrun () { local prefix="Dry run: " for msg in "$@"; do printf '%9s%s\n' "$prefix" "$msg" prefix= done } #=== FUNCTION ================================================================ # NAME: _DEBUG # DESCRIPTION: Writes one or more message lines if in DEBUG mode # PARAMETERS: List of messages # RETURNS: Nothing #=============================================================================== _DEBUG () { [ "$DEBUG" == 0 ] && return for msg in "$@"; do printf 'D> %s\n' "$msg" done } # }}} #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # Debug mode, default off # DEBUG=0 # # Option defaults # DRYRUN=0 # not dry run mode by default SILENT=0 # not silent by default # # Process options # while getopts :hdDs opt do case "${opt}" in h) _usage;; d) DRYRUN=1;; D) DEBUG=1;; s) SILENT=1;; ?) echo "$SCRIPT: Invalid option; aborting"; exit 1;; esac done shift $((OPTIND - 1)) # # Check there's an argument after removing any options. Abort if not # if [[ $# -ne 1 ]]; then echo "${red}Missing shownumber argument${reset}" _usage fi # # Paths to files # BASEDIR="$HOME/HPR/Show_Submission" SHOWDIR="$BASEDIR/shownotes/hpr${1}" # RAWFILE="$SHOWDIR/shownotes.txt" JSONFILE="$SHOWDIR/shownotes.json" PICDIR="$SHOWDIR/uploads" # INDEXFILE="$PICDIR/index.html" FORMAT="$SHOWDIR/.format" ASSETS="$SHOWDIR/.assets" # Created in parse_JSON PICLIST="$SHOWDIR/.pictures" # Created in parse_JSON STATUS="$SHOWDIR/.status" # ---- # Files to be used by the TT² macros that build Markdown from plain # text notes. # ---- # MANIFEST contains a list of pictures and thumbnails, with blank lines if no # thumbnail. MANIFEST="$SHOWDIR/.pictures.mf" # PICTPL contains TT² macro calls which can be cut and pasted into the plain # text notes PICTPL="$SHOWDIR/.pictures.tt2" # ---- # # Constants # TNSIZE=800 # Thumbnail size TNW=$TNSIZE # Maximum width TNH=$((TNSIZE*2)) # Maximum height # # Check the show directory exists # if [[ ! -d $SHOWDIR ]]; then echo "$SCRIPT: ${red}Directory $SHOWDIR not found${reset}" exit 1 fi # # Make temporary files and set traps to delete them. # # TMP1 - HTML header # TMP2 - HTML footer # TMP3 - call to TT² macro 'pic1' (make a plain picture link) # TMP4 - call to TT² macro 'pic2' (make a thumbnail link) # TMP1=$(mktemp) || { echo "$SCRIPT: ${red}creation of temporary file failed!${reset}" exit 1 } TMP2=$(mktemp) || { echo "$SCRIPT: ${red}creation of temporary file failed!${reset}" exit 1 } TMP3=$(mktemp) || { echo "$SCRIPT: ${red}creation of temporary file failed!${reset}" exit 1 } TMP4=$(mktemp) || { echo "$SCRIPT: ${red}creation of temporary file failed!${reset}" exit 1 } trap 'cleanup_temp $TMP1 $TMP2 $TMP3 $TMP4' SIGHUP SIGINT SIGPIPE SIGTERM EXIT # # Parse these fields from the raw input file (shownotes.txt) into Bash # variables so we can make an index.html later. # # ---- # NOTE: We're moving to the JSON file for show info. This could be parsed with # 'jq'. This filter seems reasonably close to our needs: # # command=$(jq -r '@sh "hostname=\(.host.Host_Name) title=\(.episode.Title) summary=\(.episode.Summary)"' $RAWFILE) # eval ${command:1:-1} # # The '@sh' format thing ensures the output is OK for the shell. # It seems 'jq' quotes the returned text (it's a string) which 'eval' doesn't # like, but giving 'eval' the string without the quotes (${command:1:-1}) # makes this work. # Update: adding the '-r' option removes the enclosing double quotes # Use the '-f FILE' option to read the filter from a file. # ---- # # Title='' Summary='' Host_Name='' # eval \ # "$(sed -n '/^\(Title\|Summary\|Host_Name\):/{s/^\([^:]\+\):\t/\1="/;s/$/"/;p}' "$RAWFILE")" # Title='' Summary='' Host_Name='' jqprog="@sh \"Host_Name=\(.host.Host_Name) Title=\(.episode.Title) Summary=\(.episode.Summary)\"" command=$(jq -r "$jqprog" "$JSONFILE") eval "${command}" # # Make two HTML templates and two TT² templates. We do this in dry-run mode # too then throw them away! # # {{{ cat > "$TMP1" < $Title (HPR Show $1)

$Title (HPR Show $1)

$Summary

$Host_Name


Index

    ENDHTML1 cat > "$TMP2" <
ENDHTML2 cat > "$TMP3" <<'ENDPIC1' [% row = piclist.$ind -%] [% pic1("$prefix",'*Title*',"$row.file") -%] ENDPIC1 cat > "$TMP4" <<'ENDPIC2' [% row = piclist.$ind -%] [% pic2("$prefix",'*Title*',"$row.thumb","$row.file") -%] ENDPIC2 # }}} # # Read the previously created list of pictures into array 'pix' # if [[ -e $PICLIST ]]; then declare -a pix mapfile -t pix < "$PICLIST" else echo "$SCRIPT: ${red}No pictures found!${reset}" echo "$SCRIPT: ${red}Show has no $PICLIST file!${reset}" exit 1 fi _DEBUG 'Pictures:' "${pix[@]}" # # The assets directory doesn't exist - something's wrong # if [[ ! -d $PICDIR ]]; then echo "$SCRIPT: ${red}No picture/asset directory found!${reset}" echo "$SCRIPT: ${red}Expected $PICDIR${reset}" exit 1 fi # # Look for the show note format file # # Formats offered by the web form on the HPR site: # # Keyword Explanation # ------- ----------- # plain_text Plain text # html5 HTML5 (preferred) # markdown_standard Markdown (standard) # Markdown_GitHub Markdown (GitHub flavoured) # Markdown_Pandoc Markdown (Pandoc flavoured) # restructured_text RestructuredText # txt2tags txt2tags # NOTEFORMAT= if [[ -e $FORMAT ]]; then NOTEFORMAT="$(cat "$FORMAT")" else echo "$SCRIPT: ${red}No format file found!${reset}" echo "$SCRIPT: ${red}Expected $FORMAT${reset}" exit 1 fi # # Regex to match 'plain_text' or any Markdown variant # FMTRE='^(plain_text|[Mm]arkdown_)' _DEBUG "Format: $NOTEFORMAT" # ---- # Check whether we're trying to run this script a second time. It will # potentially try and make thumbnails of thumbnails if we do! # ---- if [[ -e $MANIFEST || -e $PICTPL || $(grep -c pictures "$STATUS") -gt 0 ]]; then echo "$SCRIPT: ${red}This doesn't seem to be the first run of this script!${reset}" echo "$SCRIPT: ${yellow}Delete $MANIFEST, $PICTPL and any thumbnails " \ "and run it again - if you are certain about this!${reset}" | fmt -tu echo "$SCRIPT: ${red}Exiting...${reset}" exit 1 fi # # Start the TT² template and order the files (only for plain text and Markdown # variant shows) # declare -a opix if [[ $NOTEFORMAT =~ $FMTRE ]]; then if [[ $DRYRUN -eq 0 ]]; then echo "[%# Place at the top of the show notes -%]" > "$PICTPL" echo -e "[% ind = 0 -%]\n" >> "$PICTPL" else _dryrun "would have initialised $PICTPL" fi # # Get the order of the picture files, assuming they aren't in a usable sort # order, and store them in 'opix'. The user can just type 1-10 (or whatever # the count is) to use them in the presented order. The default prompt # offers such an order for speed. They can abort the script at this point # if they type CTRL-D. # _silent "${blue}Building ordered picture list${reset}" " " if ! make_file_list pix opix; then echo "Script aborted" exit 1 fi else # # Fill the 'opix' array, but we don't care about ordering # mapfile -t -d ' ' opix < <(echo -n "${pix[@]}") fi _DEBUG '(Possibly) re-ordered pictures:' "${opix[@]}" # # Convert the original pictures and make thumbnails if needed. Thumbnails get # a name in the form 'NAME_thumbnail.EXT'. We record any thumbnails we create. # In plain text mode we write details to a new file called '.pictures.mf' # which is to be a manifest used by 'do_pandoc' to simplify the addition of # Markdown picture and thumbnail links to the notes. # pcount=1 for pic in "${opix[@]}"; do # # The extension and name of the file we'll convert the original picture # into. No path here. We replace internal spaces with underscores. # NOTE: Space → underscore conversion has been done in parse_JSON # ext="${pic#*.}" newpic="${pic##*/}" # newpic="${newpic// /_}" # # What is this picture and does it need a thumbnail? Collect the type, # width and height. # if [[ -e "$PICDIR/$pic" ]]; then geo=$(gm identify -format "%m:%w:%h/" "$PICDIR/$pic") RES=$? [ $RES -ne 0 ] && { echo "Aborting!"; exit $RES; } fi # # Remove repetitions for GIFs and extract the type # geo="${geo%%/*}" type="${geo%%:*}" # # Special actions for GIFs, only needed if the notes are plain text or # a Markdown variant # if [[ $type == 'GIF' ]]; then echo "GIF detected" # Cater for plain text and Markdown variant format notes if [[ $NOTEFORMAT =~ $FMTRE ]]; then if [[ $DRYRUN -eq 0 ]]; then echo "$PICDIR/$newpic" >> "$MANIFEST" echo "" >> "$MANIFEST" if [[ $pcount -gt 1 ]]; then echo "[% ind = ind + 1 -%]" >> "$PICTPL" fi # Add 'pic1' macro call cat "$TMP3" >> "$PICTPL" else _dryrun "Would have added ${yellow}$PICDIR/$newpic${reset} to" \ "${yellow}$MANIFEST${reset}" _dryrun "would have added a blank line to ${yellow}$MANIFEST${reset}" _dryrun "Would have added the ${yellow}pic1${reset} macro to" \ "${yellow}$PICTPL${reset}" fi fi # # Nothing else needs to be done, so skip the rest of the loop # _silent "No image processing performed for $type file $pic" echo "----" ((pcount++)) continue else # # A simple image, get its size # geo="${geo#*:}" pw="${geo%:*}" ph="${geo#*:}" if [[ $pw -gt $TNW || $ph -gt $TNH ]]; then THUMB=1 tn="${newpic%.*}_thumbnail.$ext" else THUMB=0 tn= fi fi # # Include thumbnails or omit them # if [[ $THUMB -eq 1 ]]; then _DEBUG 'Thumbnail:' "${newpic}" "${ext}" "${tn}" # # Convert originals to new path/file (or document it in dry-run mode) # if [[ $DRYRUN -eq 0 ]]; then gm convert "$PICDIR/$pic" -strip -auto-orient "$PICDIR/$newpic" gm convert "$PICDIR/$pic" -strip -auto-orient -resize $TNSIZE "$PICDIR/$tn" # # Only make a (new) picture list if we have plain text or # a Markdown variant # if [[ $NOTEFORMAT =~ $FMTRE ]]; then echo "$PICDIR/$newpic" >> "$MANIFEST" echo "$PICDIR/$tn" >> "$MANIFEST" if [[ $pcount -gt 1 ]]; then echo "[% ind = ind + 1 -%]" >> "$PICTPL" fi # Add 'pic2' macro call cat "$TMP4" >> "$PICTPL" fi # # All thumbnails are new so need to be recorded # echo "$tn" >> "$PICLIST" echo "$tn" >> "$ASSETS" _silent "Converted picture ${yellow}$pic${reset} with thumbnail" else _dryrun \ "Would have converted ${yellow}$pic${reset} to " \ "${yellow}$newpic${reset} and ${yellow}$tn${reset} in " \ "${yellow}$(trimpath "$PICDIR" 3)${reset}" # # Only make a (new) picture list if we have plain text or # a Markdown variant # if [[ $NOTEFORMAT =~ $FMTRE ]]; then _dryrun "Would have added ${yellow}$PICDIR/$newpic${reset} to" \ "${yellow}$MANIFEST${reset}" _dryrun "would have added ${yellow}$PICDIR/$tn${reset} to" \ "${yellow}$MANIFEST${reset}" _dryrun "Would have added the ${yellow}pic2${reset} macro to" \ "${yellow}$PICTPL${reset}" fi echo "----" fi else _DEBUG 'No thumbnail:' "${newpic}" "${ext}" # # Convert originals to new path/file (or document it) # if [[ $DRYRUN -eq 0 ]]; then gm convert "$PICDIR/$pic" -strip -auto-orient "$PICDIR/$newpic" # # Only make a (new) picture list if we have plain text or # a Markdown variant # if [[ $NOTEFORMAT =~ $FMTRE ]]; then echo "$PICDIR/$newpic" >> "$MANIFEST" echo "" >> "$MANIFEST" if [[ $pcount -gt 1 ]]; then echo "[% ind = ind + 1 -%]" >> "$PICTPL" fi # Add 'pic1' macro call cat "$TMP3" >> "$PICTPL" fi _silent "Converted picture ${yellow}$pic${reset} without thumbnail" else _dryrun "Would have converted ${yellow}$pic${reset} to " \ "${yellow}$newpic${reset}" # # Only make a (new) picture list if we have plain text or # a Markdown variant # if [[ $NOTEFORMAT =~ $FMTRE ]]; then _dryrun "Would have added ${yellow}$PICDIR/$newpic${reset} to" \ "${yellow}$MANIFEST${reset}" _dryrun "would have added a blank line to ${yellow}$MANIFEST${reset}" _dryrun "Would have added the ${yellow}pic1${reset} macro to" \ "${yellow}$PICTPL${reset}" fi echo "----" fi fi ((pcount++)) done # # Final steps - not wanted in dry run mode # if [[ $DRYRUN -eq 0 ]]; then # # If we made thumbnails the .assets and .pictures files will have been # updated. Clean these up here. # perl -i.BAK -ne 'print if ! $x{$_}++' "$PICLIST" perl -i.BAK -ne 'print if ! $x{$_}++' "$ASSETS" # # Set the status for this show # echo "pictures" >> "$STATUS" fi exit # vim: syntax=sh:ts=8:sw=4:ai:et:tw=78:fo=tcrqn21:fdm=marker