Detect truncated mp3 or ogg files

5

I recently had a hard disk failure and could not rescue all my music files. Since I didn't have enough storage available to do a full backup of the disk, I tried to cp as many tracks as possible, but now I am left with some files that end prematurely.

I already found this answer to find out "the song length" and this tool that does the same. For one broken file, I get this output:

ffmpeg -i broken.mp3 2>&1 | grep Duration
  Duration: 00:04:18.14, start: 0.025057, bitrate: 92 kb/s

mp3_check -a broken.mp3 2>&1| grep SONG
SONG_LENGTH         01:43.05

So ffmpeg seems to rely on some metadata (04:18 is the duration that is also displayed in my media player), while mp3_check seems to actually read the whole file. I could use this to write a script that covers mp3, but:

Is there an easier solution rather than comparing ffmpeg and mp3_check output in order to find broken files?

How would I do this with Ogg files, where no mp3_check is available?

Jasper

Posted 2014-04-27T16:21:46.197

Reputation: 798

mp3diags scans and detects truncated files. If you choose so, it even repairs them. It is available for Linux and Windows – nixda – 2015-08-28T13:04:09.403

I already have written a bash script for the first question, shall I include that in the question? Or does this topic fit better to Stackoverflow or Unix+Linux? – Jasper – 2014-04-27T18:32:40.127

I would encourage you to post the script as an answer anyway, and if someone has a solution, so be it. But maybe someone else will also find it helpful – a win-win situation. The question is perfectly fine here. We don't disallow scripting questions as long as they're in the context of anything a power user would do, but not exclusive to programming. And Unix/Linux questions are all on topic here too. – slhck – 2014-04-27T18:35:46.543

Answers

2

This is the script I'm now using:

#!/bin/bash

echo "checking $1"

find "$1" -name "*.mp3" | while read filename; do
  echo "checking $(basename "$filename")"
  ffmpeg_dur=$(ffmpeg -i "$filename" 2>&1 | awk -F: '($1 ~ /Duration/) {printf "60*%d+%d\n", $3, $4}' | bc)
  # mp3_check_dur=$(mp3_check -a "$filename" 2>&1 | awk -F'[ :.]+' '($1 ~ /SONG_LENGTH/) {printf "60*%d+%d\n", $2, $3}' | bc )
  mp3info_dur=$(mp3info -x "$filename" 2>&1 | awk -F'[ :.]+' '($1 ~ /Length/) {printf "60*%d+%d\n", $2, $3}' | bc )

  if [[ -z $ffmpeg_dur ]] ; then  # some files are so broken that ffmpeg doesn't print a length
    echo "ERROR (ffmpeg): $filename"
  else
    len_diff=$(( $ffmpeg_dur - $mp3_check_dur ))
    if [[ $len_diff -gt 0 ]] ; then
      echo -e "ERROR (length): $filename\t${len_diff}"
    fi
  fi

done

The duration reported by ffmpeg is always longer than the one of mp3_check and mp3info. There sometimes is a 1 second difference between the latter as well. mp3info is available as package for some linux distributions (Ubuntu, Arch, ...?), mp3_check has to be built from source.

Jasper

Posted 2014-04-27T16:21:46.197

Reputation: 798

Won't work "as is". mp3_check_dur should be replaced by mp3info_dur in $((ffmpeg_dur - mp3_check_dur)) – Andrey Regentov – 2015-01-25T05:25:02.367