Skip to content

Instantly share code, notes, and snippets.

@fikovnik
Created June 16, 2024 18:38
Show Gist options
  • Save fikovnik/963fd95b3ffbde2d139d39dd1d2c73a2 to your computer and use it in GitHub Desktop.
Save fikovnik/963fd95b3ffbde2d139d39dd1d2c73a2 to your computer and use it in GitHub Desktop.
Create a m4b file from mp3 files with metadata and image cover (tailored for audioteka.cz)
#!/bin/bash -x
CATEGORY="audioteka"
function get_mp3_tag() {
local file="$1"
local tag="$2"
ffprobe -v error -show_entries format_tags="$tag" -of default=noprint_wrappers=1:nokey=1 "$file"
}
function create_metadata() {
local dir="$1"
local meta_file="$2"
local list_file="$3"
local playlist="$dir/playlist.pls"
echo "" >"$list_file"
echo ";FFMETADATA1" >"$meta_file"
local metadata=0
local start=0
local title
local title
local length
local album
local artist
while IFS= read -r line; do
if [[ $line == File* ]]; then
filename="$FOLDER_PATH/$(echo "$line" | cut -d '=' -f 2)"
if [ $metadata -eq 0 ]; then
metadata=1
album=$(get_mp3_tag "$filename" album)
artist=$(get_mp3_tag "$filename" artist)
{
echo "title=$album"
echo "album=$album"
echo "artist=$artist"
echo "album_artist=$artist"
echo "genre=$(get_mp3_tag "$filename" genre)"
echo "date=$(get_mp3_tag "$filename" date)"
echo "comment=$(get_mp3_tag "$filename" comment)"
echo "category=$CATEGORY"
} >>"$meta_file"
fi
elif [[ $line == Title* ]]; then
title=$(echo "$line" | cut -d '=' -f 2)
elif [[ $line == Length* ]]; then
length=$(echo "$line" | cut -d '=' -f 2)
echo "file '$filename'" >>"$list_file"
{
echo "[CHAPTER]"
echo "TIMEBASE=1/1000"
echo "START=$start"
start=$((start + length))
echo "END=$start"
echo "title=$title"
} >>"$meta_file"
fi
done <"$playlist"
}
# Check if the correct number of arguments is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <path_to_folder>"
exit 1
fi
FOLDER_PATH=""
if [[ -f "$1" && $(file -b --mime-type "$1") == application/zip ]]; then
FOLDER_PATH=$(basename "$1" .zip)
mkdir -p "$FOLDER_PATH"
unzip -q "$1" -d "$FOLDER_PATH"
elif [ -d "$1" ]; then
FOLDER_PATH="$1"
else
echo "Don't know what to do with $1"
exit 1
fi
BOOK_NAME=$(basename "$FOLDER_PATH")
# Check if the provided path is a directory
if [ ! -d "$FOLDER_PATH" ]; then
echo "Error: $FOLDER_PATH is not a directory"
exit 1
fi
# Check if the cover image exists in the directory
COVER_FILE="$FOLDER_PATH/$BOOK_NAME-duze.jpg"
if [ ! -f "$COVER_FILE" ]; then
echo "Error: Cover image '$COVER_FILE' not found in the directory"
exit 1
fi
# Output files
LIST_FILE="$BOOK_NAME.files.txt"
META_FILE="$BOOK_NAME.meta"
COMBINED_FILE="$BOOK_NAME.mp3"
CONVERTED_FILE="$BOOK_NAME.converted.m4b"
OUTPUT_FILE="$BOOK_NAME.m4b"
create_metadata "$FOLDER_PATH" "$META_FILE" "$LIST_FILE"
# Combine MP3 files into one MP3 file
[ -f "$COMBINED_FILE" ] || ffmpeg -f concat -safe 0 -i "$LIST_FILE" -c copy "$COMBINED_FILE"
# Convert into mp4b
[ -f "$OUTPUT_FILE" ] || ffmpeg -i "$COMBINED_FILE" "$CONVERTED_FILE"
# Add metadata to the m4b file
[ -f "$OUTPUT_FILE" ] || ffmpeg -i "$CONVERTED_FILE" \
-i "$META_FILE" \
-i "$COVER_FILE" \
-map 0:a -map_metadata 1 -map 2:v -disposition:v:0 attached_pic -c copy -movflags +faststart \
"$OUTPUT_FILE"
echo "M4B file created successfully: $OUTPUT_FILE"
@yazdan
Copy link

yazdan commented Feb 10, 2025

Hi I put some time and effort to make this work on my computer so this is my pay back to this useful script

  1. You need to have a cover for your book name like folder-name-duez.jpg
  2. You need to have list of all files in file named playlist.pls. The easiest way to create that list is to use clementine player on linux
  3. Enjoy it.

Thanks for your effort developing this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment