#mytheatre python client for xbmc
#version 2.0 by marplar 11-dec-2004
############################################################
#set ip address of pc running mytheatre
servip = '192.168.123.100'
#
#set port for avbroadcaster http
avbservport = 8000
#
#set port for python epg server
epgservport = 8001
#
#set delay for channel change in seconds (default = 4, max = 18)
changedelay=4
############################################################
import urllib, time, xbmc, xbmcgui, xml.parsers.expat, string, thread
from socket import *
from htmlparser import htmlparser
try: emulating = xbmcgui.emulating
except: emulating = false
#get actioncodes from keymap.xml
action_previous_menu = 10
action_select_item = 7
action_show_file_info = 11
action_popup_context=117
#reads avbroadcaster web page and creates list of favorite groups or channels
class myparser(htmlparser):
#read tag
def handle_starttag(self, tag, attrs):
#create dictionary of tag attributes
attrdict=dict(attrs)
if tag == 'html':
#initialse list
self.parselist = []
self.chtype = ''
elif tag == 'a':
#hyperlink tag - get link
self.href=attrdict['href']
elif tag == 'td' and len(attrdict) == 2 and self.tag == 'tr':
#table tag - check colour to determine if encrypted
if attrdict['bgcolor'] == '#00bf00':
self.encrypt = ' '
else:
self.encrypt = '!'
#store tag type
self.tag=tag
#read data (outside tag)
def handle_data(self,data):
if self.tag == 'title':
#add title to list
self.parselist.append(['title', data])
#store title to determine if page is favorite group list or channels
self.title = data
elif self.tag == 'center':
#get channel type (v/r) and add to encrypted flag (!/ )
self.chtype=data + self.encrypt + ' '
elif self.tag == 'a':
#hyperlink data
if self.href[:16] == '/list.htm?favid=':
#link is for favorite lists
if data[:9] == 'next page' and len(self.parselist) > 10:
#add next page link to list
self.parselist.append(['nextpage', data, self.href])
elif self.title == 'favorite groups list':
# add favorite group name and link to list
self.parselist.append(['favlist', data, self.href])
elif self.href[:17] == '/set_ch.htm?chid=':
#link is for channel
self.parselist.append(['channel', self.chtype + data,self.href])
def close(self):
#return list to caller
return self.parselist
self.close()
#reads mytheatre epg data
class listingsxmlparser:
def getprograms(self, chid):
#set up parser
p = xml.parsers.expat.parsercreate('utf-8')
p.startelementhandler = self.start_element
p.endelementhandler = self.end_element
p.characterdatahandler = self.char_data
#get epg data
sockobj = socket(af_inet, sock_stream)
try:
sockobj.connect((servip, epgservport))
sockobj.send('/?chid=' + chid)
data = sockobj.recv(200000)
sockobj.close()
p.parse(data)
except:
self.parselist = []
self.parselist.append(['','','','','error connecting to epg server', '',''])
return self.parselist
def start_element(self, name, attrs):
if name == 'xml':
#initialse list
self.parselist = []
elif name == 'program':
#new program
self.language=''
self.startdate=''
self.starttime=''
self.endtime=''
self.eventname=''
self.shortdescr=''
self.extdescr=''
#store name
self.name=name
def end_element(self, name):
if name =='program':
#add data to list if not in past
if float(self.endtime)>time.time():
self.startdate=time.strftime('%d %b',time.localtime(float(self.starttime)))
self.starttime=time.strftime('%h:%m',time.localtime(float(self.starttime)))
self.endtime=time.strftime('%h:%m',time.localtime(float(self.endtime)))
self.parselist.append([self.language,self.startdate,self.starttime,self.endtime,self
.eventname,self.shortdescr,self.extdescr])
#clear name
self.name = ''
def char_data(self, data):
#store attributes
if self.name == 'language':
self.language=data
elif self.name == 'starttime':
self.starttime=data
elif self.name == 'endtime':
self.endtime=data
elif self.name == 'eventname':
self.eventname=data
elif self.name == 'shortdescr':
self.shortdescr=data
elif self.name == 'extdescr':
self.extdescr=data
class myclass(xbmcgui.window):
def (self):
if emulating: xbmcgui.window.(self)
#set default mode
self.tvradiomode = 'tv only'
self.encryptmode = 'all channels'
#set up screen
self.addcontrol(xbmcgui.controlimage(0,0,800,600, 'background.png'))
self.addcontrol(xbmcgui.controllabel(50, 60, 10, 80, 'my', 'font14', '0xffff2000'))
self.addcontrol(xbmcgui.controllabel(75, 60, 100, 80, 'theatre', 'font14', '0xff000000'))
self.buttvradio = xbmcgui.controlbutton(300, 60, 120, 35, self.tvradiomode, 'button-focus.png', 'button-nofocus.png')
self.addcontrol(self.buttvradio)
self.butencrypt = xbmcgui.controlbutton(500, 60, 120, 35, self.encryptmode, 'button-focus.png', 'button-nofocus.png')
self.addcontrol(self.butencrypt)
self.strtitle = xbmcgui.controllabel(50, 120, 200, 200, '', 'font14', '0xffa5ff00')
self.addcontrol(self.strtitle)
self.list = xbmcgui.controllist(50, 150, 250, 370)
self.addcontrol(self.list)
self.strepgtitle = xbmcgui.controllabel(350, 190, 200, 200, 'program guide', 'font14', '0xffa5ff00')
self.addcontrol(self.strepgtitle)
self.txtprogdesc = xbmcgui.controllabel(350, 220, 350, 350,'','font13','0xffa5ff00')
self.addcontrol(self.txtprogdesc)
self.txtprogdesc.setvisible(false)
self.epglistvisible=true
self.epglist = xbmcgui.controllist(350, 220, 320, 300)
self.addcontrol(self.epglist)
#set navigation
self.list.controlleft(self.buttvradio)
self.epglist.controlleft(self.list)
self.epglist.controlright(self.butencrypt)
self.butencrypt.controlleft(self.buttvradio)
self.butencrypt.controldown(self.list)
self.buttvradio.controldown(self.list)
self.buttvradio.controlright(self.butencrypt)
#initialise parsers
self.myparser=myparser()
self.epgparser=listingsxmlparser()
#get favorites group list
self.file = '/list.htm'
self.parselist(url)
self.setfocus(self.list)
#read html page
def getlist(self,url,file):
opener = urllib.fancyurlopener({})
try:
doc = opener.open(url + file)
webpage = doc.read() # read file
doc.close()
self.myparser.feed(webpage)
listings=self.myparser.close()
return listings
except:
self.message('unable to retrieve channel list')
return 'nolist'
#check if stream is being received
def testchannel(self, url):
opener = urllib.fancyurlopener({})
try:
doc = opener.open(url + '/dvbcore.mpg')
datacount = len(doc.readline())
doc.close
if datacount > 0:
return 'ok'
else:
return ' '
except:
return ' '
#display listing
def parselist(self,url):
#get listings
self.listings =self.getlist(url,self.file)
#check listing was retreived
if self.listings == 'nolist':
return
self.list.reset()
#check if list is complete
while self.listings[len(self.listings)-1][0] == 'nextpage':
nextpage = self.getlist(url, self.listings[len(self.listings)-1][2])
self.listings.pop(len(self.listings)-1) #remove nextpage from original list
nextpage.pop(0) #remove title from extended list
self.listings.extend(nextpage) #join original and extended lists
#filter channels to show tv/radio and fta/encryted
count = 0
while count < len(self.listings):
if self.listings[count][0] == 'channel':
if self.parsecheckmode(self.listings[count][1]) == 0:
self.listings.pop(count)
else:
count = count + 1
else:
count = count + 1
#populate list
for listing in self.listings:
if listing[0] == 'title':
if listing[1] == 'favorite groups list':
self.strtitle.setlabel(listing[1])
else:
self.list.additem(listing[1])
#check channel to show tv/radio and fta/encryted
def parsecheckmode(self, listing):
if self.tvradiomode == 'tv only' and listing[0:1] <> 'v':
return 0
elif self.tvradiomode == 'radio only' and listing[0:1] <> 'r':
return 0
elif self.encryptmode == 'fta only' and listing[1:2] =='!':
return 0
elif self.tvradiomode == 'tv and radio':
if listing[0:1] <> 'v' and listing[0:1] <> 'r':
return 0
else:
return 1
#display epg info
def displayepg(self,chid):
#populate new listings
self.epglistings=self.epgparser.getprograms(chid)
for listing in self.epglistings:
self.epglist.additem(listing[2] + ' ' + listing[4])
#split epg description lines
def linesplit(self,text,width):
lines = []
while len(text) > width:
cutoff1 = string.rfind(text[:width]," ")
cutoff2 = string.find(text[:width],"\n")
if cutoff2 > cutoff1:
cutoff = cutoff2
elif cutoff1 < width*4/5:
cutoff = width
else:
cutoff = cutoff1
lines.append(text[:cutoff])
text = " " + string.lstrip(text[cutoff:])
if width < 3: text = string.lstrip(text)
lines.append(text)
return lines
#handle user input
def oncontrol(self, control):
if control == self.list:
#user selected group/channel list
try:
listingspos = self.list.getselectedposition()+1
if self.listings[listingspos][0] == 'channel':
#user selected channel change
dialog = xbmcgui.dialogprogress()
dialog.create('mytheatre', 'tuning to ' + self.listings[listingspos][1][3:])
self.getlist(url, self.listings[listingspos][2])
searchpos = 0
#wait for change delay
while searchpos <= changedelay * 5 and dialog.iscanceled() == 0:
searchpos = searchpos + 5
dialog.update(searchpos)
time.sleep(1)
#wait for stream to be detected
while searchpos <= 95 and dialog.iscanceled() == 0 and self.testchannel(url) != 'ok':
searchpos = searchpos + 5
dialog.update(searchpos)
time.sleep(1)
if searchpos < 100 and dialog.iscanceled() == 0:
#play stream
dialog.close()
xbmc.player().play(url + '/dvbcore.mpg')
elif dialog.iscanceled() == 0:
#time out
dialog.close()
self.message('channel not decryptable or not broadcasting')
else:
#user cancelled
dialog.close()
else:
#user selected group list
self.file = self.listings[self.list.getselectedposition()+1][2]
self.strtitle.setlabel(self.listings[self.list.getselectedposition()+1][1])
self.parselist(url)
except:
self.message('error tuning channel.')
if control == self.epglist:
if self.epglistvisible == true:
#display extended epg info
listingspos = self.epglist.getselectedposition()
if len(self.epglistings) > 0:
self.strepgtitle.setlabel(self.epglistings[listingspos][4])
epgdata = 'start: ' + self.epglistings[listingspos][1]
epgdata = epgdata + ' ' + self.epglistings[listingspos][2]
epgdata = epgdata + ' end: ' + self.epglistings[listingspos][3] + '\n'
epgdata = epgdata + 'description: \n'
epgdesclist=self.linesplit(self.epglistings[listingspos][5] + '\n' + self.epglistings[listingspos][6], 40)
for epgdesc in epgdesclist:
epgdata = epgdata + epgdesc + '\n'
epgdata = epgdata + 'language : ' + self.epglistings[listingspos][0]
self.epglist.setvisible(false)
self.epglistvisible = false
self.txtprogdesc.setvisible(true)
self.txtprogdesc.setlabel(epgdata)
else:
#return to list
self.setfocus(self.list)
self.txtprogdesc.setvisible(false)
self.epglist.setvisible(true)
self.epglistvisible = true
self.setfocus(self.epglist)
listingspos = self.list.getselectedposition()+1
self.strepgtitle.setlabel(self.listings[listingspos][1][3:])
elif control == self.buttvradio:
#user selected tv/radio mode button - toggle button and display list
if self.tvradiomode == 'tv and radio':
self.tvradiomode = 'tv only'
elif self.tvradiomode == 'tv only':
self.tvradiomode = 'radio only'
else:
self.tvradiomode = 'tv and radio'
self.buttvradio.setlabel(self.tvradiomode)
self.parselist(url)
elif control == self.butencrypt:
#user selected encrypt mode button - toggle button and display list
if self.encryptmode == 'all channels':
self.encryptmode = 'fta only'
else:
self.encryptmode = 'all channels'
self.butencrypt.setlabel(self.encryptmode)
self.parselist(url)
def onaction(self, action):
if action == action_previous_menu:
#user selected back - show favourite groups or exit
if self.listings[1][0] == 'channel':
self.file = '/list.htm'
self.parselist(url)
self.setfocus(self.list)
else:
self.close()
if action == action_show_file_info or action == action_popup_context:
#display epg
if self.epglistvisible == false:
self.txtprogdesc.setvisible(false)
self.epglist.setvisible(true)
self.epglistvisible = true
if self.listings[1][0] == 'channel':
listingspos = self.list.getselectedposition()+1
#clear list
self.epglist.reset()
#get listings
self.displayepg(self.listings[listingspos][2][17:])
self.strepgtitle.setlabel(self.listings[listingspos][1][3:])
if len(self.epglistings) > 0:
self.setfocus(self.epglist)
else:
self.epglist.additem('no program guide')
self.setfocus(self.list)
#display message
def message(self, message):
dialog = xbmcgui.dialog()
dialog.ok(" mytheatre", message)
###############
#main
###############
url='
http://' + servip + ':' + str(avbservport)
mydisplay = myclass()
mydisplay.domodal()
del mydisplay