3

I have a bunch of mp3 files in a folder. They are recorded from cassette tape, and the individual tracks need to be separated out. This is one one the filenames:

Gobbolino the Witch's Cat, 10:52 The Hare & Tortoise, 14:52 The Shoe Tree, 24:22 The Emperors New Clothes, 34:11 The Red Nightcaps, 37:07 Aldo in Arcadia (1), 40:37 The Forest Troll.mp3

You can see it has timestamps within the filename, indicating the start of each track. The first track is not timestamped since it always starts at 00:00. And the last track should always go to the end of the mp3. Somehow, I want to extract these timestamps in order to create separate files.

If the file above were split correctly the output would be:

Gobbolino the Witch's Cat.mp3
The Hare & Tortoise.mp3
The Shoe Tree.mp3
The Emperors New Clothes.mp3
The Red Nightcaps.mp3
Aldo in Arcadia (1).mp3
The Forest Troll.mp3

I know how to loop thru files, and how to cut a file with ffmpeg, but I don't know how to extract the timestamps and track names from the filename. I'm using zsh, and here is my current code:

for file in *; do
  if [[ -f "$file" ]]; then
    # extract timestamps and loop thru, for each timestamped section
    ffmpeg -ss TIMESTAMP_START -to TIMESTAMP_END -I "$file" -acodec copy TRACK_NAME.mp3
  fi
done

UPDATE

My spec for this problem has changed. The filename looks like this:

Tape 1 - Gobbolino the Witch's Cat, 11-06 The Hare & Tortoise, 14-25 The Shoe Tree, 24-06 The Emperors New Clothes, 34-27 The Red Nightcaps, 37-29 Aldo in Arcadia (1), 40-40 The Forest Troll.mp3

i.e it has an Album name at the beginning, and there are hyphens in the timestamps instead of colons (filenames can't have colons in macOS). Also, I wanted to insert some mp3 tags into the files, and put the tracks for each album in its own album folder.

I based my solution on Gilles one below. The script looks like this:

setopt interactive_comments
for file in *(.); do
    extension=$file:e
    rest=$file:r
    timestamp_start=0:00
    timestamp_duration=$(ffprobe -i "$file"  -show_entries format=duration -v quiet -of csv="p=0" -sexagesimal -sexagesimal)
    timestamp_duration=${timestamp_duration%.*}
    tracknum=1
    while [[ $rest =~ ,\ *([0-9:]+-[0-9][0-9])\ * ]]; do
        track_name="$rest[1,$MBEGIN-1]"
        if [[ "$track_name" == *"Tape "* ]]; then
       albumname="${track_name%% - *}"
           track_name="${track_name#* - }"
           echo "\n\nALBUM NAME $albumname\n"
           mkdir $albumname
        fi
        rest=$rest[$MEND+1,-1]
        timestamp_end=$match[1]
        timestamp_end="${timestamp_end//-/:}"

    # echo "$timestamp_start $timestamp_end $track_name.$extension"
        ffmpeg -ss $timestamp_start -to $timestamp_end -i $file -acodec libmp3lame -ac 2 -ab 256k -ar 44100 -metadata album="$albumname" -metadata title="$track_name" -metadata track="$tracknum" $track_name.$extension
        mv $track_name.$extension $albumname
        timestamp_start=$timestamp_end
        tracknum=$((tracknum+1))
        last_track_name="$rest:r"
    done
    if [[ -n $timestamp_end ]]; then
        # echo "$timestamp_start $timestamp_duration $last_track_name.$extension"
        ffmpeg -ss $timestamp_start -to $timestamp_duration -i $file -acodec libmp3lame -ac 2 -ab 256k -ar 44100 -metadata album="$albumname" -metadata title="$last_track_name" -metadata track="$tracknum" $last_track_name.$extension
        mv $last_track_name.$extension $albumname
    fi
done
2
  • 1
    Pick a better text processing language, e.g. perl, where this is easy. Hint: compute the timestamp of end of $file, and set the list of output files to "00:00 $file,$eof_timestamp EOF", to make it easier to parse.
    – waltinator
    Jan 1, 2022 at 0:04
  • @waltinator Nonsense. Zsh's text processing capabilities are a bit haphazard, but they're well up to this task. Even plain sh could cope with a bit more effort. Jan 1, 2022 at 11:25

2 Answers 2

4

Use a loop over the file name to match track delimiters using regular expression matching with the =~ conditional expression operator. The regular expression ,\ *([0-9:]+:[0-9][0-9])\ * matches a comma followed by a timestamp with optional spaces around it.

$file:e and $file:r extract the file extension through history modifiers.

Instead of looping over all files and then matching just the regular files, use a glob qualifier to match just regular files.

for file in *(.); do
    extension=$file:e
    rest=$file:r
    timestamp_start=0:00
    timestamp_end=
    while [[ $rest =~ ,\ *([0-9:]+:[0-9][0-9])\ * ]]; do
        track_name=$rest[1,$MBEGIN-1]
        rest=$rest[$MEND+1,-1]
        timestamp_end=$match[1]
        ffmpeg -ss $timestamp_start -to $timestamp_end -I $file -acodec copy $track_name.$extension
        timestamp_start=$timestamp_end
    done
    if [[ -n $timestamp_end ]]; then
        ffmpeg -ss $timestamp_end -I $file -acodec copy $rest.$extension
    else
        : # If you want special processing for single-track files, it goes here.
    fi
done
1
  • Thanks, I based my solution on this, and updated the question
    – beekay
    Jan 3, 2022 at 16:45
-1
 awk -F "," '{for(i=1;i<=NF;i++){gsub(/[0-9]{2}:[0-9]{2}/,"",$i);gsub(/^[[:space:]]*/,"",$i);print $i".mp3"}}' file.txt

output

Gobbolino the Witch's Cat.mp3
The Hare & Tortoise.mp3
The Shoe Tree.mp3
The Emperors New Clothes.mp3
The Red Nightcaps.mp3
Aldo in Arcadia (1).mp3
The Forest Troll.mp3.mp3
1
  • Let me know the reason for down vote Jan 2, 2022 at 19:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .