introduce rendertask class and more py3 compat stuff

This commit is contained in:
MaZderMind 2014-07-20 13:10:25 +02:00
parent bd079cc0cd
commit d804f6ff48
2 changed files with 59 additions and 53 deletions

View file

@ -160,11 +160,11 @@ def tasks(queue):
for event in events(): for event in events():
# generate a task description and put them into the queue # generate a task description and put them into the queue
queue.put(( queue.put(Rendertask(
'intro.svg', infile = 'intro.svg',
str(event['id'])+".dv", outfile = str(event['id'])+".dv",
introFrames, sequence = introFrames,
{ parameters = {
'$id': event['id'], '$id': event['id'],
'$title': event['title'], '$title': event['title'],
'$subtitle': event['subtitle'], '$subtitle': event['subtitle'],
@ -173,15 +173,15 @@ def tasks(queue):
)) ))
# place a task for the outro into the queue # place a task for the outro into the queue
queue.put(( queue.put(Rendertask(
'outro.svg', infile = 'outro.svg',
'outro.dv', outfile = 'outro.dv',
outroFrames sequence = outroFrames
)) ))
# place the pause-sequence into the queue # place the pause-sequence into the queue
queue.put(( queue.put(Rendertask(
'pause.svg', infile = 'pause.svg',
'pause.dv', outfile = 'pause.dv',
pauseFrames sequence = pauseFrames
)) ))

86
make.py
View file

@ -9,7 +9,7 @@ import math
import time import time
import shutil import shutil
import errno import errno
import urllib from urllib.request import urlopen
from lxml import etree from lxml import etree
from xml.sax.saxutils import escape as xmlescape from xml.sax.saxutils import escape as xmlescape
import cssutils import cssutils
@ -21,6 +21,25 @@ from threading import Thread, Lock
import subprocess import subprocess
from queue import Queue from queue import Queue
class Rendertask:
def __init__(self, infile, sequence, parameters={}, outfile=None, workdir='.'):
self.infile = infile
self.sequence = sequence
self.parameters = parameters
self.outfile = outfile
self.workdir = workdir
def fromtupel(tuple):
return Rendertask(tuple[0], tuple[2], tuple[3], tuple[1])
def ensure(input):
if isinstance(input, tuple):
return Rendertask.fromtupel(input)
elif isinstance(input, Rendertask):
return input
else:
return None
# Project-Name # Project-Name
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("you must specify a project-name as first argument, eg. './make.py sotmeu14'") print("you must specify a project-name as first argument, eg. './make.py sotmeu14'")
@ -61,38 +80,36 @@ cssutils.ser.prefs.lineSeparator = ' '
cssutils.log.setLevel(logging.FATAL) cssutils.log.setLevel(logging.FATAL)
def render(infile, outfile, sequence, parameters={}, workdir=os.path.join(projectname, 'artwork')): def render(infile, outfile, sequence, parameters={}, workdir=os.path.join(projectname, 'artwork')):
return rendertask(Rendertask(infile=infile, outfile=outfile, sequence=sequence, parameters=parameters, workdir=workdir))
def rendertask(task):
# in debug mode we have no thread-worker which prints its progress # in debug mode we have no thread-worker which prints its progress
if debug: if debug:
print("generating {0} from {1}".format(outfile, infile)) print("generating {0} from {1}".format(task.outfile, task.infile))
# make sure a .frames-directory exists in out workdir # make sure a .frames-directory exists in out workdir
ensurePathExists(os.path.join(workdir, '.frames')) ensurePathExists(os.path.join(task.workdir, '.frames'))
# open and parse the input file # open and parse the input file
with open(os.path.join(workdir, infile), 'r') as fp: with open(os.path.join(task.workdir, task.infile), 'r') as fp:
svgstr = fp.read() svgstr = fp.read()
for key in parameters.keys(): for key in task.parameters.keys():
svgstr = svgstr.replace(key, xmlescape(str(parameters[key]))) svgstr = svgstr.replace(key, xmlescape(str(task.parameters[key])))
svg = etree.fromstring(svgstr.encode('utf-8')) svg = etree.fromstring(svgstr.encode('utf-8'))
# find all images and force them to absolute file-urls
namespaces = {'xlink': 'http://www.w3.org/1999/xlink', 'svg': 'http://www.w3.org/2000/svg'}
for el in svg.findall(".//svg:image[@xlink:href]", namespaces=namespaces):
el.attrib['{http://www.w3.org/1999/xlink}href'] = 'file:///' + os.path.realpath(workdir) + '/' + el.attrib['{http://www.w3.org/1999/xlink}href']
# frame-number counter # frame-number counter
frameNr = 0 frameNr = 0
# iterate through the animation seqence frame by frame # iterate through the animation seqence frame by frame
# frame is a ... tbd # frame is a ... tbd
for frame in sequence(): for frame in task.sequence():
# print a line for each and every frame generated # print a line for each and every frame generated
if debug: if debug:
print("frameNr {0:2d} => {1}".format(frameNr, frame)) print("frameNr {0:2d} => {1}".format(frameNr, frame))
# open the output-file (named ".gen.svg" in the workdir) # open the output-file (named ".gen.svg" in the workdir)
with open(os.path.join(workdir, '.gen.svg'), 'w') as fp: with open(os.path.join(task.workdir, '.gen.svg'), 'w') as fp:
# apply the replace-pairs to the input text, by finding the specified xml-elements by thier id and modify thier css-parameter the correct value # apply the replace-pairs to the input text, by finding the specified xml-elements by thier id and modify thier css-parameter the correct value
for replaceinfo in frame: for replaceinfo in frame:
(id, type, key, value) = replaceinfo (id, type, key, value) = replaceinfo
@ -110,7 +127,7 @@ def render(infile, outfile, sequence, parameters={}, workdir=os.path.join(projec
fp.write( etree.tostring(svg, encoding='unicode') ) fp.write( etree.tostring(svg, encoding='unicode') )
# invoke inkscape to convert the generated svg-file into a png inside the .frames-directory # invoke inkscape to convert the generated svg-file into a png inside the .frames-directory
errorReturn = subprocess.check_output('cd {0} && inkscape --export-png=.frames/{1:04d}.png .gen.svg 2>&1 >/dev/null'.format(workdir, frameNr), shell=True, universal_newlines=True) errorReturn = subprocess.check_output('cd {0} && inkscape --export-png=.frames/{1:04d}.png .gen.svg 2>&1 >/dev/null'.format(task.workdir, frameNr), shell=True, universal_newlines=True)
if errorReturn != '': if errorReturn != '':
print("inkscape exitted with error\n"+errorReturn) print("inkscape exitted with error\n"+errorReturn)
sys.exit(42) sys.exit(42)
@ -121,21 +138,21 @@ def render(infile, outfile, sequence, parameters={}, workdir=os.path.join(projec
# remove the dv we are about to (re-)generate # remove the dv we are about to (re-)generate
ensureFilesRemoved(os.path.join(workdir, outfile)) ensureFilesRemoved(os.path.join(task.workdir, task.outfile))
# invoke avconv aka ffmpeg and renerate a lossles-dv from the frames # invoke avconv aka ffmpeg and renerate a lossles-dv from the frames
# if we're not in debug-mode, suppress all output # if we're not in debug-mode, suppress all output
os.system('cd {0} && ffmpeg -ar 48000 -ac 2 -f s16le -i /dev/zero -f image2 -i .frames/%04d.png -target pal-dv -aspect 16:9 -shortest "{1}"'.format(workdir, outfile) + ('' if debug else '>/dev/null 2>&1')) os.system('cd {0} && ffmpeg -ar 48000 -ac 2 -f s16le -i /dev/zero -f image2 -i .frames/%04d.png -target pal-dv -aspect 16:9 -shortest "{1}"'.format(task.workdir, task.outfile) + ('' if debug else '>/dev/null 2>&1'))
# as before, in non-debug-mode the thread-worker does all progress messages # as before, in non-debug-mode the thread-worker does all progress messages
if debug: if debug:
print("cleanup") print("cleanup")
# remove the .frames-dir with all frames in it # remove the .frames-dir with all frames in it
shutil.rmtree(os.path.join(workdir, '.frames')) shutil.rmtree(os.path.join(task.workdir, '.frames'))
# remove the generated svg # remove the generated svg
ensureFilesRemoved(os.path.join(workdir, '.gen.svg')) ensureFilesRemoved(os.path.join(task.workdir, '.gen.svg'))
@ -150,7 +167,7 @@ def events():
else: else:
# download the schedule # download the schedule
response = urllib.urlopen(project.scheduleUrl) response = urlopen(project.scheduleUrl)
# read xml-source # read xml-source
xml = response.read() xml = response.read()
@ -182,6 +199,8 @@ def events():
# expose helper-methods method to project # expose helper-methods method to project
project.events = events project.events = events
project.render = render project.render = render
project.rendertask = rendertask
project.Rendertask = Rendertask
project.fps = fps project.fps = fps
@ -267,38 +286,25 @@ def worker():
# loop until all tasks are done (when the thread fetches a sentinal from the queue) # loop until all tasks are done (when the thread fetches a sentinal from the queue)
while True: while True:
# fetch a task from the queue # fetch a task from the queue
task = tasks.get() task = Rendertask.ensure(tasks.get())
# if it is a stop-sentinal break out of the loop # if it is a stop-sentinal break out of the loop
if task == None: if task == None:
break break
# print that we're about to render a task # print that we're about to render a task
tprint('rendering {0}'.format(task[1])) tprint('rendering {0} from {1}'.format(task.outfile, task.infile))
# render options # prepend workdir to input file
opts = ( task.infile = os.path.join(workdir, task.infile)
# argument 0 is the input file. prepend the workdir task.outfile = os.path.join(outdir, task.outfile)
os.path.join(workdir, task[0]), task.workdir = workdir
# argument 1 is the output file. prepend the outdir
os.path.join(outdir, task[1]),
# argument 2 is the frame generator, nothing to do here
task[2],
# argument 3 are the extra parameters
task[3] if len(task) > 3 else {},
# argument 4 is the workdir path
workdir
)
# render with these arguments # render with these arguments
render(*opts) rendertask(task)
# print that we're finished # print that we're finished
tprint('finished {0}, {1} tasks left'.format(task[1], max(0, tasks.qsize() - num_worker_threads))) tprint('finished {0}, {1} tasks left'.format(task.outfile, max(0, tasks.qsize() - num_worker_threads)))
# mark the task as finished # mark the task as finished
tasks.task_done() tasks.task_done()