From d395ab9c7a5ffde65e665979056c1034c87add2a Mon Sep 17 00:00:00 2001 From: Alex Cabal Date: Tue, 21 May 2019 16:38:34 -0500 Subject: [PATCH] Add some server-side infrastructure scripts --- scripts/deploy-ebook-to-www | 166 ++++++++++++++++++++++++++++++++++ scripts/generate-opds.php | 105 +++++++++++++++++++++ scripts/generate-rss.php | 73 +++++++++++++++ scripts/reset-php-fpm-opcache | 30 ++++++ 4 files changed, 374 insertions(+) create mode 100755 scripts/deploy-ebook-to-www create mode 100755 scripts/generate-opds.php create mode 100644 scripts/generate-rss.php create mode 100755 scripts/reset-php-fpm-opcache diff --git a/scripts/deploy-ebook-to-www b/scripts/deploy-ebook-to-www new file mode 100755 index 00000000..aa34c646 --- /dev/null +++ b/scripts/deploy-ebook-to-www @@ -0,0 +1,166 @@ +#!/bin/bash + +usage(){ + fmt <&2; exit 1; } +require(){ command -v "$1" > /dev/null 2>&1 || { suggestion=""; if [ ! -z "$2" ]; then suggestion=" $2"; fi; die "$1 is not installed.${suggestion}"; } } +if [ $# -eq 1 ]; then if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then usage; fi fi +# End boilerplate + +# Check for dependencies +require "convert" "Try: apt-get install imagemagick" +require "rsvg-convert" "Try: apt-get install librsvg2-bin" + +verbose="false" + +if [ $# -eq 0 ]; then + usage +fi + +for var in "$@" +do + if [ "${var}" = "-v" ] || [ "${var}" = "--verbose" ]; then + verbose="true" + break + fi +done + +for dir in "$@" +do + if [ "${dir}" = "" ] || [ "${dir}" = "-v" ] || [ "${dir}" = "--verbose" ]; then + continue + fi + + repoDir=$(realpath "${dir%/}") + baseName=$(basename "${repoDir}") + + if [ "${baseName}" = ".git" ]; then + continue + fi + + if [ ! -d "${repoDir}" ]; then + die "Invalid repo root: ${repoDir}" + fi + + cd "${repoDir}" || return + + if [ "${verbose}" = "true" ]; then + printf "Entering %s\n" "${repoDir}" + fi + + if git show HEAD:src/epub/content.opf | grep --quiet --extended-regexp "1900-01-01T00:00:00Z"; then + printf "Looks like a draft ebook, skipping\n" + continue + fi + + webDir=$(git show HEAD:src/epub/content.opf | grep --only-matching --extended-regexp "url:https://standardebooks.org/ebooks/[^<]+<\/dc:identifier>" | sed --regexp-extended "s/<[^>]+?>//g" | sed --regexp-extended "s/^url:https:\/\/standardebooks.org\/ebooks\/?//") + + if [ "${webDir}" = "" ]; then + die "Empty webdir!" + fi + + workDir=$(mktemp -d) + imgWorkDir=$(mktemp -d) + webDir="/standardebooks.org/www/ebooks/${webDir}" + + if [ "${verbose}" = "true" ]; then + printf "Generating cover image for web ... " + fi + + urlSafeIdentifier=$(git show HEAD:src/epub/content.opf | grep --only-matching --extended-regexp "url:https://standardebooks.org/ebooks/[^<]+<\/dc:identifier>" | sed --regexp-extended "s/<[^>]+?>//g" | sed --regexp-extended "s|url:https://standardebooks.org/ebooks/||g" | sed --regexp-extended "s|/|_|g") + + # Build the hero image for individual ebook pages + git show HEAD:images/cover.jpg > "${imgWorkDir}/${urlSafeIdentifier}.jpg" + cp "${imgWorkDir}/${urlSafeIdentifier}.jpg" "${imgWorkDir}/${urlSafeIdentifier}@2x.jpg" + # Resize and crop the image to 2156 width, 720 height, and starting at the coords 0,1078 + convert -resize "1078" -crop "1078x359+0+539" -sampling-factor 4:2:0 -strip -quality 75 -colorspace RGB -interlace JPEG "${imgWorkDir}/${urlSafeIdentifier}.jpg" "${imgWorkDir}/${urlSafeIdentifier}-hero.jpg" + convert -resize "2156" -crop "2156x718+0+1078" -sampling-factor 4:2:0 -strip -quality 75 -colorspace RGB -interlace JPEG "${imgWorkDir}/${urlSafeIdentifier}@2x.jpg" "${imgWorkDir}/${urlSafeIdentifier}-hero@2x.jpg" + + # Build the cover image thumbnail + git show HEAD:images/cover.jpg > "${imgWorkDir}/${urlSafeIdentifier}.jpg" + git show HEAD:images/cover.svg > "${imgWorkDir}/${urlSafeIdentifier}.svg" + sed -i "s/cover\.jpg/${urlSafeIdentifier}\.jpg/g" "${imgWorkDir}/${urlSafeIdentifier}.svg" + cp "${imgWorkDir}/${urlSafeIdentifier}.svg" "${imgWorkDir}/${urlSafeIdentifier}@2x.svg" + + # Due to a bug in `convert` we have to use rsvg-convert to convert SVG to PNG, then work on the PNG with `convert`. + rsvg-convert --keep-aspect-ratio --format png --output "${imgWorkDir}/${urlSafeIdentifier}.png" "${imgWorkDir}/${urlSafeIdentifier}.svg" + rsvg-convert --keep-aspect-ratio --format png --output "${imgWorkDir}/${urlSafeIdentifier}@2x.png" "${imgWorkDir}/${urlSafeIdentifier}@2x.svg" + + # Resize and compress the image (formula from Google Page Speed Insights) + convert -resize "196" -sampling-factor 4:2:0 -strip -quality 75 -colorspace RGB -interlace JPEG "${imgWorkDir}/${urlSafeIdentifier}.png" "${imgWorkDir}/${urlSafeIdentifier}.jpg" + convert -resize "392" -sampling-factor 4:2:0 -strip -quality 75 -colorspace RGB -interlace JPEG "${imgWorkDir}/${urlSafeIdentifier}@2x.png" "${imgWorkDir}/${urlSafeIdentifier}@2x.jpg" + + mv "${imgWorkDir}/${urlSafeIdentifier}@2x.jpg" "${imgWorkDir}/${urlSafeIdentifier}-cover@2x.jpg" + mv "${imgWorkDir}/${urlSafeIdentifier}.jpg" "${imgWorkDir}/${urlSafeIdentifier}-cover.jpg" + + sudo chgrp --preserve-root --recursive se "${imgWorkDir}/${urlSafeIdentifier}"* + sudo chmod --preserve-root --recursive g+w "${imgWorkDir}/${urlSafeIdentifier}"* + + if [ "${verbose}" = "true" ]; then + printf "Done.\n" + printf "Building ebook ... " + fi + + git clone --quiet "${repoDir}" "${workDir}" + + mkdir "${workDir}/dist" + + # Build the ebook + if ! se build --output-dir="${workDir}/dist" --check --kindle --kobo --covers "${workDir}"; then + rm --preserve-root --recursive --force "${workDir}" + die "Error building ebook, stopping deployment." + fi + + if [ "${verbose}" = "true" ]; then + printf "Done.\n" + fi + + # Delete the contents of the old webdir + rm --preserve-root --recursive --force "${webDir}" + + # Re-create the webdir + mkdir -p "${webDir}" + + # Move contents of the work dir over + mv "${workDir}"/* "${webDir}/" + + # Move the cover images over + mv "${imgWorkDir}/${urlSafeIdentifier}"*.jpg "/standardebooks.org/www/images/covers/" + + # Delete the now-empty work dir (empty except for .git) + rm --preserve-root --recursive --force "${workDir}" "${imgWorkDir}" + + sudo chgrp --preserve-root --recursive se "${webDir}" + + sudo chmod --preserve-root --recursive g+ws "${webDir}" + + if [ "${verbose}" = "true" ]; then + printf "Flushing PHP-FPM opcache and apcu cache ... " + fi + + /standardebooks.org/scripts/reset-php-fpm-opcache standardebooks.org + + if [ "${verbose}" = "true" ]; then + printf "Done.\n" + fi +done + +# Build the OPDS catalog +if [ "${verbose}" = "true" ]; then + printf "Rebuilding OPDS catalog ... " +fi + +bash -c "php /standardebooks.org/scripts/generate-opds.php > /standardebooks.org/www/opds/all.xml; export XMLLINT_INDENT=\$(printf \"\\t\") && xmllint --c14n /standardebooks.org/www/opds/all.xml | (printf \"%s\\n\" \"\" && cat) | xmllint --output /standardebooks.org/www/opds/all.xml --format -" + +if [ "${verbose}" = "true" ]; then + printf "Done.\n" +fi diff --git a/scripts/generate-opds.php b/scripts/generate-opds.php new file mode 100755 index 00000000..6a23803a --- /dev/null +++ b/scripts/generate-opds.php @@ -0,0 +1,105 @@ +\n"); +?> + + https://standardebooks.org/opds/all + + + All Standard Ebooks + Free and liberated ebooks, carefully produced for the true book lover. + https://standardebooks.org/images/logo.png + + + Standard Ebooks + https://standardebooks.org + + registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/'); + + $authors = array(); + $temp = $xml->xpath('/package/metadata/dc:identifier') ?: []; + $url = preg_replace('/^url:/ius', '', (string)array_shift($temp)) ?? ''; + $relativeUrl = preg_replace('/^https:\/\/standardebooks.org/ius', '', $url) ?? ''; + + $temp = $xml->xpath('/package/metadata/dc:title') ?: []; + $title = array_shift($temp); + + $temp = $xml->xpath('/package/metadata/meta[@property="se:long-description"]') ?: []; + $longDescription = array_shift($temp); + + $authors = $xml->xpath('/package/metadata/dc:creator') ?: []; + + $temp = $xml->xpath('/package/metadata/dc:date') ?: []; + $published = array_shift($temp); + + $temp = $xml->xpath('/package/metadata/dc:language') ?: []; + $language = array_shift($temp); + + $temp = $xml->xpath('/package/metadata/meta[@property="dcterms:modified"]') ?: []; + $modified = array_shift($temp); + + $temp = $xml->xpath('/package/metadata/dc:description') ?: []; + $description = array_shift($temp); + + $subjects = $xml->xpath('/package/metadata/dc:subject') ?: []; + + $sources = $xml->xpath('/package/metadata/dc:source') ?: []; + + $filesystemPath = preg_replace('/\/src\/epub\/content.opf$/ius', '', $path) ?? ''; + $temp = glob($filesystemPath . '/dist/*.epub'); + $epubFilename = preg_replace('/(\|\.epub)/ius', '', preg_replace('/.+\//ius', '', array_shift($temp) ?? '') ?? '') ?? ''; + $temp = glob($filesystemPath . '/dist/*.azw3'); + $kindleFilename = preg_replace('/.+\//ius', '', array_shift($temp) ?? '') ?? ''; + + ?> + + + <?= $title ?> + attributes() !== null){ + $id = $author->attributes()->id; + } + $temp = $xml->xpath('/package/metadata/meta[@property="se:url.encyclopedia.wikipedia"][@refines="#' . $id . '"]') ?: []; + $wikiUrl = array_shift($temp); + $temp = $xml->xpath('/package/metadata/meta[@property="se:name.person.full-name"][@refines="#' . $id . '"]') ?: []; + $fullName = array_shift($temp); + $temp = $xml->xpath('/package/metadata/meta[@property="se:url.authority.nacoaf"][@refines="#' . $id . '"]') ?: []; + $nacoafLink = array_shift($temp); + ?> + + + + + + + + + + + Standard Ebooks + + + + Public domain in the United States; original content released to the public domain via the Creative Commons CC0 1.0 Universal Public Domain Dedication + + + + + + + + + + + + + + + diff --git a/scripts/generate-rss.php b/scripts/generate-rss.php new file mode 100644 index 00000000..0987cc50 --- /dev/null +++ b/scripts/generate-rss.php @@ -0,0 +1,73 @@ +registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/'); + + $temp = $xml->xpath('/package/metadata/dc:date') ?: []; + $publishedTimestamp = strtotime(array_shift($temp)); + + $sortedContentFiles[$publishedTimestamp] = $xml; +} + +krsort($sortedContentFiles); + +$sortedContentFiles = array_slice($sortedContentFiles, 0, $rssLength); + +print("\n"); +?> + + + Standard Ebooks - New Releases + https://standardebooks.org + A list of the latest Standard Ebooks ebook releases, most-recently-released first. + en-US + https://creativecommons.org/publicdomain/zero/1.0/ + + http://blogs.law.harvard.edu/tech/rss + + + https://standardebooks.org/images/logo-rss.png + Standard Ebooks - New Releases + The Standard Ebooks logo + https://standardebooks.org + 144 + 144 + + xpath('/package/metadata/dc:identifier') ?: []; + $url = preg_replace('/^url:/ius', '', (string)array_shift($temp) ?? '') ?? ''; + + $temp = $xml->xpath('/package/metadata/dc:title') ?: []; + $title = array_shift($temp) ?? ''; + + $temp = $xml->xpath('/package/metadata/dc:creator') ?: []; + $title .= ', by ' . array_shift($temp) ?? ''; + + $temp = $xml->xpath('/package/metadata/dc:description') ?: []; + $description = array_shift($temp) ?? ''; + + $temp = $xml->xpath('/package/metadata/dc:date') ?: []; + $published = gmdate('D, d M Y H:i:s +0000', strtotime(array_shift($temp) ?? '') ?: 0); + + $seSubjects = $xml->xpath('/package/metadata/meta[@property="se:subject"]') ?: []; + ?> + <?= $title ?> + + + + + + + + + + + diff --git a/scripts/reset-php-fpm-opcache b/scripts/reset-php-fpm-opcache new file mode 100755 index 00000000..c8673dfb --- /dev/null +++ b/scripts/reset-php-fpm-opcache @@ -0,0 +1,30 @@ +#!/bin/bash + +usage(){ + echo -n + fmt <' > /tmp/php-fpm-opcache-reset.php +sudo -u www-data env SCRIPT_FILENAME=/tmp/php-fpm-opcache-reset.php REQUEST_METHOD=GET cgi-fcgi -bind -connect "/run/php/$1.sock" &> /dev/null +rm /tmp/php-fpm-opcache-reset.php