20 Feb

script for shrinking videos

For this tip, you’ll need mencoder, ffmpeg and php installed. Only tested on Linux.

I’ve a load of videos, because I don’t like deleting anything in case I want to watch it again later (hoarding!).

I’ve three 1TB drives stuffed full of what I’ve collected over the last 20 years, but still I keep running out of room.

So, choice – buy more drives, or shrink what I have? I chose to shrink what I have, because I’m totally happy with VHS-quality video in stereo – I don’t need to watch fan-subbed Bleach in HD in 5.1 surround sound!

So, I wrote a script which will search the current directory (and its sub-directories) for all videos, will figure out their bit-rates, and will shrink them if they’re too large.

The script also reduces the videos so they match a maximum width and height.

It’s written in such a way that it will start with the most wasteful files (those that are taking up the most room), and work from that to the least wasteful.

It’s also written to only do 100 videos in a run. There’s no point shrinking absolutely every video you have, as most will probably save only a few kilobytes. The script does 100 of the most wasteful ones.

Of course, you could always run it again and again afterwards, and it should choose different videos each time to shrink.

How to use: copy the file into the root directory of your video repository. Go to that directory. Type “php shrink.php” into the console. Now go do something for a few days. Simple as that!

<?php

$maxwidth=624;
$maxheight=352;

function list_files($dir, $list) {
  $files=new DirectoryIterator($dir);
  foreach ($files as $f) {
    if ($f->isDot()) {
      continue;
    }
    $fname=$f->getPathname(); 
    if (is_dir($fname)) {
      $list=list_files($fname, $list); 
      continue;
    } 
    if (in_array(
      strtolower(preg_replace('/.*\./', '', $fname)),
      array('avi', 'mpg', 'mp4')
    )) {
      if (preg_match('/-shrink.avi$/', $fname)) {
        unlink($fname);
        continue;
      }
      $arr2=array();
      // { get duration of video
        exec('ffmpeg -i "'.$fname.'" 2>&1 | grep Duration', $arr2);
        $bits=explode(':', preg_replace('/.*Duration: ([^,]*),.*/', '\1', $arr2[0]));
        if (count($bits)!=3) {
          echo 'failed to get duration of "'.$fname.'"'."\n";
        }
        $duration=(int)preg_replace('/^0/', '', $bits[2])
          +((int)preg_replace('/^0/', '', $bits[1]))*60
          +((int)preg_replace('/^0/', '', $bits[0]))*3600;
      // }
      // { get width/height of video
        exec('ffmpeg -i "'.$fname.'" 2>&1 | grep "Video:"', $arr2);
        $bits=explode('x', preg_replace('/.*\s([0-9]+x[0-9]+)[\s,].*/', '\1', $arr2[1]));
        if (count($bits)!=2) {
          echo 'failed to get dimensions of "'.$fname.'"'."\n";
          continue;
        }
        $width=$bits[0];
        $height=$bits[1];
      // }
      $fsize=filesize($fname);
      $bps=$fsize/$duration;
      echo '.';
      $list[]=array($bps, $fsize, $width, $height, $duration, $fname);
    }
  }
  return $list;
}

echo "\ngetting file details\n";
$list=list_files('.', array());

echo "\nsorting by bits per second\n";
for ($i=0; $i<count($list)-1; ++$i) {
  echo '.';
  for ($j=$i+1; $j<count($list); ++$j) {
    if ($list[$j][0]>$list[$i][0]) {
      $tmp=$list[$j];
      $list[$j]=$list[$i];
      $list[$i]=$tmp;
    }
  }
}

echo "\nshrinking\n";
for ($i=0; $i<100&&$i<count($list); ++$i) {
  $fname=$list[$i][5];
  echo $fname."\nold size: ".$list[$i][1]."\n";
  @unlink('divx2pass.log');
  $resize='';
  if ($list[$i][2]>$maxwidth || $list[$i][3]>$maxheight) {
    $rx=$maxwidth/$list[$i][2];
    $ry=$maxheight/$list[$i][3];
    $ratio=$rx<$ry?$rx:$ry;
    $width=(int)($list[$i][2]*$ratio);
    $height=(int)($list[$i][3]*$ratio);
    $resize='-vf scale='.$width.':'.$height;
  }
  exec('mencoder "'.$fname.'" -ovc xvid -xvidencopts bvhq=1:chroma_opt:quan'
    .'t_type=mpeg:bitrate=400:pass=1 '.$resize.' -alang en -oac copy -o "'
    .$fname.'-shrink.avi" 2>&1');
  exec('mencoder "'.$fname.'" -ovc xvid -xvidencopts bvhq=1:chroma_opt:quan'
    .'t_type=mpeg:bitrate=400:pass=2 '.$resize.' -alang en -oac mp3lame '
    .' -o "'.$fname.'-shrink.avi" 2>&1');
  echo 'new size: '.filesize($fname.'-shrink.avi')."\n\n";
  if (filesize($fname.'-shrink.avi')<$list[$i][1]) {
    unlink($fname);
    rename($fname.'-shrink.avi', preg_replace('/\.[^\.]*$/', '.avi', $fname));
  }
  else {
    unlink($fname.'-shrink.avi');
  }
}