mirror of
https://github.com/foo-dogsquared/dotfiles.git
synced 2025-01-31 04:57:57 +00:00
Create a split album script in Oil
This commit is contained in:
parent
90be31cc52
commit
37036a1e2c
222
bin/split-album
Executable file
222
bin/split-album
Executable file
@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env oil
|
||||
|
||||
# The following are the dependencies for this script.
|
||||
#
|
||||
# * Oil shell v0.8.8
|
||||
# * coreutils v8.32
|
||||
# * file 5-32
|
||||
# * ffmpeg v4.3.1
|
||||
# * ripgrep v12.1.1 with SIMD runtime
|
||||
|
||||
shopt --set strict:all
|
||||
|
||||
const show_help = "A small script for splitting files into tracks, perfect for full album releases and audiobooks.
|
||||
|
||||
split-album [options...] -tf \$TIMESTAMP_FILE -af \$ALBUM_FILE
|
||||
|
||||
Options:
|
||||
-h, --help Show the help section.
|
||||
--descriptive-help Show a helpful tutorial-esque description of the program.
|
||||
-af, --audio-file [file] Set the audio file to be split.
|
||||
-tf, --timestamp-file [file] Set the timestamp file to be used for splitting.
|
||||
-t, --title [title] Set the title of the album.
|
||||
-d, --date [date] Set the publication date of the album.
|
||||
-a, --author [author] Set the author of the album.
|
||||
--json Print the JSON data for use with other applications.
|
||||
--strict Set to be strict, exiting when an invalid timestamp is encountered.
|
||||
|
||||
Environment variables:
|
||||
EXTENSION The extension to be used. This is used in conjunction with FFmpeg
|
||||
where it can detect the codec to be converted automatically with it.
|
||||
|
||||
When any of the required metadata is missing (i.e., title, date, author), it will be prompted.
|
||||
"
|
||||
|
||||
const show_descriptive_help = "Split an album with a timestamp file.
|
||||
The timestamp file contains a starting timestamp (any format that ffmpeg accepts in seeking but it is recommended in HH:MM:SS) and the title of the chapter/track.
|
||||
For more information, see https://trac.ffmpeg.org/wiki/Seeking.
|
||||
Lines starting with '#' and empty lines will be ignored.
|
||||
|
||||
The following is an example of the content of a timestamp file.
|
||||
|
||||
\`\`\`
|
||||
00:00:00 Introduction
|
||||
00:03:54 It's the R-rated scene
|
||||
00:05:43 Boring exposition at the cafe
|
||||
00:16:54 Expedition time
|
||||
00:21:51 Fighting time
|
||||
00:22:22 Shower scene
|
||||
\`\`\`
|
||||
|
||||
You can also create a timestamp file in JSON format.
|
||||
It is the equivalent of '.chapters' from the JSON output (i.e., '--json').
|
||||
The equivalent JSON of the previous example would be this:
|
||||
|
||||
\`\`\`
|
||||
[
|
||||
{
|
||||
\"timestamp\": \"00:00:00\",
|
||||
\"title\": \"Introduction\"
|
||||
},
|
||||
{
|
||||
\"timestamp\": \"00:03:54\",
|
||||
\"title\": \"It's the R-rated scene\"
|
||||
},
|
||||
{
|
||||
\"timestamp\": \"00:05:43\",
|
||||
\"title\": \"Boring exposition at the cafe\"
|
||||
},
|
||||
{
|
||||
\"timestamp\": \"00:16:54\",
|
||||
\"title\": \"Expedition time\"
|
||||
},
|
||||
{
|
||||
\"timestamp\": \"00:21:51\",
|
||||
\"title\": \"Fighting time\"
|
||||
},
|
||||
{
|
||||
\"timestamp\": \"00:22:22\",
|
||||
\"title\": \"Shower scene\"
|
||||
}
|
||||
]
|
||||
\`\`\`
|
||||
|
||||
There will be a folder created with the safe name of the album (in kebab-case) containing the split tracks.
|
||||
The original file will be kept, do what you want with it.
|
||||
"
|
||||
|
||||
const EXTENSION = ${EXTENSION:-"ogg"}
|
||||
|
||||
var audio_file = ''
|
||||
var timestamp_file = ''
|
||||
|
||||
var album = ''
|
||||
var author = ''
|
||||
var pub_date = ''
|
||||
|
||||
var prints_json = false
|
||||
var strict_mode = false
|
||||
|
||||
while test $len(ARGV) -gt 0 {
|
||||
case $[ARGV[0]] {
|
||||
-h|--help)
|
||||
write -- $show_help
|
||||
exit
|
||||
;;
|
||||
--descriptive-help)
|
||||
write -- $show_descriptive_help
|
||||
exit
|
||||
;;
|
||||
-af|--audio-file)
|
||||
set audio_file = $2
|
||||
shift 2
|
||||
;;
|
||||
-tf|--timestamp-file)
|
||||
set timestamp_file = $2
|
||||
shift 2
|
||||
;;
|
||||
-a|--author)
|
||||
set author = $2
|
||||
shift 2
|
||||
;;
|
||||
-d|--date)
|
||||
set pub_date = $2
|
||||
shift 2
|
||||
;;
|
||||
-t|--title)
|
||||
set album = $2
|
||||
shift 2
|
||||
;;
|
||||
--strict)
|
||||
set strict_mode = true
|
||||
shift
|
||||
;;
|
||||
--json)
|
||||
set prints_json = true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
}
|
||||
}
|
||||
|
||||
proc warnf(msg, @args) {
|
||||
>&2 printf "${msg}\\n" @args
|
||||
}
|
||||
|
||||
proc errorf(msg, @args) {
|
||||
>&2 printf "${msg}\\n" @args
|
||||
exit 1
|
||||
}
|
||||
|
||||
proc prompt(msg, :out, prefix = ">> ") {
|
||||
>&2 printf '%s\n%s' $msg $prefix
|
||||
read --line
|
||||
setref out = $_line
|
||||
}
|
||||
|
||||
proc kebab-case(word) {
|
||||
write -- $word | sed --regexp-extended --expression 's/./\L&/g' --expression 's/\s+/-/g' --expression 's/[^.a-z0-9-]//g' --expression 's/^-+|-+$//g' --expression 's/-+/-/g'
|
||||
}
|
||||
|
||||
test -f $audio_file || errorf '%s is not a regular file' $audio_file
|
||||
test -f $timestamp_file || errorf '%s is not a regular file' $timestamp_file
|
||||
|
||||
test $album || prompt "What is the title of the album?" :album
|
||||
test $author || prompt "Who is the author of the album?" :author
|
||||
test $pub_date || prompt "When is the album published?" :pub_date
|
||||
|
||||
const output_data = {}
|
||||
set output_data['file'] = $audio_file
|
||||
set output_data['chapters'] = []
|
||||
|
||||
const timestamp_regex = / %start digit{2,} ':' digit{2} ':' digit{2} %end /
|
||||
var has_error = false
|
||||
|
||||
# Also cleans up the timestamp file with comments and empty lines.
|
||||
# I just want to improve the timestamp format (a little bit).
|
||||
case $(file --mime-type --brief $timestamp_file) {
|
||||
"application/json")
|
||||
json read :output_data['chapters'] < $audio_file
|
||||
;;
|
||||
"text/plain")
|
||||
cat $timestamp_file | sed --regexp-extended --expression '/^\s*$/d' --expression '/^#/d' | while read --line {
|
||||
var chapter = {}
|
||||
set chapter['title'] = $(write -- $_line | cut -d' ' -f2-)
|
||||
set chapter['timestamp'] = $(write -- $_line | cut -d' ' -f1)
|
||||
|
||||
write -- ${chapter['timestamp']} | rg --quiet $timestamp_regex || {
|
||||
warnf "'%s' %s is not a valid timestamp" ${chapter['timestamp']} ${chapter['title']}
|
||||
set has_error = true
|
||||
continue
|
||||
}
|
||||
|
||||
_ output_data['chapters'].append(chapter)
|
||||
}
|
||||
;;
|
||||
}
|
||||
|
||||
if (strict_mode and has_error) { exit 1 }
|
||||
|
||||
const title_slug = $(kebab-case $album)
|
||||
mkdir -p $title_slug
|
||||
|
||||
const chapter_len = len(output_data['chapters'])
|
||||
|
||||
for index in @(seq $[chapter_len]) {
|
||||
set chapter = output_data['chapters'][Int(index) - 1]
|
||||
var start = chapter['timestamp']
|
||||
var end = output_data['chapters'][Int(index)]['timestamp'] if Int(index) != chapter_len else null
|
||||
var filename = $(printf "%s/%.2d-%s.%s" $title_slug $index $(kebab-case ${chapter['title']}) $EXTENSION)
|
||||
|
||||
warnf "[%d/%d] %s\\r" ${index} ${chapter_len} ${chapter['title']}
|
||||
if (Int(index) != chapter_len) {
|
||||
ffmpeg -loglevel quiet -nostdin -i $audio_file -ss $start -to $end $filename
|
||||
} else {
|
||||
ffmpeg -loglevel quiet -nostdin -i $audio_file -ss $start $filename
|
||||
}
|
||||
}
|
||||
|
||||
if (prints_json) { json write :output_data }
|
||||
|
Loading…
Reference in New Issue
Block a user