ThumbnailGenerator.py #2

  • //
  • guest/
  • alan_petersen/
  • scripts/
  • ThumbnailGenerator.py
  • View
  • Commits
  • Open Download .zip Download (10 KB)
#!/usr/bin/python

import os
import re
import sys
import shutil
import logging
import getopt
import marshal
import binascii
import tempfile
from subprocess import Popen, PIPE, STDOUT

#### MODIFY THIS SECTION AS APPROPRIATE 

P4CMD="/usr/local/bin/p4"
P4PORT="localhost:1666"
P4USER="p4super"
PASSFILE="superpass"
P4CLIENT="thumb_client"
CONVERTCMD="/opt/local/bin/convert"
LOGGING_LEVEL=logging.DEBUG
THUMBNAIL_GENERATOR_COUNTER="thumb-generator"
THUMBNAIL_FORMAT=".png"
THUMBNAIL_SIZE="x160"
TEMP_DIR = "/tmp"

#### END MODIFICATION SECTION

# usage
#     provides usage information for the utility
def usage():
	PROGRAM_USAGE = """\
Usage:	ThumbnailGenerator.py [-h] [-l logfile] [-v] [-g] [-i]
           -h = show help (this message)
           -v = verbose (debug mode)
           -f filename = specify file (depot path and version //path/to/file#nn)
           -p pageNum = specify page number of file (default is 0 or first page)
           -l logfile = specify logfile (default is STDOUT)
           -g = generate sample client specification
           -i = initialize the system, creating the logger counter and processing all changelists
"""
	print(PROGRAM_USAGE)
	sys.exit(0)

# generateSampleClient
#    this routine generates a sample client specification. The output should be sanity
#    checked, but can make creating the client spec a little easier.
def generateSampleClient():
	CLIENT_PREAMBLE = """\
Client:	{0}
Owner:	{1}
Description:
	Created by p4admin.
Root:	{2}
Options:	noallwrite noclobber nocompress unlocked nomodtime normdir
SubmitOptions:	submitunchanged
LineEnd:	local""".format(P4CLIENT, P4USER, TEMP_DIR)
	CLIENT_VIEW = """\
	//{0}/....ppm //{1}/{0}/....ppm
	//{0}/....PPM //{1}/{0}/....PPM
	//{0}/....bmp //{1}/{0}/....bmp
	//{0}/....BMP //{1}/{0}/....BMP
	//{0}/....jpg //{1}/{0}/....jpg
	//{0}/....JPG //{1}/{0}/....JPG
	//{0}/....bpm //{1}/{0}/....bpm
	//{0}/....BPM //{1}/{0}/....BPM
	//{0}/....gif //{1}/{0}/....gif
	//{0}/....GIF //{1}/{0}/....GIF
	//{0}/....pgm //{1}/{0}/....pgm
	//{0}/....PGM //{1}/{0}/....PGM
	//{0}/....png //{1}/{0}/....png
	//{0}/....PNG //{1}/{0}/....PNG
	//{0}/....xbm //{1}/{0}/....xbm
	//{0}/....XBM //{1}/{0}/....XBM
	//{0}/....xpm //{1}/{0}/....xpm
	//{0}/....XPM //{1}/{0}/....XPM
	//{0}/....tga //{1}/{0}/....tga
	//{0}/....TGA //{1}/{0}/....TGA
	//{0}/....psd //{1}/{0}/....psd
	//{0}/....PSD //{1}/{0}/....PSD
	//{0}/....PDF //{1}/{0}/....PDF
	//{0}/....pdf //{1}/{0}/....pdf
	//{0}/....AI //{1}/{0}/....AI
	//{0}/....ai //{1}/{0}/....ai
"""
	depotList = []
	cmd = ["depots"]
	results = p4MarshalCmd(cmd)
	for r in results:
		if 'type' in r and (r['type'] == "local" or r['type'] == "stream"):
			depotList.append(r['name'])
	print(CLIENT_PREAMBLE)
	print("View:")
	for d in depotList:
		print(CLIENT_VIEW.format(d, P4CLIENT))
	sys.exit()



