我一直在努力用一点bash代码来根据mp3文件的时间长度生成mp4chaps.chapter .txt文件,以便当我将其转换为有声读物(m4b)时,我可以再次添加章节信息。
我想我已经解决了它,但我很好奇这是否可以做得更优雅?
我的poc代码(假设在您运行此脚本的文件夹中有mp3文件):
#!/usr/bin/env bash
echo "00:00:00.000 Chapter 1"
i=2
total_seconds=0
total_millis=0
for mp3 in *.mp3; do
duration=$(ffprobe -v quiet -print_format json -show_format "${mp3}" | jq ".format.duration"|sed 's/"//g')
millisecs="$(echo $duration | awk -F'.' '{print $2}')"
millisecs="${millisecs#"${millisecs%%[!0]*}"}"
total_millis=$((total_millis + millisecs))
secs=$((total_millis / 1000000))
total_millis=$((total_millis - (secs * 1000000)))
total_seconds=$((total_seconds + $(echo $duration | awk -F'.' '{print $1}') + secs))
hour=$((total_seconds / (60 * 60)))
minutes=$(((total_seconds - (hour * 60 * 60)) / 60))
seconds=$((total_seconds - (hour * 60 * 60) - (minutes * 60)))
hour=$(printf "%02d" $hour)
minutes=$(printf "%02d" $minutes)
seconds=$(printf "%02d" $seconds)
echo "$hour:$minutes:$seconds.${millisecs:0:3} Chapter $i"
i=$((i + 1))
done
mp4chapts.chapter .txt文件的格式如下:
00:00:00.000 Chapter 1
00:11:46.612 Chapter 2
00:22:55.525 Chapter 3
00:41:22.670 Chapter 4
01:09:17.337 Chapter 5
ffprobe json格式为:
{
"format": {
"filename": "Book with chapter 01.mp3",
"nb_streams": 1,
"nb_programs": 0,
"format_name": "mp3",
"format_long_name": "MP2/3 (MPEG audio layer 2/3)",
"start_time": "0.025057",
"duration": "706.612245",
"size": "5657329",
"bit_rate": "64050",
"probe_score": 51,
"tags": {
"title": "a good book 01",
"artist": "The Author",
"album": "fantastic album information",
"track": "01",
"publisher": "Good Media, Inc",
"genre": "Fiction",
"comment": "Read by ...",
"copyright": "2019",
"date": "2021"
}
}
}
我使用这个json的持续时间。
请尝试以下操作:
echo "00:00:00.000 Chapter 1"
i=2
total_millis=0
for mp3 in *.mp3; do
duration=$(ffprobe -v quiet -show_entries format=duration "${mp3}" | sed -n 's/duration=//p')
millisecs=$((${duration%.*} * 1000 + 10#${duration#*.} / 1000))
((total_millis += millisecs))
hour=$((total_millis / 3600 / 1000))
mins=$(((total_millis - hour*3600*1000) / 60 / 1000))
secs=$(((total_millis - hour*3600*1000 - mins*60*1000) / 1000))
millis=$((total_millis - hour*3600*1000 - mins*60*1000 - secs*1000))
printf "%02d:%02d:%02d.%03d Chapter %dn" "$hour" "$mins" "$secs" "$millis" "$i"
((i++))
done
恐怕还是不够优雅。我统一了累加的变量进入total_millis
的时间使代码更短比原来的。顺便说一下,echo
中millisecs
的值行可能不正确。它是直接从当前章节计算出来的不反映total_millis
的值
(替代)
如果您的date
命令支持-d
选项,这里有一个作弊方法:
echo "00:00:00.000 Chapter 1"
i=2
total_secs=0
for mp3 in *.mp3; do
duration=$(ffprobe -v quiet -show_entries format=duration "${mp3}" | sed -n 's/duration=//p')
total_secs=$(echo "$total_secs + $duration" | bc)
day=$(date -d "@$total_secs" -u +%d)
hour=$(( 10#$(date -d "@$total_secs" -u +%H) + (10#$day - 1) * 24 ))
printf "%02d:%s Chapter %dn" "$hour" "$(date -d "@$total_secs" -u +%M:%S.%3N)" "$((i++))"
done
date
命令的行为严重依赖于平台,上述解决方案将不可移植。