ffmpegを使用してAudibleオーディオブックを複数の章に分割しますか?

ffmpegを使用してAudibleオーディオブックを複数の章に分割しますか?

私は注意を払ってきました。この回答ffmpegLinuxMintは私のAudibleオーディオブックの一部を変換して再生するために使用されます。各書籍はソースファイルですが、ffmpeg変換の先頭にすべての章がリストされていることを確認しました。

本を章に分ける方法はありますかffmpeg?各章を別々のファイルに変換(章別に分割)しますか?単独で使用するのが最善ですが、ffmpeg他のプログラム/スクリプトを(と一緒にffmpeg)使用することもオプションです。

(私は(Pythonスクリプトを使用して)DVDを偶数長さのチャンクまたはチャプターに分割することに関する他の答えを見ましたが、ffmpeg私が探しているものではないので、より簡単な方法があることを願っています。)

答え1

私は最近この仕事をやってきました。 Nemoが上記のように、ffprobeはコマンドを使用して章を簡単に開始および終了できるjsonファイルを提供します。

ffprobe -i fileName -print_format json -show_chapters

-sexagesimalコマンドに追加すると、より読みやすい出力(IMO)が生成され、出力は後で処理するためにファイルにリダイレクトされます。

FFmpegには少し助けが必要だったので、jgとAtomicParsleyも使用しました。前者はJSONファイルを解析し、後者は結果のm4bファイルに画像とメタデータを追加します。

スクリプトはまた、m4aファイルを使用した出力をサポートするか、必要に応じてmp3への変換をサポートします。引数$ 1 - 入力ファイルと(オプション)$ 2出力タイプ(デフォルトはm4b)を使用して呼び出すだけです。

これに基づいて、次のスクリプトを作成しました。

#!/bin/bash

# script to convert m4b (audiobook) files with embedded chapted (for eg. converted from Audbile) into individual chapter files

# required: ffmpeg; jg (json interpreter) & AtomicParsley (to embed pictures and add additional metadata to m4a/m4b AAC files)

