| Summary | Make a single uncompressed zip file from all images in a folder and its subfolders, without also including videos. | |
| Repository | https://github.com/jjclark1982/scripts/blob/main/make-cbz |
I often see a single photo/art gallery with a nested folder structure that Stash treats as multiple galleries.
For example, Stash would look at a structure like this and make galleries of the 6 innermost folders that have pictures:
Cosplayer/
March 2022/
Gwen Stacy/
Cosplay Photoset/
Boudoir Photoset/
Backstage/
Videos/
Marin Kitagawa/
Cosplay Photoset/
Boudoir Photoset/
Backstage/
Videos/
The innermost folder structure is important, but it is not the most natural grouping, and causes related items to get separated in the app.
If you create a zip file of each related gallery, it will preserve the file structure within that zip file and Stash will treat it as a single gallery. But you donβt want to include videos in the zip file as they will not be seen by Stash or CBZ viewers.
Gwen Stacy/
Gwen Stacy.cbz
Videos/
Marin Kitagawa/
Marin Kitagawa.cbz
Videos/
Here is a script to make a single uncompressed zip file from all images in a folder and its subfolders, without also including videos.
Repository on GitHub: scripts/make-cbz at main Β· jjclark1982/scripts Β· GitHub
Updated August 13 to run image identification and zip creation in parallel.
#!/bin/sh -uo pipefail
# make-cbz.sh
# Create a .CBZ file of all the images in a directory.
# Leave non-image files in place.
#
# Example:
# > make-cbz images/
# ...
# Created images.cbz
#
# Example: zip all subdirectories individually
# > make-cbz */
# ...
# Created a.cbz
# Created b.cbz
# Created c.cbz
#
# Example: use a different extension (default .cbz)
# > EXTENSION=zip make-cbz images/
# ...
# Created images.zip
EXTENSION=".${EXTENSION:-cbz}"
# Function to identify image files based on content
function list_images() {
find "$1" \
| sort --version-sort \
| filter_images
}
function filter_images() {
local filename
while read -r filename; do
[ -f "$filename" ] || continue # must be a regular file
case "$(file "$filename")" in
*"image"* |\
*"Image"* |\
*"Picture"* )
echo "$filename"
;;
esac
done
}
function zip_images_in_dir() { (
# Run in a subshell that will exit on the first error
set -e
DIR="$1"
if [ ! -d "$DIR" ]; then
echo Error: "$DIR" is not a directory. Skipped.
return 1
fi
# Work relative to parent folder of DIR, to standardize paths within zip files.
cd "$DIR"
DIR="$(basename "$PWD")"
cd ..
# Check whether this zipfile has already been created.
ZIPFILE="${DIR%/}$EXTENSION"
if [ -f "$ZIPFILE" ]; then
echo Error: "$ZIPFILE" already exists. Skipped.
return 2
fi
if [ -f "${DIR}/$(basename "$ZIPFILE")" ]; then
echo "$ZIPFILE" already exists within "$DIR". Skipped.
return 3
fi
echo Creating "$ZIPFILE"...
# Simple command to zip only common image extensions
# zip -0 --latest-time -m -r "$ZIPFILE" "$DIR" \
# -i '*.jpeg' -i '*.jpg' -i '*.gif' -i '*.png' -i '*.svg' -i '*.webp' -i '*.heic' -i '*.avif' \
# -i '*.JPEG' -i '*.JPG' -i '*.GIF' -i '*.PNG' -i '*.SVG' -i '*.WEBP' -i '*.HEIC' -i '*.AVIF'
# if [ -z "$(list_images .)" ]; then
# echo No image files found in "$DIR". Skipped.
# return 4
# fi
# Zip identified images with no compression, and remove original files
list_images "$DIR" | zip -0 --latest-time -m "$ZIPFILE" -@
if [ ! -f "$ZIPFILE" ]; then
echo Error: "$ZIPFILE" was not created.
return 5
fi
echo Created "$ZIPFILE"
# Remove empty folders.
# (zip -m is supposed to handle this but find is more thorough)
# TODO: check how this handles @eaDir
find "$DIR" -name ".DS_Store" -delete
find "$DIR" -type d -empty -delete
# If main directory was not empty, move zipfile into it to stay with related files.
if [ -d "$DIR" ]; then
mv "$ZIPFILE" "$DIR"/
echo Moved "$ZIPFILE" into "$DIR/"
fi
return 0
) }
# Main routine: loop over command-line arguments
for DIR; do
echo
zip_images_in_dir "$DIR"
done