#!/usr/bin/env sh get_time(){ echo "$*" | sed "s/[ \t].*//" } get_title(){ echo "$*" | cut -d ' ' -f 2- } reverse_word_order(){ local result= for word in $@; do result="$word $result" done echo "$result" } # bc is mandatory: arythmetic operations are very limited in ash. get_seconds(){ number_position=0 value=0 values=$(echo "$*" | sed 's/:/\ /g' | sed "s/^0//") for i in $(reverse_word_order $values); do case $number_position in 0) value=$(echo "$value + $i " | bc);; 1) value=$(echo "$value + (60 * $i)" | bc);; 2) value=$(echo "$value + (3600 * $i)" | bc);; *) echo "invalid timecode $*"; exit 1;; esac number_position=$((number_position+1)) done echo $value } # Get a more usable time representation for the beginning and the end of songs. get_values(){ audio_file="$1" time_file="$2" track_number=1 while read X; do if [ $track_number -ne 1 ]; then to_unformatted=$(get_time $X) to_s=$(get_seconds $to_unformatted) echo "$from_s $to_s $title" fi from_unformatted=$(get_time $X) from_s=$(get_seconds $from_unformatted) title=$(get_title $X) if [ "$NONUMBER" = "" ]; then track_n=$track_number if [ $track_number -lt 10 ]; then track_n="0$track_number" fi title="${track_n}${SEPARATOR}${title}" fi track_number=$(echo $track_number + 1 | bc) done < "${time_file}" echo "$from_s $eof_marker $title" } run_ffmpeg(){ file=$1 from=$2 duration=$3 final_title=$4 LOG_LEVEL="-loglevel error" FROM="-ss $from" if [ "$duration" = "" ]; then DURATION="" else DURATION="-t $duration" fi INPUT_FILE="-i $file" OUTPUT_FILE="$final_title" case "v$VERBOSITY" in v0) ;; v1) echo "extracting '$final_title'" ;; v2) echo "ffmpeg $LOG_LEVEL $FROM $DURATION $INPUT_FILE '$OUTPUT_FILE'" ;; *) echo "verbosity is not set properly" >&2 exit 1 ;; esac if [ "$SIMULATION" = "" ]; then $(< /dev/null ffmpeg $LOG_LEVEL $FROM $DURATION $INPUT_FILE "$OUTPUT_FILE") fi } rip(){ track_start_s=0 track_end_s=0 audio_file="$1" time_file="$2" get_values "$audio_file" "$time_file" | while read LINE; do track_start_s=$(echo $LINE | cut -d ' ' -f 1) track_end_s=$(echo $LINE | cut -d ' ' -f 2) track_title=$(echo $LINE | cut -d ' ' -f 3-) if [ "$track_end_s" != "$eof_marker" ]; then track_duration=$(echo "$track_end_s - $track_start_s" | bc) else track_duration="" fi run_ffmpeg "${audio_file}" "${track_start_s}" "${track_duration}" "${track_title}.${FORMAT}" done } usage(){ echo "usage: $0 command" echo "command: show " echo "command: rip " 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 non empty, 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 exept 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" fi if [ "$VERBOSITY" = "" ]; then echo "default VERBOSITY: 1" VERBOSITY=1 fi # Assume that there should be a separator. if [ "$SEPARATOR" = "" ]; then echo "default SEPARATOR: ' - '" SEPARATOR=" - " fi if [ "$SIMULATION" != "" ]; then echo "SIMULATION envvar is set: this is a simulation." fi # soxi provides the total length of the music file. #which soxi 2>/dev/null #total_length=$(soxi -D "${audio_file}" | sed "s/\..*//") # integer values only eof_marker="END_OF_FILE" case "x-${command}" in x-show) # Takes the audio file in first parameter if [ $# -ne 2 ]; then echo "Usage: $0 show music-file time-stamps-file" >&2 exit 1 fi get_values "$1" "$2" ;; 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