get-tracks/get-tracks.sh

282 lines
5.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env sh
# From a single byte in hexadecimal per line
# to lines ending with 0a (hex for '\n').
regroup_lines(){
awk '
BEGIN {
line_start=1
}
{
if (line_start == 1)
line = $1;
else
line = line " " $1;
line_start = 0;
if ($1 == "0a") {
print line;
line_start = 1
}
}
END {
if (line_start == 0)
print line
}
'
}
# From to '
simple_quote(){
sed "s/e2 80 99/27/g"
}
# From / to ' - '
replace_slashes(){
sed "s/2f/20 2d 20/g"
}
remove_multibyte_characters(){
sed "s/e2 80 .. //g"
}
# Convert input into hexadecimal and a single byte per line.
to_hex_one_column(){
xxd -p -c 1
}
# Reverse hexadecimal to original value.
from_hex(){
xxd -p -r
}
# Remove non ascii characters, convert "" to "'", or remove invalid filename characters.
to_ascii(){
to_hex_one_column | # Convert input into hexadecimal and a single byte per line.
regroup_lines | # Required to easily match multi-byte characters.
simple_quote | # Convert "" to "'".
replace_slashes | # Replace / by ' - ', since it isn't a valid filename character.
remove_multibyte_characters | # Remove non ascii values.
from_hex # Convert back from hex.
}
process_end_of_songs(){
awk -v NONUMBER="$NONUMBER" -v SEPARATOR="$SEPARATOR" '
BEGIN {
OFS=" "
}
{
if (NR > 1) {
print timestamp, $1, title;
}
timestamp = $1;
if (NONUMBER == 1) {
title = $2
}
else {
if (NR < 10) {
title = "0" NR SEPARATOR $2
}
else {
title = NR SEPARATOR $2
}
}
for (i=3; i <= NF; i++) {
title = title " " $i
}
}
END {
print timestamp, "END_OF_FILE", title;
}
'
}
first_column_to_seconds(){
awk '
{
# from 10:30 to 630
n = split ($1, arr, ":")
for (i = 0; i <= n; i++) {
if (i == 0) {
v = arr[n-i];
}
else if (i == 1) {
v += 60 * arr[n-i];
}
else if (i == 2) {
v += 3600 * arr[n-i];
}
}
$1 = v;
print;
v = 0;
}
'
}
# Get a more usable time representation for the beginning and the end of songs.
process_time_file(){
to_ascii | first_column_to_seconds | process_end_of_songs
}
run_ffmpeg(){
file=$1
from=$2
to=$3
final_title=$4
LOG_LEVEL="-loglevel error"
FROM="-ss $from"
TO=""
if [ "$to" != "" ]; then
TO="-to $to"
fi
INPUT_FILE="$file"
OUTPUT_FILE="$final_title"
case "v$VERBOSITY" in
v0)
;;
v1)
echo "extracting '$final_title'"
;;
v2)
echo "ffmpeg $LOG_LEVEL $FROM $TO -i $INPUT_FILE '$OUTPUT_FILE'"
;;
*)
echo "verbosity is not set properly" >&2
exit 1
;;
esac
if [ "$SIMULATION" = "" ]; then
$(< /dev/null ffmpeg $LOG_LEVEL $FROM $TO -i "$INPUT_FILE" "$OUTPUT_FILE")
fi
}
rip(){
audio_file="$1"
time_file="$2"
process_time_file < "$time_file" | while read LINE; do
track_start=$(echo $LINE | cut -d ' ' -f 1)
track_end=$(echo $LINE | cut -d ' ' -f 2)
track_title=$(echo $LINE | cut -d ' ' -f 3-)
if [ "$track_end" = "END_OF_FILE" ]; then
track_end=""
fi
run_ffmpeg "${audio_file}" "${track_start}" "${track_end}" "${track_title}.${FORMAT}"
done
}
usage(){
echo "usage: $0 command"
echo "command: show <song-list>"
echo "command: rip <single-file-playlist> <song-list>"
echo
echo "song-list line format example: 1:30 My second track of the playlist"
echo "show output format: start end title"
echo
echo "envvar: SIMULATION, if non empty, do not invoke ffmpeg"
echo "envvar: NONUMBER, if equals to 1, do not write song number"
echo "envvar: FORMAT [mp3,ogg,opus,…], see the ffmpeg documentation"
echo "envvar: SEPARATOR [separator] (default: ' - '), write song number, with this separator"
echo " example with SEPARATOR='_': song names will be 01_song.opus 02_song.opus…"
echo "envvar: VERBOSITY [0-3] (default: 1)"
echo " verbosity 0: no output except errors from ffmpeg"
echo " verbosity 1: simple indications on the current track being extracted"
echo " verbosity 2: print actual ffmpeg commands the script currently runs"
}
if [ $# -lt 1 ]; then
usage
exit 0
fi
command=$1
shift
if [ "$FORMAT" = "" ]; then
echo "default FORMAT: opus"
FORMAT="opus"
else
echo "FORMAT: $FORMAT"
fi
if [ "$VERBOSITY" = "" ]; then
echo "default VERBOSITY: 1"
VERBOSITY=1
else
echo "VERBOSITY level: $VERBOSITY"
fi
if [ "$NONUMBER" = "" ]; then
echo "default NONUMBER: disabled"
NONUMBER=0
# Assume that there should be a separator.
if [ "$SEPARATOR" = "" ]; then
echo "default SEPARATOR: ' - '"
SEPARATOR=" - "
else
echo "SEPARATOR: '$SEPARATOR'"
fi
else
echo "NONUMBER: won't prefix tracks"
SEPARATOR=""
fi
if [ "$SIMULATION" != "" ]; then
echo "SIMULATION envvar is set: this is a simulation."
fi
</dev/null xxd -p >/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
echo "xxd: you don't have an xxd program with '-p' option." 1>&2
exit 1
fi
</dev/null xxd -r >/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
echo "xxd: you don't have an xxd program with '-r' option." 1>&2
exit 1
fi
case "x-${command}" in
x-show)
# Takes the audio file in first parameter
if [ $# -ne 1 ]; then
echo "Usage: $0 show time-stamps-file" >&2
exit 1
fi
process_time_file < "$1"
;;
x-rip)
# Takes the audio file in first parameter
if [ $# -ne 2 ]; then
echo "Usage: $0 show music-file time-stamps-file" >&2
exit 1
fi
rip "$1" "$2"
;;
*)
usage 1>&2
exit 1
esac