私は注意を払ってきました。この回答ffmpeg
LinuxMintは私の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