# p4MarshalCmd
#     executes the p4 command, results sent to a list
def p4MarshalCmd(cmd,quiet=False):
	if not quiet:
		logging.debug("p4 {0}".format(" ".join(cmd)))
	list = []
	pipe = Popen([P4CMD, "-p", P4PORT, "-u", P4USER, "-c", P4CLIENT, "-G"] + cmd, stdout=PIPE).stdout
	try:
		while 1:
			record = marshal.load(pipe)
			list.append(record)
	except EOFError:
		pass
	pipe.close()
	return list

# p4InputCmd
#     executes the p4 command with input
def p4InputCmd(data,cmd,quiet=False):
	if not quiet:
		logging.debug("p4 {0}".format(" ".join(cmd)))
	list = []
	proc = Popen([P4CMD, "-p", P4PORT, "-u", P4USER, "-c", P4CLIENT, "-G"] + cmd, stdout=PIPE, stdin=PIPE, stderr=PIPE)
	outPipe = proc.stdout
	proc.stdin.write(data)
	return proc.communicate()

# p4Cmd
#     executes a p4 command, returns results
def p4Cmd(cmd,quiet=False):
	if not quiet:
		logging.debug("p4 {0}".format(" ".join(cmd)))
	proc = Popen([P4CMD, "-p", P4PORT, "-u", P4USER, "-c", P4CLIENT] + cmd, stdout=PIPE, stderr=PIPE)
	return proc.communicate()

# p4Cmd
#     executes a p4 command, returns results
def convert(original,thumb,page=0):
	logging.debug("convert -thumbnail {0} {1}[{2}] {3}".format(THUMBNAIL_SIZE, original, page, thumb))
	proc = Popen([CONVERTCMD, "-thumbnail", THUMBNAIL_SIZE, original + "[{0}]".format(page), thumb], stdout=PIPE, stderr=PIPE)
	return proc.communicate()

# containsError
#     utility function to check for any error code in the results array
def containsError(results=[],logError=True):
	foundError = False
	for r in results:
		if 'code' in r:
			if r['code'] == 'error':
				foundError = True
				if logError:
					logging.error(r['data'].strip())
			elif r['code'] == 'info':
				#code info output can be important in troubleshooting
				logging.debug(r['data'])
	return foundError

# checkLogin 
#     check the login ticket on the server
def checkLogin(username=""):
	cmd = ["login","-s"]
	result = p4MarshalCmd(cmd,quiet=True)
	if containsError(result, False):
		return False
	else:
		return True

# login
#    logs the user in using the password in the password file
def login():
	if PASSFILE is not None and os.path.isfile(PASSFILE):
		f = open(PASSFILE)
		lines = f.readlines()
		f.close()
		adminpass = lines[0].strip()
		cmd = ["login"]
		result = p4InputCmd(adminpass, cmd)
		if len(result[1]) > 0:
			return False
		else:
			return True
	else:
		return False

# isSubmitted
#    checks to see if the changelist exists and that it is a 'submitted' state
def isSubmitted(change):
	cmd = ["describe", "-s", change]
	result=p4MarshalCmd(cmd)
	if containsError(result, False):
		return False
	if result[0]['status'] == "submitted":
		return True
	else:
		return False

# setLoggerCounter
def resetCounters():
	cmd = ["counter", "logger", "0"]
	p4Cmd(cmd)
	cmd = ["counter", "-d", THUMBNAIL_GENERATOR_COUNTER]
	p4Cmd(cmd)

# setThumbAttribute
#    sets the 'thumb' attribute on the revision to the hex value specified
def setThumbAttribute(revision, hex):
	cmd = ["attribute", "-e", "-f", "-n", "thumb", "-i", revision]
	result = p4InputCmd(hex, cmd)

# generateThumbnail
#    generates a thumbnail for the specified depot file
def generateThumbnail(depotFile, page=0):
	logging.debug("generating thumbnail for " + depotFile)
	file = None
	rev = 0
	matchObj = re.match( r'(.*)#(\d+)$', depotFile, re.M|re.I)
	if matchObj:
		file = matchObj.group(1)
		rev = int(matchObj.group(2))
	else:
		file = depotFile
	extension = os.path.splitext(file)[1]
	p4print = tempfile.NamedTemporaryFile(dir=TEMP_DIR, prefix="p4_", suffix=extension)
	p4print.close()
	thumb = tempfile.NamedTemporaryFile(dir=TEMP_DIR, prefix="thumb_", suffix=THUMBNAIL_FORMAT)
	thumb.close()
	logging.debug("getting file from depot...")
	cmd = ["print", "-o", p4print.name, depotFile]
	result = p4Cmd(cmd)
	logging.debug("trying to generate thumbnail...")
	convert(p4print.name, thumb.name, page)
	if os.path.exists(thumb.name):
		file = open(thumb.name, "rb")
		bytes = file.read()
		file.close()
		hex = binascii.hexlify(bytes)
		logging.debug("storing thumbnail in metadata...")
		setThumbAttribute(depotFile, hex)
		os.remove(thumb.name)
	os.remove(p4print.name)

