0

I have created a script that can split an audio file into smaller files via ffmpeg using timestamps, and another script which can take in timestamps & names in batches. My use for this script personally is downloading complete albums in single files off youtube and then copying the timestamps from the description or a comment to quickly separate each song into it's own individual file.

The problem lies somewhere in the batch splitting script, in a nutshell, sometimes it works perfectly fine, but sometimes it'll fail naming files correctly (missing characters) or in the worst case, missing songs/timestamps.

Here's the splitting script (relevant because the script I need help with relies on this):

#!/bin/sh
#Use ffmpeg to split an audio file using timestamps.

filename=$(echo $1 | sed 's: :\ :g' | rev | cut -d '.' -f2- | rev) #Filename without extension format=$(echo $1 | rev | cut -d '.' -f1 | rev) #File extension fileout=$(echo $filename_out.$format | sed 's: :\ :g') #Output file name timestart=$2 timestop=$3

if [ $3 ]; then timestart=$2 timestop=$3 if [ "$4" ]; then fileout=$(echo $4.$format | sed 's: :\ :g') #Sets a name for the output file if one was specified. fi elif [ $2 ]; then timestart=00:00:00 timestop=$2 else echo "Usage: split_audio [file] [time arg 1] [time arg 2] [filename arg] Example: split_audio file.mp3 00:20:00 Creates file with content from start to minute 20 Example: split_audio file.mp3 20:00 30:00 Creates file with content from minute 20 to minute 30 Example: split_audio file.mp3 20:00 end custom Creates file named custom with content from minute 20 to file end " exit fi

if [ "$3" == "end" ]; then ffmpeg -ss $timestart -i "$filename.$format" -c:a copy -async 1 "$fileout" else ffmpeg -ss $timestart -to $timestop -i "$filename.$format" -c:a copy -async 1 "$fileout" fi

Here's the batch splitting script (the one where the problem most likely lies):

#!/bin/sh
#Designed so you can copy text (for instance from a youtube vid description or comment) that has timestamps for songs in file and automatically dividing the file down to files for each individual song.
#Depends on accompanying script: split_audio.sh

mainscript=/home/$USER/Scripts/split_audio.sh mainfile=$(echo $1 | sed 's: :\ :g') timefile=$(echo $2 | sed 's: :\ :g') pass=0

if [ -z "$2" ]; then echo "Usage: split_audio [file] [file] Example: split_audio_batch filename.extension timestamps.extension Divides file.mp3 to multiple files using the names and timestamps from timestamps.txt" exit fi

while read line; do fileout=$(echo $pass. $songname | tr -s ' ') if [ $pass -eq 0 ]; then last=00:00 songname=$(echo $line | grep -zoe [A-z\ ]) else if [ $(echo $line | grep -oP '\d{2}:\d{2}:\d{2}') ]; then current=$(echo $line | grep -oP '\d{2}:\d{2}:\d{2}') songname=$(echo $line | grep -zoe [A-z\ ]) elif [ $(echo $line | grep -oP '\d{2}:\d{2}') ]; then current=$(echo $line | grep -oP '\d{2}:\d{2}') songname=$(echo $line | grep -zoe [A-z\ ]) elif [ $(echo $line | grep -oP '\d:\d{2}') ]; then current=$(echo $line | grep -oP '\d:\d{2}') songname=$(echo $line | grep -zoe [A-z\ ]) fi #echo $mainscript "$mainfile" $last $current "$fileout" $mainscript "$mainfile" $last $current "$fileout" last=$current fi pass=$(($pass + 1)) done < $timefile

fileout=$(echo $pass. $songname | tr -s ' ') #echo $mainscript "$mainfile" $last end "$fileout" $mainscript "$mainfile" $last end "$fileout"

You could test the scripts going to just about any album on youtube and using youtube-dl to fetch them then running the scripts on them, but to provide actually specific examples:

Example 1

1. 00:00 You're Coming With Me
2. 05:35 The Escape
3. 09:44 Predator and Prayer
4. 14:49 Anxious Darwinians
5. 20:13 Panophobia
6. 24:07 Ideomotor
7. 32:14 Lonely Solipsist
8. 35:59 Dead Ocean
9. 42:18 The Mass of the Earth
10. 46:59 Everybody Wants You (Dead)
11. 51:59 Revenge of the Dadaists

Result: Everything worked perfectly

Example 1 result

Example 2

Synopsis - 0:00
Rise and Fall - 0:32
Born Dead Buried Alive - 4:34
Take a Bow - 9:07
Trophy Kill - 13:13
Business Suits and Combat Boots - 16:52
Serendipity - 22:21
Memento Mori - 26:03
Void of Sympathy - 29:13
Chiaroscuro - 33:34
Forget Tomorrow - 34:42

Result: Some files are ok, some are missing the first character in their name,one file was entirely skipped.

The results of example 2

My one hint is this error from ffmpeg: Parse error, at least 3 arguments were expected, only 1 given in string 'hiaroscuro - 33:34'

I am completely stumped over this, because if I uncomment that debug line (#echo $mainscript "$mainfile" $last $current "$fileout") and comment out the main command, it will return every command as if it were completely correct (at least as far as echo can go, it can't print the backslashes but those are clearly being inserted correctly based on the fact that a lot of the files are working) in fact if i copy the command echo prints wholesale (but add in that missing backspace for any spaces), the command will work even if what should have been the exact same command did not work when the script ran it.

However if I do the same but do not comment out the main command (as in make the script run normally alongside the echo command), the echo output will go as wrong as the rest of the script does.

This is really as deep as my debugging knowledge goes and since it's showing me no issues I have no clue what's going on. I've tried running the script in bash, dash and zsh but all of them just net me the exact same results (with the exception being that bash complains about ignored null byte in input).

If you want to test run the script you can technically do so with any audio or video file that is long enough to include all the timestamps in the timestamp examples. Or you can go the longer route of using youtube-dl --extract-audio to download albums off youtube and then run the script on those downloaded files as it was meant to.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
Cestarian
  • 2,051
  • Double-quote your variables and $(...) expressions when you use them. So [ "$x" ] instead of [ $x ], etc – Chris Davies Jul 31 '22 at 21:44
  • I haven't analyzed the scripts thoroughly, but I suspect ffmpeg or something is consuming stdin. – Kamil Maciorowski Jul 31 '22 at 21:47
  • @roaima could you be more specific? variables and $(...) expressions is very broad. I did double qoute them where i felt it was needed, do you just mean the ones where I'm checking if the user passed arguments or not? – Cestarian Jul 31 '22 at 21:49
  • @KamilMaciorowski Yes! That solved it. Thank you! – Cestarian Jul 31 '22 at 21:52
  • @Cestarian certainly. Whenever you use a $ value you should ensure the variable or expression is contained in double quotes. This prevents the shell parsing the value, potentially splitting at whitespace, etc. Consider a=one and then [ -n $a ] && echo yes. Now repeat but with a='one two'. Finally repeat both attempts but with "$a" instead of plain $a – Chris Davies Aug 01 '22 at 09:52

0 Answers0