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