Skip to content

Instantly share code, notes, and snippets.

@wa1kb0y
Created February 25, 2026 22:01
Show Gist options
  • Select an option

  • Save wa1kb0y/b68a5e6ab0d4e548d4d207554b693ff2 to your computer and use it in GitHub Desktop.

Select an option

Save wa1kb0y/b68a5e6ab0d4e548d4d207554b693ff2 to your computer and use it in GitHub Desktop.
EPUB format fix for books exported from Apple Books
#!/usr/bin/env bash
# This script is used to export epub books from iBooks / Mac Books and fix format to correct EPUB
# What you find in the $ibooks_dir on iCloud are epubs that are zipped directories
# This script will make them normal .epub files that you can use in other e-readers
# I would advise to download your epubs from the iCloud to a folder on your desktop, there might also be pdf's in there
# How to run:
# Save this file as repackage_epubs.sh
# Make executable in the terminal: chmod +x repackage_epubs.sh
# ./repackage_epubs.sh -i # Use iBooks folder
# ./repackage_epubs.sh -d /path # Use custom folder (absolute path)
# ./repackage_epubs.sh # Use current directory
# Function to build an EPUB file from an epub folder
build_epub ()
{
local source_folder="$1" # Input: folder
local dest_file # Output: EPUB filename
local output_dir="$2" # Output directory (absolute path)
# Set output filename: use the folder name with .epub extension
dest_file="${output_dir}/$(basename "${source_folder}").epub"
# Enter the source folder (exit if failed)
cd "${source_folder}" || { echo "Error: Could not enter ${source_folder}"; exit 1; }
# Step 1: Add mimetype file to the EPUB with no compression (required by EPUB spec)
zip --quiet -X0 "${dest_file}" mimetype || { echo "Error: Failed to add mimetype to ${dest_file}"; exit 1; }
# Step 2: Add all other files with max compression, excluding .DS_Store and mimetype
zip --quiet -rDX9 "${dest_file}" * -x "*.DS_Store" -x mimetype || { echo "Error: Failed to create ${dest_file}"; exit 1; }
}
# Main script
main()
{
local search_dir="." # Default: recursive search current directory
local ibooks_dir="~/Library/Mobile Documents/iCloud~com~apple~iBooks/Documents"
local output_dir="output"
# Create output directory if it doesn't exist
mkdir -p "$output_dir" || { echo "Error: Could not create output directory"; exit 1; }
# Get absolute path for output_dir
output_dir="$(cd "$output_dir" && pwd)"
# Parse command-line options
while getopts "d:i" opt; do
case "${opt}" in
d)
search_dir="${OPTARG}"
;;
i)
search_dir="$ibooks_dir"
;;
*)
echo "Usage: $0 [-d search_directory] [-i]" >&2
echo " -d: specify a custom search directory" >&2
echo " -i: use the default iBooks directory" >&2
exit 1
;;
esac
done
shift $((OPTIND - 1))
echo "Searching for .epub directories in: $search_dir"
echo "Output will be saved in: $output_dir"
# Find and process all .epub directories
find "$search_dir" -type d -name \*.epub -print0 |
while IFS= read -r -d '' epubdir; do
# Check if a file with the same name as the directory does NOT exist in output_dir
if [[ ! -f "${output_dir}/$(basename "$epubdir").epub" ]]; then
echo "Processing $(basename "$epubdir")";
build_epub "$epubdir" "$output_dir";
else
echo "Skipping $(basename "$epubdir") (output file already exists)";
fi;
done
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment