xmltv.py :  » Web-Services » python-xmltv » pytvgrab-lib-0.5.1 » lib » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Services » python xmltv 
python xmltv » pytvgrab lib 0.5.1 » lib » xmltv.py
#!/usr/bin/env python
# -----------------------------------------------------------------------
# Copyright (C) 2003 Chris Ottrey.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
# -----------------------------------------------------------------------
#
# This code is part of the pytvgrab project:
#    http://pytvgrab.sourceforge.net
#
# -----------------------------------------------------------------------
# Subversion Information, do not edit
#
# $Rev: 258 $
# $LastChangedDate: 2004-11-13 16:45:05 +1100 (Sat, 13 Nov 2004) $
# $LastChangedRevision: 258 $
# $LastChangedBy: ottrey $
#
# $Log: $
#

import sys
import string
import time
import copy

import message
import i18n

OUTPUT_ENCODING = 'UTF-8'
TOFFSET_MINUTES = 0

def _underscore_2_dash(str):
  result=string.lower(str)
  result=string.replace(result, '_', '-')
  return result

def setOutputEncoding(enc):
  """ Will set the OUTPUT_ENCODING global variable. """
  global OUTPUT_ENCODING
  OUTPUT_ENCODING = enc
  # Test the encoding
  unicode("abc",OUTPUT_ENCODING).encode(OUTPUT_ENCODING)

# setOutputEncoding

class _Element:

  TABSTOP=2*' '
  attributes=[]

  def __init__(self, **kwargs):
    self.pcdata = ''
    self.elements={}
    self.__dict__.update(kwargs)
  # __init__()

  def setPcdata(self, pcdata):
    self.pcdata=pcdata
  # setPcdata()

  def getName(self):
    result=_underscore_2_dash(self.__class__.__name__)
    return result
  # getName()

  def addElement(self, element):
    name=element.getName()
    if self.elements.has_key(name):
      self.elements[name].append(element)
    else:
      self.elements[name]=[element]
  # addElement()

  def _elementsHasDataSpec(self):
    """
    Finds out if the data has some data type specification
    (lang,system and such..)
    """
    if self.pcdata:
      if isinstance(self.pcdata, XMLDataAndAttr):
        return True
      #if type[self.pcdata] == type([]) and \
      #   len(self.pcdata) > 0 and \
      #   isinstance(self.pcdata[0], XMLDataAndAttr):
      #  return True
    return False

  def _singleElementStr(self,element,indent):
    result=''
    if self.elements.has_key(element):
      obj=self.elements[element]
      if obj:
        for i in obj:
          result=result+'%s' % i.__str__(indent+self.TABSTOP)
    return result

  def _elementsStr(self, indent=''):
    result=''
    #if 'pcdata' in self.elementList:
    if self.pcdata:
      if isinstance( self.pcdata, unicode ):
        self.pcdata = self.pcdata.encode( OUTPUT_ENCODING )
        result=result+'%s' % self.pcdata
      elif self._elementsHasDataSpec():
        data = self.pcdata.data
        spec = self.pcdata.spec
        if isinstance( data, unicode ):
            data = data.encode( OUTPUT_ENCODING, 'replace')
        if isinstance( spec, unicode ):
            spec = spec.encode( OUTPUT_ENCODING, 'replace')
        result=result+'%s>%s' % (spec,data)
      else:
        result=result+'%s' % self.pcdata
    else:
      result=result+'\n'
      if hasattr( self, 'elementOrder' ):
        for element in self.elementOrder:
          if self.elements.has_key(element):
          #if element in self.elementList:
            result+= self._singleElementStr(element,indent)
        # do the elements that's NOT in the element order last
        for element in self.elementList:
          if not (element in self.elementOrder):
            result+= self._singleElementStr(element,indent)
      else:
        for element in self.elementList:
          result+= self._singleElementStr(element,indent)
      result=result+indent
    return result
  # _elementsStr()

  def __str__(self, indent=''):
    name=self.getName()

    result='%s<%s ' % (indent, name)
    for attribute in self.attributes:
      if self.__dict__.has_key(attribute):
        value=self.__dict__[attribute]
        if value:
          if isinstance( value, unicode ):
            value = value.encode( OUTPUT_ENCODING )
          result=result+'%s="%s" ' % (_underscore_2_dash(attribute), value)
    if not self._elementsHasDataSpec():
      # Don't add the closing '>' to the tag, _elementsStr will do that
      result=result[:-1]+'>'

    result=result+self._elementsStr(indent)

    result=result+'</%s>\n' % (name)

    return result
  # __str__()

# _Element

class Element(_Element):
  """ Element
    NB. This class does not enforce any order,
    frequency or combinations of its child elements.

    (I was thinking maybe I could parse the DTD for this.
     Hmmm.. I'd have to do that _every_ time it was used. )
  """

  def __init__(self, name, **attributes):
    self.elements    = {}
    self.elementList = []
    self.attributes  = []
    self.pcdata      = ''
    self.name        = name
    if attributes.has_key('attributes'):
      attributes=attributes['attributes']
    for attr, value in attributes.items():
      if value:
        self.attributes.append(attr)
        self.__dict__[attr]=value
  # __init__()

  def addElement(self, element):
    name=element.getName()

    if self.elements.has_key(name):
      self.elements[name].append(element)
    else:
      self.elements[name]=[element]

    if name not in self.elementList:
      self.elementList.append(name)
  # addElement()

  def getName(self):
    return self.name
  # getName()

# Element

class XMLDataAndAttr:
  """
  This class is used as a container for those fields
  where data and a data specifying attribute is needed.
  i.e. XMLDataAndAttr("A title", 'lang="sv"')
       => <title lang="sv">A title</title>
  """

  def __init__(self, data, spec):
    self.data = data
    self.spec = spec

  def __str__(self):
    data = self.data
    if isinstance( data, unicode ):
      data = data.encode( OUTPUT_ENCODING )
    return data

  def __repr__(self):
    if isinstance( self.data, unicode ):
      return self.data.encode('ascii', 'replace')
    return self.data
# XMLDataAndAttr

class XMLFile:

  def __init__(self, root, dtd):
    self.root=root
    self.dtd=dtd
  # __init__()

  def __str__(self):
    result=''
    result=result+'<?xml version="1.0" encoding="'+ OUTPUT_ENCODING + '"?>\n'
    result=result+'<!DOCTYPE %s SYSTEM "%s">\n\n' % (self.root.getName(), self.dtd)
    result=result+'%s' % self.root
    return result
  # __str__()

# XMLFile

class XMLTV(XMLFile):

  def __init__(self, generator_info_name, root='tv', dtd='xmltv.dtd'):
    self.tv=Element('tv', generator_info_name=generator_info_name)
    self.root=self.tv
    self.dtd=dtd
    self.channel={}
    self.program={}
  # __init__()

  def addChannel(self, id, display_name, icon=None, url=None):
    if id not in self.channel.keys():
      message.moreinfo(_('Adding channel %s to xmltv') % repr(id))
      c=Element('channel', id=id)
      d=Display_Name()
      d.setPcdata(display_name)
      if icon:
        i=Element('icon', attributes=icon)
        c.addElement(i)
      if url:
        u=Element('url')
        u.setPcdata(url)
        c.addElement(u)
      c.addElement(d)
      self.tv.addElement(c)
      self.channel.update({id: c})
      self.program.update({id: []})
  # addChannel()

  def addProgram(self, start, channel, title, stop=None, info=None):

    message.moreinfo(_('Adding program %s to channel %s') % (repr(title), repr(channel)))

    # this should be done in grabbers
    # take a look at br_uol, it does using 2 regexp
    # (maybe there is a better way, if so, let me know -- Gustavo)
    #title=title.replace('&', '&amp;')   # hmmm.. fussy fussy
    p=Program('programme', start=start, stop=stop, channel=channel)
    t=Element('title')
    t.setPcdata(title)
    p.addElement(t)

    if info:
      for name, data in info.items():
        if type(data) == type([]) and \
           len(data) > 0 and isinstance(data[0],XMLDataAndAttr):
           # Special handling for arrays of XMLDataAndAttr
            for ee in data:
              e = Element(name)
              e.setPcdata(ee)
              p.addElement(e)
        else:
          e=Element(name)
          if type(data) == type('') or isinstance(data, unicode)\
             or isinstance(data,XMLDataAndAttr):
            e.setPcdata(data)
          elif type(data) == type({}):
            for n2, d2 in data.items():
              e2=Element(n2)
              e2.setPcdata(d2)
              e.addElement(e2)
          elif type(data) == type([]):
            for ee in data:
                for n2, d2 in ee.items():
                  e2=Element(n2)
                  e2.setPcdata(d2)
                  e.addElement(e2)
          p.addElement(e)

    self.tv.addElement(p)
    try:
      self.program[channel].append(p)
    except :
      sys.stderr.write('[%s]\n' % channel)
      sys.stderr.write('[%s]\n' % self.program.keys())
  # addProgram()

  def set_stops(self):
    """
    Adds stops to programs that doesn't have a stop.
    This is done by using the start time of the next programme
    or 24hr is added to the start if this is the last grabbed program
    """
    for c in self.channel.keys():
      #sometimes there is no programs at all for a channel..
      if len(self.program[c])==0:
        continue
      elif len(self.program[c])==1:
        # prep prev_p for the last resort +24h
        prev_p = prev_p=self.program[c][0]
      else:
        prev_p=self.program[c][0]
        for p in self.program[c][1:]:
          # Only set stop if it didn't already exist
          if not (hasattr( prev_p, 'stop' ) and prev_p.stop ):
            # Ugly hack here because 'stop' wasn't given to the Element constructor.
            # Which means it won't be in the attributes list.
            if not 'stop' in prev_p.attributes:
              prev_p.attributes.append('stop')
            prev_p.stop=p.start

          # remove doubles
          if prev_p.start == p.start:
            self.program[c].remove(p)

          prev_p=p

      # for

      # Last resort: if we could not find a stop
      # (generally on the last program) we set the program duration
      # to be 24 hours.
      if not hasattr( prev_p, 'stop' ):
        prev_p.stop = copy.copy( prev_p.start )
        prev_p.stop.nextDay()
        if not 'stop' in prev_p.attributes:
          prev_p.attributes.append( "stop" )
    # for
  # set_stops()

  def offset_times(self):
    for c in self.channel.keys():
      #sometimes there is no programs at all for a channel..
      if len(self.program[c])==0:
        continue
      else:
        for p in self.program[c]:
          p.start=p.start+TOFFSET_MINUTES*60
          if hasattr( p, 'stop' ):
            p.stop=p.stop+TOFFSET_MINUTES*60
  # offset_times()

  def write(self, file=None):

    # first do some house keeping
    self.set_stops()
    self.offset_times()

    buf=str(self)
    if buf:
      if file:
        f=open(file, 'w')
        f.write('%s' % buf)
        f.close()
      else:
        print buf

    statistics={
      'channels': len(self.root.elements.get('channel', []))   ,
      'programs': len(self.root.elements.get('programme', [])) ,
      'descriptions': 0                                ,
    }
    message.moreinfo(_("Statistics (NOT WORKING): channels=%(channels)s, programs=%(programs)s, descriptions=%(descriptions)s") % statistics)
  # write()

# XMLTV


# ---------- Start: this section will be deprecated -------------- #

# ---------- (some of) XMLTV DTD -----------------------------------#
# (version 0.5.35 backwards compatible with 0.5.15) starts here --- #
#
class Display_Name(_Element): elementList   = ['pcdata']
class Channel     (_Element):
  elementList   = ['display-name']
  #elementList   = 'display-name+'
  attributes = ['id']
class Title       (_Element): elementList   = ['pcdata']
class Desc        (_Element): elementList   = ['pcdata']
class Director    (_Element): elementList   = ['pcdata']
class Actor       (_Element): elementList   = ['pcdata']
class Credits     (_Element):
  elementList   = ['director', 'actor']
  #elementList   = 'director*, actor*'
class Colour      (_Element): elementList   = ['pcdata']
class Video       (_Element):
  elementList   = ['colour']
  #elementList   = 'colour?'
class Value       (_Element): elementList   = ['pcdata']
class Rating      (_Element):
  elementList   = ['value']
  #elementList   = 'value'
class Program   (Element):
  elementOrder   = ['title', 'sub-title', 'desc', 'credits', 'date',\
                   'category', 'language', 'orig-language', 'length',\
                   'icon', 'url', 'country', 'episode-num', 'video', 'audio',\
                   'previously-shown', 'premiere', 'last-chance', 'new',\
                   'subtitles', 'rating', 'star-rating']
  #elementList   = 'title+, desc*, credits?, video?, rating*'
  attributes = ['start', 'stop', 'channel']
class TV          (_Element):
  elementOrder   = ['channel', 'programme']
  #elementList   = 'channel, programme'
  elementList = ['generator_info_name']
# ---------- (some of) XMLTV DTD (version 0.5.35 - 0.5.15) ends here ----- #

# ---------- End: this section will be deprecated ---------------- #


# --------------  Unit Tests  -------------- #
using_unittest2=False
try:
  import unittest2 as unittest
  using_unittest2=True
except:
  import unittest

class XMLTV_UnitTest(unittest.TestCase):
  def setUp(self):
    message.verbose=message.verbose_level.ERROR
    self.xmltv=XMLTV('AU python generator')
    # "UTF-8" is default, this is same same but different :)
    setOutputEncoding("utf-8")
    self.xmltv.addChannel( 2 , 'ABC'   , icon={'src':'http://abc.net.au/common/logos/whtblkwht.gif'}, url='http://abc.net.au')
    self.xmltv.addChannel( 7 , 'Seven' )
    self.xmltv.addChannel( 9 , 'Nine'  )

    self.xmltv.addProgram( '200307181900' , 2 , 'ABC News'            )
    info={}
    info['rating'] = {'value':'G'}

    info['desc'] = [XMLDataAndAttr('Description','lang="en"'),
                    XMLDataAndAttr(u'Beskrivning','lang="sv"'),
                    XMLDataAndAttr(u'De beschrijving','lang="nl"'),
                    XMLDataAndAttr('Beskrivelse','lang="no"'),
                    XMLDataAndAttr(u'Description, mate :)','lang="au"'),]
    info['unsupported-element'] = "should go last (if anywhere)"
    info['sub-title'] = "sub-title"
    info['credits'] = "credits"
    info['date'] = "date"
    info['category'] = u"Sci-Fi"
    info['language'] = u"fi"
    info['orig-language'] = u"orig-language"
    info['length'] = "length"
    info['icon'] = u"icon"
    info['url'] = u"url"
    info['country'] = "country"
    info['episode-num'] = XMLDataAndAttr('.1/6.','system="xmltv_ns"')
    info['video'] = "video"
    info['audio'] = "audio"
    info['previously-shown'] = "previously-shown"
    info['premiere'] = "premiere"
    info['last-chance'] = "last-chance"
    info['new'] = "new"
    info['subtitles'] = "subtitles"
    info['rating'] = "rating"
    info['star-rating'] = "star-rating"

    self.xmltv.addProgram( '200307182400' , 7 , XMLDataAndAttr('Home and Away','lang="au"'), info=info)
    self.xmltv.addProgram( '200307182400' , 9 , 'Frasier'       , info={'rating': {'value':'G'}} )

    self.xmltv_str="""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">

<tv generator-info-name="AU python generator">
  <channel id="2">
    <icon src="http://abc.net.au/common/logos/whtblkwht.gif">
    </icon>
    <url>http://abc.net.au</url>
    <display-name>ABC</display-name>
  </channel>
  <channel id="7">
    <display-name>Seven</display-name>
  </channel>
  <channel id="9">
    <display-name>Nine</display-name>
  </channel>
  <programme start="200307181900" channel="2">
    <title>ABC News</title>
  </programme>
  <programme start="200307182400" channel="7">
    <title lang="au">Home and Away</title>
    <sub-title>sub-title</sub-title>
    <desc lang="en">Description</desc>
    <desc lang="sv">Beskrivning</desc>
    <desc lang="nl">De beschrijving</desc>
    <desc lang="no">Beskrivelse</desc>
    <desc lang="au">Description, mate :)</desc>
    <credits>credits</credits>
    <date>date</date>
    <category>Sci-Fi</category>
    <language>fi</language>
    <orig-language>orig-language</orig-language>
    <length>length</length>
    <icon>icon</icon>
    <url>url</url>
    <country>country</country>
    <episode-num system="xmltv_ns">.1/6.</episode-num>
    <video>video</video>
    <audio>audio</audio>
    <previously-shown>previously-shown</previously-shown>
    <premiere>premiere</premiere>
    <last-chance>last-chance</last-chance>
    <new>new</new>
    <subtitles>subtitles</subtitles>
    <rating>rating</rating>
    <star-rating>star-rating</star-rating>
    <unsupported-element>should go last (if anywhere)</unsupported-element>
  </programme>
  <programme start="200307182400" channel="9">
    <title>Frasier</title>
    <rating>
      <value>G</value>
    </rating>
  </programme>
</tv>
"""
  # setUp()

  def test01(self): v=str(self.xmltv); assert v == self.xmltv_str, v

if using_unittest2 or __name__ == '__main__':
  unittest.main()
# --------------  Unit Tests  -------------- #
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.