# discover the file type (extension) of the input file
ext=${1##*.}
echo "extension: $ext"
# all files / folders are named based on the "shortname" of the input file
shortname=$(basename "$1" ".$ext")
picture=$shortname.jpg
chapterdata=$shortname.dat
metadata=$shortname.tmp
echo "shortname: $shortname"

# if an output type has been given on the command line, set parameters (used in ffmpeg command later)
if [[ $2 = "mp3" ]]; then
  outputtype="mp3"
  codec="libmp3lame"
elif [[ $2 = "m4a" ]]; then
  outputtype="m4a"
  codec="copy"
else
  outputtype="m4b"
  codec="copy"
fi
echo "outputtype: |$outputtype|"

# if it doesn't already exist, create a json file containing the chapter breaks (you can edit this file if you want chapters to be named rather than simply "Chapter 1", etc that Audible use)
[ ! -e "$chapterdata" ] && ffprobe -loglevel error \
            -i "$1" -print_format json -show_chapters -loglevel error -sexagesimal \
            >"$chapterdata"
read -p "Now edit the file $chapterdata if required. Press ENTER to continue."
# comment out above if you don't want the script to pause!

# read the chapters into arrays for later processing
readarray -t id <<< $(jq -r '.chapters[].id' "$chapterdata")
readarray -t start <<< $(jq -r '.chapters[].start_time' "$chapterdata")
readarray -t end <<< $(jq -r '.chapters[].end_time' "$chapterdata")
readarray -t title <<< $(jq -r '.chapters[].tags.title' "$chapterdata")

# create a ffmpeg metadata file to extract addition metadata lost in splitting files - deleted afterwards
ffmpeg -loglevel error -i "$1" -f ffmetadata "$metadata"
artist_sort=$(sed 's/.*=\(.*\)/\1/' <<<$(cat "$metadata" |grep -m 1 ^sort_artist))
album_sort=$(sed 's/.*=\(.*\)/\1/' <<<$(cat "$metadata" |grep -m 1 ^sort_album))
rm "$metadata"

# create directory for the output
mkdir -p "$shortname"
echo -e "\fID\tStart Time\tEnd Time\tTitle\t\tFilename"
for i in ${!id[@]}; do
  let trackno=$i+1
  # set the name for output - currently in format <bookname>/<tranck number>
  outname="$shortname/$(printf "%02d" $trackno). $shortname - ${title[$i]}.$outputtype"
  #outname=$(sed -e 's/[^A-Za-z0-9._- ]/_/g' <<< $outname)
  outname=$(sed 's/:/_/g' <<< $outname)
  echo -e "${id[$i]}\t${start[$i]}\t${end[$i]}\t${title[$i]}\n\t\t$(basename "$outname")"
  ffmpeg -loglevel error -i "$1" -vn -c $codec \
            -ss ${start[$i]} -to ${end[$i]} \
            -metadata title="${title[$i]}" \
            -metadata track=$trackno \
            -map_metadata 0 -id3v2_version 3 \
            "$outname"
  [[ $outputtype == m4* ]] && AtomicParsley "$outname" \
            --artwork "$picture" --overWrite \
            --sortOrder artist "$artist_sort" \
            --sortOrder album "$album_sort" \
            > /dev/null
done

必要に応じて、JSONファイル(.datファイル)をAudibleファイルとして編集し、チャプター名を「Chapter 1」、「Chapter 2」などとして指定できます。

例えば。最初は、ファイルの最初の部分を次のように読み取ることができます。

{
    "chapters": [
        {
            "id": 0,
            "time_base": "1/1000",
            "start": 0,
            "start_time": "0:00:00.000000",
            "end": 3206908,
            "end_time": "0:53:26.908000",
            "tags": {
                "title": "Chapter 1"
            }
        },

関連行を単純に変更すると、"title": "Introduction"結果の分割ファイルが変更されます。

答え2

ffmpeg(and ffprobe)に応じて、以下の最小限のアプローチを使用できますjq

#!/bin/bash
# Description: Split an 
# Requires: ffmpeg, jq
# Author: Hasan Arous
# License: MIT

in="$1"
out="$2"
splits=""
while read start end title; do
  splits="$splits -c copy -ss $start -to $end $out/$title.m4b"
done <<<$(ffprobe -i "$in" -print_format json -show_chapters \
  | jq -r '.chapters[] | .start_time + " " + .end_time + " " + (.tags.title | sub(" "; "_"))')

ffmpeg -i "$in" $splits

https://gist.github.com/aularon/c48173f8246fa57e9c1ef7ff694ab06f

答え3

ffprobeを使用すると、次のコマンドでその章の開始時間と終了時間を取得できます。

ffprobe -i fileName -print_format json -show_chapters

その後、ffmpegを使用して開始時刻と終了時刻を分割できます。

ffmpeg -i fileName -ss start -to end outFile

「-t」を使用しないでください。変換に時間がかかります。 「-ss」と「-to」はファイルの時間位置です。

これを自動化するにはスクリプトを作成する必要があります。

答え4

小さな間違いがあります。パラメータなしでこのスクリプトを使用してMP3オーディオブック(speech.mp3)を変換しようとすると、多くの空のm4bファイル(各章に1つずつ、各章にサイズが0)が得られます。

いくつかの変更を挿入します。

#!/bin/bash

# script to convert m4b (audiobook) files with embedded chapted (for eg. converted from Audbile) into individual chapter files

# required: ffmpeg; jg (json interpreter) & AtomicParsley (to embed pictures and add additional metadata to m4a/m4b AAC files)

# discover the file type (extension) of the input file
ext=${1##*.}
echo "extension: $ext"
# all files / folders are named based on the "shortname" of the input file
shortname=$(basename "$1" ".$ext")
picture=$shortname.jpg
chapterdata=$shortname.dat
metadata=$shortname.tmp
echo "shortname: $shortname"

extension="${1##*.}"

forcemp3=0

if [ "$extension" == "mp3" ]; then
  forcemp3=1
fi

# if an output type has been given on the command line, set parameters (used in ffmpeg command later)
if [[  $2 = "mp3"  ||  $forcemp3 = 1  ]] ; then
  outputtype="mp3"
  codec="libmp3lame"
  echo mp3
elif [[ $2 = "m4a" ]]; then
  outputtype="m4a"
  codec="copy"
else
  outputtype="m4b"
  codec="copy"
fi
echo "outputtype: |$outputtype|"

# if it doesn't already exist, create a json file containing the chapter breaks (you can edit this file if you want chapters to be named rather than simply "Chapter 1", etc that Audible use)
[ ! -e "$chapterdata" ] && ffprobe -loglevel error \
            -i "$1" -print_format json -show_chapters -loglevel error -sexagesimal \
            >"$chapterdata"
read -p "Now edit the file $chapterdata if required. Press ENTER to continue."
# comment out above if you don't want the script to pause!

# read the chapters into arrays for later processing
readarray -t id <<< $(jq -r '.chapters[].id' "$chapterdata")
readarray -t start <<< $(jq -r '.chapters[].start_time' "$chapterdata")
readarray -t end <<< $(jq -r '.chapters[].end_time' "$chapterdata")
readarray -t title <<< $(jq -r '.chapters[].tags.title' "$chapterdata")

# create a ffmpeg metadata file to extract addition metadata lost in splitting files - deleted afterwards
ffmpeg -loglevel error -i "$1" -f ffmetadata "$metadata"
artist_sort=$(sed 's/.*=\(.*\)/\1/' <<<$(cat "$metadata" |grep -m 1 ^sort_artist))
album_sort=$(sed 's/.*=\(.*\)/\1/' <<<$(cat "$metadata" |grep -m 1 ^sort_album))
rm "$metadata"

# create directory for the output
mkdir -p "$shortname"
echo -e "\fID\tStart Time\tEnd Time\tTitle\t\tFilename"
for i in ${!id[@]}; do
  let trackno=$i+1
  # set the name for output - currently in format <bookname>/<tranck number>
  outname="$shortname/$(printf "%02d" $trackno). $shortname - ${title[$i]}.$outputtype"
  #outname=$(sed -e 's/[^A-Za-z0-9._- ]/_/g' <<< $outname)
  outname=$(sed 's/:/_/g' <<< $outname)
  echo -e "${id[$i]}\t${start[$i]}\t${end[$i]}\t${title[$i]}\n\t\t$(basename "$outname")"
  ffmpeg -loglevel error -i "$1" -vn -c $codec \
            -ss ${start[$i]} -to ${end[$i]} \
            -metadata title="${title[$i]}" \
            -metadata track=$trackno \
            -map_metadata 0 -id3v2_version 3 \
            "$outname"
  [[ $outputtype == m4* ]] && AtomicParsley "$outname" \
            --artwork "$picture" --overWrite \
            --sortOrder artist "$artist_sort" \
            --sortOrder album "$album_sort" \
            > /dev/null
done

関連情報