Locating bad movie copies

A while back is setup a HTPC/NAS in order to store all my movies and make them accessible over the network to devices like the Roku and our Blu-ray player.

Copying the movies was a long an tedious process, but worth the end result I think. One problem that occurred however was that I ended up with a few movies where either the audio did not copy or the wrong language of audio was copied. I'd copied hundreds of movies and tv shows at this point so trying to go through them all manually to find those which had a bad copy would have taken a while.

I ended up writing a little PHP script which with the help of Libav's avprobe to extract stream information was able to identify which files were missing audio or missing english audio. For anyone else who may find use for such a script, here is the code:

<?php

if (PHP_SAPI !== 'cli'){
    die('This script is intended for CLI use only.');
}

define('AVPROBE_BIN', 'C:\Software\Packages\Libav\bin\avprobe.exe');

$dir = isset($argv[1])?$argv[1]:getcwd();

$iter = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($dir)
);

foreach ($iter as $file){
    if ($file->isFile() && $file->isReadable()){
        try {
            fprintf(STDERR, "Scanning file %s%s", $file->getPathname(), PHP_EOL);
            $streams = extractStreamsInformation($file);
            if (!hasAudioStream($streams)){
                echo $file->getPathname(), ' is missing audio.', PHP_EOL;
            } else if (!hasEnglishAudio($streams)){
                echo $file->getPathname(), ' has no english audio.', PHP_EOL;
            }
        } catch (RuntimeException $e){
            echo $file->getPathname(), ' could not be scanned. ', $e->getMessage(), PHP_EOL;
        }
    }
}


function extractStreamsInformation(SplFileInfo $file){
    static $cmdTemplate = '"%s" -show_streams -of json "%s" 2>nul';

    $cmd = sprintf($cmdTemplate, AVPROBE_BIN, $file->getPathname());

    exec($cmd, $output, $ret);
    if ($ret !== 0){
        throw new RuntimeException('Could not process file '.$file->getPathname());
    }

    $json = implode('', $output);
    $decoded = json_decode($json, true);
    if (json_last_error() !== JSON_ERROR_NONE){
        throw new RuntimeException('Invalid JSON output.');
    }

    if (!isset($decoded['streams'])){
        throw new RuntimeException('No streams data.');
    }

    return $decoded['streams'];
}

function hasAudioStream($streams){
    foreach ($streams as $stream){
        if ($stream['codec_type'] == 'audio'){
            return true;
        }
    }

    return false;
}

function hasEnglishAudio($streams){
    foreach ($streams as $stream){
        if ($stream['codec_type'] == 'audio' && isset($stream['tags']['language'])){
            if ($stream['tags']['language'] == 'eng'){
                return true;
            }
        }
    }

    return false;
}

You'll need to download the avprobe executable and set the path to it in the script. Then simply run the script from within any directory with media files.