나는주의를 기울여 왔습니다.이 답변ffmpeg
LinuxMint에서 내 Audible 오디오북 중 일부를 변환하고 재생하는 데 사용됩니다 . 각 책은 소스 파일이지만 ffmpeg
변환 시작 부분에 모든 장이 나열되어 있음을 확인했습니다.
책을 장으로 나누는 방법이 있습니까 ffmpeg
? 각 장을 별도의 파일로 변환(장별로 나누기)합니까? 단독으로 사용하는 것이 가장 좋지만 ffmpeg
다른 프로그램/스크립트를 (와 함께 ffmpeg
) 사용하는 것도 옵션입니다...
(저는 (파이썬 스크립트를 사용하여) 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 파일(각 장마다 하나씩, 각 장마다 크기가 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