# processChange
#    processes the files in the specified changelist
def processChange(change):
	path = "//...@{0},@{0}".format(change)
	cmd = ["sync", "-p", "-n", path]
	result=p4MarshalCmd(cmd)
	if not containsError(result, False):
		for r in result:
			if r['action'] == "deleted":
				continue
			depotFile = "{0}#{1}".format(r['depotFile'], r['rev'])
			generateThumbnail(depotFile, 0)

# initialConfiguration
#    routine to initialize the system by (re)setting the logger counter, deleting
#    the thumbnail generator counter, and processing all of the submitted changelists
#    starting from the beginning. Needless to say, this can potentially take a long
#    time to run. if you simply want to turn on the functionality, you just have
#    to create the logger counter (p4 counter logger 0) and then schedule the thumbnail
#    generator to run. Only new changelists will be processed, however (i.e. only new
#    versions of files will have thumbnails created)
def initialConfiguration():
	logging.info("performing initial configuration")
	resetCounters()
	cmd = ["changes"]
	result = p4MarshalCmd(cmd)
	if(containsError(result, False)):
		errorExit("Error finding updates")
	for r in reversed(result):
		if r['status'] == "submitted":
			change = r['change']
			processChange(change)
	
# checkForChanges
#    uses the logger command to find any changelists since the last run
def checkForChanges():
	cmd=["logger", "-t", THUMBNAIL_GENERATOR_COUNTER]
	result=p4MarshalCmd(cmd)
	if(containsError(result, False)):
		errorExit("Error finding updates")
	last = 0
	for r in result:
		sequence = r['sequence']
		change = r['attr']
		if (int(change) > last) and isSubmitted(change):
			logging.info("Processing changelist @{0}".format(change))
			processChange(change)
			last = int(change)
		logging.debug("updating logger counter...")
		cmd=["logger", "-c", sequence, "-t", THUMBNAIL_GENERATOR_COUNTER]
		p4Cmd(cmd)

###########################################################################
##### MAIN PROGRAM STARTS HERE
#####

def main(argv=None):

	verbose = False
	logFile = None
	depotFile = None
	pageNum = 0
	generateSample = False
	initialize = False
	
	try:
		opts, args = getopt.getopt(argv, "hl:f:p:vgi")
	
		for opt, arg in opts:
			if opt == "-v":
				verbose = True
			elif opt == "-h":
				usage()
			elif opt == "-l":
				logFile = arg
			elif opt == "-f":
				depotFile = arg
			elif opt == "-p":
				pageNum = int(arg)
			elif opt == "-g":
				generateSample = True
			elif opt == "-i":
				initialize = True
	
		logLevel = logging.WARN
		if verbose:
			logLevel = logging.DEBUG
			
		if logFile is not None:
			logging.basicConfig(filename=logFile, format='%(asctime)s [%(levelname)s] %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logLevel)
		else:
			logging.basicConfig(format='[%(levelname)s] %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logLevel)

		if not checkLogin():
			if not login():
				errorExit("Not logged in")
				
		if generateSample:
			generateSampleClient()
		elif depotFile is not None:
			generateThumbnail(depotFile, pageNum)
		else:
			if initialize:
				initialConfiguration()
			else:
				checkForChanges()
		sys.exit(0)

	except getopt.GetoptError as e:
		print(e)
		print("ERROR: unknown argument\n")
		usage()
		sys.exit(2)

if __name__ == '__main__':
	main(sys.argv[1:])

# Change User Description Committed
#2 13685 alan_petersen integrating (to prevent splitsville)
#1 8913 alan_petersen script to generate thumbnail images