Previous EuroHacker Magazine, issue #1 Next

Pr0ngallery

Written by: guess who?

A while ago I needed to sample a pretty vast file collection, belonging to a dude I know on the Net. The files were large movie files and so I would have to download all of them just to check out if I liked 'em. This annoyed me so I thought about what to do. The idea I came up with was using mplayer to rip stills out of each file, so I could easily check out if it was worth downloading. In hindsight I could have downloaded chunks of each file using HTTP's Range header and then done mplayer -idx on them but I don't know if that would work plus that's an unnecessary waste of bandwidth. This solution only requires me to run the script on the dude's box.

It's worked so far, but it wouldn't surprise me if it craps out. YMMV. Also, this is my first and only Python script at this point, so don't harass me.


#!/usr/bin/python

import os, sys, commands, re, string, glob

def usage():
	print 'pr0ngallery - A movie still gallery generator'
	print 'Usage: pr0ngallery <files>'

def make_stills(filename, dir, interval, num_stills):

	os.chdir(dir)

	i=1
	
	while i <= num_stills:
	
		# grab a few frames
		# I've found that to avoid black frames, cluttered frames and other crap, generally you need to grab the fourth frame
		# -ss seek to second
		# -frames number of frames to grab (you need to increment the desired number by one, oddly enough)
		# -vo video out
		# -ao audio out
		# -z png compression level
		os.system('mplayer -ss %d -frames 5 -vo png -z 1 -ao null "%s" 2>/dev/null >/dev/null' % (i*interval, filename))

		#print "num_stills: %d" % num_stills
		
		print 'Creating still %d for "%s"' % (i, filename)
		
		try:
		    try:
			os.rename('00000004.png', 'still_%06d.png' % i) # may fail, I've found
		    except OSError:
			print 'Error, skipping'
			continue
		finally:
		    # delete all the crap
		    d=glob.glob('*.png')
		
		    for curr in d:
			if not re.compile('^still.+$').match(curr):
			    os.unlink(curr)
		
		    i+=1

	os.chdir('..')

def get_movie_info(filename):
	buffer=commands.getoutput('mplayer -vo null -ao null -frames 0 -identify "%s"' % filename)

	m=re.compile(r'.*?ID_VIDEO_FORMAT=([^\n]+).*?ID_VIDEO_BITRATE=([^\n]+).*?ID_VIDEO_WIDTH=([^\n]+).*?ID_VIDEO_HEIGHT=([^\n]+).*?ID_VIDEO_FPS=([^\n]+).*?ID_VIDEO_ASPECT=([^\n]+).*?ID_AUDIO_CODEC=([^\n]+).*?ID_AUDIO_FORMAT=([^\n]+).*?ID_AUDIO_BITRATE=([^\n]+).*?ID_AUDIO_RATE=([^\n]+).*?ID_AUDIO_NCH=([^\n]+).*?ID_LENGTH=([^\n]+)', re.S).match(buffer)
	
	if not m:
		print 'Unexpected error!'
		sys.exit(-1)

	t=m.groups()

	info={'vformat': t[0], 'vbitrate': int(t[1]), 'vwidth': int(t[2]), 'vheight': int(t[3]), 'vfps': float(t[4]), 'vaspect': float(t[5]), 'acodec': t[6], 'aformat': int(t[7]), 'abitrate': int(t[8]), 'arate': int(t[9]), 'anch': int(t[10]), 'length': int(t[11])}

	return info

if __name__ == '__main__':

	# TODO: this shouldn't be hardcoded
	base_dir='gallery'

	if len(sys.argv) < 2:
		usage()
		sys.exit(-1)
		
	# get curr dir
	launch_dir=os.getcwd()

	# make the dir where the galleries will be made
	# abort if it already exists
	if (os.access(base_dir, os.F_OK)):
		print '"%s" already exists, aborting' % base_dir
		sys.exit(-1)
	
	# make the dir
	os.mkdir(base_dir)
	
	# change into it
	os.chdir(base_dir)

	i=1

	# loop thru all the filenames given to us
	for filename in sys.argv[1:]:
	
		if not os.path.isabs(filename):
		    filename='%s/%s' % (launch_dir, filename)

		num_stills=200 # TODO: shouldn't be hardcoded
	
		movie_info=get_movie_info(filename)
		
		#print movie_info
		
		if movie_info['length'] == 0:
		    print 'Zero-length movie, skipping...'
		    continue
		elif movie_info['length'] <= 10:
		    num_stills=1
		# determine number of stills we need
		# there should be at least 10 seconds between stills
		elif float(movie_info['length'])/float(num_stills) < 10:
		    num_stills=int(float(movie_info['length'])/10)
		    
		interval=int(float(movie_info['length'])/num_stills)
		
		print 'Making %d stills for movie "%s"' % (num_stills, filename)
		print 'Using an interval of %d seconds for the stills' % interval
		print 'Length of movie: %d seconds' % movie_info['length']
		
		dir="stills_%09d" % i
		
		os.mkdir('%s' % dir)
		
		make_stills(filename, dir, interval, num_stills)
		
		i+=1


Copyright 2005, EuroHacker Magazine