hpr-tools/Show_Submission/do_pictures

700 lines
21 KiB
Plaintext
Raw Permalink Normal View History

#!/bin/bash -
#===============================================================================
#
# FILE: do_pictures
#
# USAGE: ./do_pictures [options] <epno>
#
# 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<epno>'
# 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
# '<name>_thumbnail.<ext>'. 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<epno>' (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" <<ENDHTML1
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="generator" content="pandoc">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<meta name="author" content="$Host_Name">
<title>$Title (HPR Show $1)</title>
<style type="text/css">code{white-space: pre;}</style>
<!--[if lt IE 9]>
<script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link rel="stylesheet" href="https://hackerpublicradio.org/css/hpr.css">
</head>
<body id="home">
<div id="container" class="shadow">
<header>
<h1 class="title">$Title (HPR Show $1)</h1>
<h3 class="summary">$Summary</h3>
<h3 class="author">$Host_Name</h3>
<hr/>
</header>
<main id="maincontent">
<article>
<h3 id="index">Index</h3>
<ul>
ENDHTML1
cat > "$TMP2" <<ENDHTML2
</ul>
</article>
</main>
</div>
</body>
</html>
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