Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # -*- coding: utf-8 -*-
- import telnetlib, sys, os
- from xml.etree import ElementTree as ET
- from time import *
- try:
- import cairo
- except ImportError:
- print "PyCairo is not installed. Exiting"
- sys.exit(1)
- class CG():
- SAFET = 54
- SAFEL = 96
- SAFER = 1920 - SAFET
- SAFEB = 1080 - SAFEL
- def __init__(self,width=1920,height=1080):
- self.w = width
- self.h = height
- self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.w, self.h)
- self.ctx = cairo.Context (self.surface)
- self.ctx.set_antialias(cairo.ANTIALIAS_GRAY)
- def subtitle(self,titles):
- self.ctx.set_font_size(46)
- self.ctx.select_font_face('Arial', cairo.FONT_SLANT_NORMAL,cairo.FONT_WEIGHT_BOLD)
- for i,text in enumerate(titles[::-1]):
- tx, ty, tw, th, dx, dy = self.ctx.text_extents(text)
- self.ctx.move_to((self.w/2)-(tw/2),self.SAFEB-(i*50))
- self.ctx.text_path (text);
- self.ctx.set_source_rgb (.9, .9, .9)
- self.ctx.fill_preserve()
- self.ctx.set_source_rgb (.1, .1, .1)
- self.ctx.set_line_width (2)
- self.ctx.stroke()
- #self.ctx.show_text(text)
- #self.ctx.stroke()
- def save(self, fname):
- self.surface.write_to_png(fname)
- #
- # This CasparServer class is a rip-off from our automation system,
- # stripped to bare minimum. Original one has multiple channels support,
- # MAM integration, playlists.... So this one is quite messy. sorry...
- #
- # Video feed is expected in 1-1
- #
- class CasparServer():
- def __init__(self):
- self.Connect()
- self.fpos = False
- self.fdur = False
- self.pos = False
- self.dur = False
- self.current_file = False
- self.current_item = False
- self.id_channel = 1
- self.video_layer = 1
- def Connect(self):
- self.cmd_conn = telnetlib.Telnet("localhost",5250)
- def Query(self,q):
- conn = self.cmd_conn
- try:
- conn.write("%s\r\n"%q)
- result = conn.read_until("\r\n").strip()
- except:
- return (False,"Connection failed.")
- if not result:
- return (False,"Query failed")
- try:
- if result[0:3] == "202":
- return (True,result)
- elif result[0:3] in ["201","200"]:
- result = conn.read_until("\r\n").strip()
- return (True,result)
- elif int(result[0:1]) > 3:
- return (False,result)
- except:
- return (False,result)
- return (False,result)
- def UpdateStat(self):
- stat, res = self.Query("INFO %s"%self.id_channel)
- request_time = time()
- if not stat: return False
- try: xstat = ET.XML(res)
- except:
- print "XStat fail"
- return False
- else:
- self.xstat = xstat
- try:
- for layer in self.xstat.find("stage").find("layers").findall("layer"):
- if int(layer.find("index").text) != self.video_layer: continue
- else:
- video_layer = layer
- break
- else:
- raise Exception
- except:
- print "No video layer"
- try:
- fg_proc = video_layer.find("foreground").find("producer")
- if fg_proc.find("type").text == "image-producer":
- self.fpos = self.fdur = self.pos = self.dur = 0
- else:
- self.fpos = int(fg_proc.find("file-frame-number").text)
- self.fdur = int(fg_proc.find("file-nb-frames").text)
- self.pos = int(fg_proc.find("frame-number").text)
- self.dur = int(fg_proc.find("nb-frames").text)
- self.current_file = os.path.splitext(fg_proc.find("filename").text)[0].split("\\")[-1]
- self.current_item = self.current_file
- except:
- self.current_file = False
- ########################################################################
- class CGPlugin():
- def __init__(self, channel):
- self.channel = channel
- self.current_item = False
- self.tasks = []
- self.OnCreate()
- def OnCreate(self):
- pass
- def OnInit(self):
- pass
- def Work(self):
- if self.channel.current_item != self.current_item:
- self.current_item = self.channel.current_item
- self.OnInit()
- if not self.tasks: return
- if self.tasks[0](): del self.tasks[0]
- ########################################################################
- class Subtitler(CGPlugin):
- SUBS_DIR = "subs"
- MEDIA_DIR = "c:\\hfn\\opt\\Caspar\\media\\"
- SUBS_OFFSET = -10
- def OnCreate(self):
- self.channel_string = "%s-2" % self.channel.id_channel
- def OnInit(self):
- self.subs = []
- self.tasks = []
- if self.LoadSubs():
- self.next_start = 0
- self.next_stop = 0
- self.tasks = [self.MakeSub,self.ShowSub]
- def ParseTC(self,tcode,tbase=25):
- hh,mm,ss = tcode.strip().split(":")
- hh = int(hh)
- mm = int(mm)
- ss = float(ss.replace(",","."))
- return (hh*3600*tbase) + (mm*60*tbase) + (ss*tbase)
- def LoadSubs(self):
- sub_file = os.path.join(self.SUBS_DIR,self.current_item+".srt")
- if os.path.exists(sub_file):
- for block in open(sub_file).read().split("\n\n"):
- try:
- block = block.split("\n")
- tcodes = block[1]
- lines = block[2:]
- except:
- print block
- continue
- start, stop = tcodes.split("-->")
- start = self.ParseTC(start)
- stop = self.ParseTC(stop)
- self.subs.append((start,stop,lines))
- return True
- else:
- return False
- def MakeSub(self):
- try:
- self.next_start,self.next_stop, titles = self.subs.pop(0)
- except:
- self.next_start = self.next_stop = False
- return False
- print "Rendering subtitle:", titles
- c = CG()
- c.subtitle(titles)
- c.save(os.path.join(self.MEDIA_DIR,"subtitle.png"))
- return True
- def ShowSub(self):
- if self.channel.fpos > self.next_start + self.SUBS_OFFSET:
- self.channel.Query("PLAY %s subtitle"%self.channel_string)
- self.current_stop = self.next_stop
- self.MakeSub()
- self.tasks.append(self.HideSub)
- return True
- else:
- return False
- def HideSub(self):
- if self.channel.fpos > self.current_stop + self.SUBS_OFFSET:
- self.channel.Query("CLEAR %s"%self.channel_string)
- if self.next_start:
- self.tasks.append(self.ShowSub)
- return True
- ########################################################################
- if __name__ == "__main__":
- csp = CasparServer()
- plg = Subtitler(csp)
- csp.Query("CLEAR 1-1")
- csp.Query("CLEAR 1-2")
- sleep(.1)
- csp.Query("PLAY 1-1 tears_of_steel")
- while True:
- sleep(.2)
- csp.UpdateStat()
- plg.Work()
Advertisement
Add Comment
Please, Sign In to add comment