troy_ 4 Posted January 12, 2023 Share Posted January 12, 2023 (edited) As an Emby newbie I'm struggling with some things, like lack of chapter support for M4B files. If you fetch your audio books with something like OpenAudible then the script below (gist here) can sort them out into Emby's preferred directory structure, and break them up into chapters. Copy the script to the folder with your M4B's, and run it. You need to have ffmpeg and ffprobe installed and working - that's about it. The original file is moved to an ignored directory, and cover art is stored per the Emby requirements. #!/usr/bin/env zsh # Check if ffmpeg is installed if ! [ -x "$(command -v ffmpeg)" ]; then echo 'Error: ffmpeg is not installed.' >&2 exit 1 fi # Check if ffprobe is installed if ! [ -x "$(command -v ffprobe)" ]; then echo 'Error: ffprobe is not installed.' >&2 exit 1 fi ffmpeg_loglevel="error" max_title_length=100 # Loop over the folder for file in *.m4b; do # Get the title and year of the file title=$(ffprobe -loglevel ${ffmpeg_loglevel} -show_entries format_tags=album -of default=noprint_wrappers=1:nokey=1 "$file") if [ "${#title}" -ge "$max_title_length" ]; then title="${file%.*}" fi author=$(ffprobe -loglevel ${ffmpeg_loglevel} -show_entries format_tags=artist -of default=noprint_wrappers=1:nokey=1 "$file") narrator=$(ffprobe -loglevel ${ffmpeg_loglevel} -show_entries format_tags=composer -of default=noprint_wrappers=1:nokey=1 "$file") year=$(ffprobe -loglevel ${ffmpeg_loglevel} -show_entries format_tags=date -of default=noprint_wrappers=1:nokey=1 "$file") audiobook_folder="${author}/${title} ($year)" audiobook_source="${audiobook_folder}/source" # Create a folder for the file if it doesn't exist if [ ! -d "$audiobook_source" ]; then mkdir -p "$audiobook_source" fi if [ ! -f "$audiobook_source/.ignore" ]; then touch "$audiobook_source/.ignore" fi if [ -f "$audiobook_source/$file" ]; then if cmp -s "${file}" "$audiobook_source/$file" ; then rm -f "${file}" else echo " File aready exists" mv "$audiobook_source/$file" "$audiobook_source/$file.bak" mv "$file" "$audiobook_source/" fi else mv "$file" "$audiobook_source/" fi if [ ! -f "${audiobook_folder}/cover.jpg" ]; then ffmpeg -loglevel ${ffmpeg_loglevel} -i "${audiobook_source}/$file" -frames:v 1 -an -vcodec copy "${audiobook_folder}/cover.jpg" fi ffmpeg -loglevel ${ffmpeg_loglevel} -i "${audiobook_source}/$file" -map_chapters -1 -f ffmetadata "${audiobook_source}/metadata.txt" # Get the chapters from the file chapters_json=$(ffprobe -loglevel ${ffmpeg_loglevel} -show_chapters -print_format json "$audiobook_source/$file") echo "${chapters_json}" > "${audiobook_source}/chapters.json" # Loop over the chapters count=`jq '.chapters | length' <<< "${chapters_json}"` total_chapters=$((count+1)) for ((i=0; i<$count; i++)); do chapter=`jq -r '.chapters['$i']' <<< "${chapters_json}"` chapter_id=$(jq -r '.id' <<< "$chapter") if [ -z "$chapter_id" ]; then echo "No chapter id. JSON: ${chapter}"; exit 1; fi printf -v chapter_number "%d" $((chapter_id+1)) chapter_title=$(jq -r '.tags.title' <<< "$chapter") seek_position=$(jq -r '.start_time' <<< "$chapter") if [ -z "$seek_position" ]; then echo "No seek position for chapter id ${chapter_id}"; exit 1; fi end_position=$(jq -r '.end_time' <<< "$chapter") if [ -z "$end_position" ]; then echo "No end position for chapter id ${chapter_id}"; exit 1; fi length_seconds=$((end_position-seek_position)) printf -v chapter_start_ms "%0d" $((seek_position*1000)) printf -v chapter_end_ms "%0d" $((end_position*1000)) printf -v chapter_length_ms "%0.6f" $((length_seconds*1000)) printf -v chapter_start_seconds "%0.6f" "${seek_position}" printf -v chapter_end_seconds "%0.6f" "${end_position}" printf -v chapter_length_seconds "%0.6f" "${length_seconds}" file_name="${chapter_number}- ${chapter_title}.m4b" chapter_file_name="${chapter_number}- ${chapter_title}.txt" cp "${audiobook_source}/metadata.txt" "${audiobook_source}/${chapter_file_name}" printf "\n\n[CHAPTER]\nTIMEBASE=1/1000\nSTART=%d\nEND=%d\ntitle=%q\n\n" "0" "${chapter_length_ms}" "${chapter_title}" >> "${audiobook_source}/${chapter_file_name}" echo "${file_name} ==> ${seek_position} to ${end_position} (${chapter_end_seconds})" if [ ! -f "${audiobook_folder}/${file_name}" ]; then ffmpeg -loglevel ${ffmpeg_loglevel} -ss "${seek_position}s" -to "${end_position}s" -i "${audiobook_source}/$file" -i "${audiobook_source}/${chapter_file_name}" -map_metadata 0 -map_chapters 1 -metadata "track=${chapter_number}/${total_chapters}" -c copy "${audiobook_folder}/${file_name}" fi done done Edited January 12, 2023 by troy_ more fixes :-D Link to comment Share on other sites More sharing options...
troy_ 4 Posted January 12, 2023 Author Share Posted January 12, 2023 Of note - this doesn't seem to fix my issue with Emby... Now I have a heap of files - all with the title instead of the chapter. At least Emby seems to recognise the Chapter inside the M4B's generated with the script above. I'm not sure what to do at this point. The metadata for the OpenAudible M4B's is correct and works with other players. Splitting the M4B up clearly isn't the answer (as I now have a book for every chapter); putting the files in the Emby preferred folder structure isn't the answer; mp3's don't seem to work either. Open to suggestions! Link to comment Share on other sites More sharing options...
Luke 37247 Posted January 19, 2023 Share Posted January 19, 2023 HI, the title doesn't come from the file name, but rather the metadata embedded within the file. Have you checked that? Link to comment Share on other sites More sharing options...
troy_ 4 Posted January 20, 2023 Author Share Posted January 20, 2023 Some of my M4B's are working now (with chapters) which may be because I've moved to `beta` I don't know if it's just an import delay, I'm a couple of weeks in now and it's still scanning my library - so perhaps the issue will clear up. You are correct @Lukethe title of each M4B is the title of the work, not the title of the chapter - so that's what's causing the issue above. I've updated the script to work as a post processor now, and it can break up and put the chapter title into the track title. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now