Update of /cvsroot/freevo/freevo/WIP/RobShortt/lib/vdrpylib
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16673
Added Files:
README.vdrpylib __init__.py channel.py event.py mark.py
recording.py svdrp.py timer.py vdr.py
Log Message:
This is vdrpylib, taken from http://sourceforge.net/projects/vdrpylib/ which
looks very much like a dead project. I have started on the bugfixes and
updates for Python 2.3. This looks like a good library for interfacing with
VDR and we could add to it.
--- NEW FILE: vdr.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
import cStringIO
import os.path
import re
import channel
import event
import recording
import svdrp
import timer
class VDR:
"""This class represents a VDR instance and all its associated
data.
It is a container for channel objects and runtime-related data.
It offers functions to retrieve channel and EPG data from the
VDR files or via SVDRP.
The channels variable is a dictionary containing integer objects
with the service ID of a channel as key and Channel objects as
values.
The recordings variable is a list containing Recording objects.
The recordings are sorted by their start time.
The timers variable is a list containing Timer objects. The
timers are sorted according to their index.
The host variable is a string containing the name of the host
running VDR.
The port variable is an integer containing the port at which the
SVDR protocol is available on host.
The vdrfile variable is a string containing the path to the VDR
executable.
The videopath variable is a string containing the path to the VDR
recordings (specify /video<n> with the lowest <n> in your system
if you have multiple directories containing recordings).
The following variables specify path names for VDR data files.
They default to the VDR default file names. Their paths are
derived from the videopath variable.
The cafile variable is a string containing the name of VDR's
ca.conf file.
The channelsfile variable is a string containing the name of
VDR's channels.conf file.
The epgfile variable is a string containing the path to VDR's
epg.data file.
The setupfile variable is a string containing the path to VDR's
setup.conf file.
The svdrpfile variable is a string containing the path to VDR's
svdrphosts.conf file.
The timerfile variable is a string containing the path to VDR's
timers.conf file.
"""
def __init__(self,
host = 'localhost',
port = svdrp.SVDRP_PORT,
vdrfile = '/usr/local/bin/vdr',
videopath = '/video',
cafile = 'ca.conf',
channelsfile = 'channels.conf',
epgfile = 'epg.data',
setupfile = 'setup.conf',
svdrpfile = 'svdrphosts.conf',
timerfile = 'timers.conf'):
self.svdrp = None
self.channels = {}
self.recordings = []
self.timers = []
self.host = host
self.port = port
self.vdrfile = vdrfile
# determine all existing video paths
self.videopath = videopath
self.vpaths = self.getvpaths(self.videopath)
# in the case of multiple video paths find where the
# following files are located:
self.cafile = self.findinvpaths(cafile)
self.channelsfile = self.findinvpaths(channelsfile)
self.epgfile = self.findinvpaths(epgfile)
self.setupfile = self.findinvpaths(setupfile)
self.svdrpfile = self.findinvpaths(svdrpfile)
self.timerfile = self.findinvpaths(timerfile)
def getvpaths(self, path):
"""Returns the available video paths (possibly) containing
recordings in the system.
"""
pat = re.compile('([^0-9]*)([0-9]+)')
result = pat.match(self.videopath)
if result is None:
return [path]
else:
paths = []
basepath = result.groups(1)
curnr = int(result.groups(2))
if basepath == None:
basepath = ''
curpath = basepath + str(curnr)
while os.isdir(curpath):
paths.append(curpath)
curnr = curnr + 1
curpath = basepath + str(curnr)
return paths
def findinvpaths(self, filename):
"""Tries to locate and verify the existence of a file in any
of the vpaths.
The filename variable is expected to be a string containing
name of the file to be located.
The return value is a string containing the full path of the
file to be located. If no file with the specified name exists
in any of the vpaths, None is returned.
"""
for vpath in self.vpaths:
path = os.path.join(vpath, filename)
if os.path.isfile(path):
return path
return None
def getsvdrp(self):
"""Returns an SVDRP object to the VDR instance represented by
this VDR object.
The connection is made to the remote endpoint identified by
the host and port variables. If host or port are None, no
connection can established and this function returns None.
"""
if self.svdrp is None and self.host and self.port:
self.svdrp = svdrp.SVDRP(self.host, self.port)
return self.svdrp
def close(self):
"""Closes the SVDRP connection to the VDR instance
represented by this VDR object.
"""
if self.svdrp is not None:
self.svdrp.close()
self.svdrp = None
def getchannel(self, id):
"""Returns a channel by index, SID, or name.
The id argument can be an integer which is either
interpreted as an index of an channel if less than 1000 or as
the service ID of a channel if greater or equal than 1000.
The argument may also be a string containing the name of a
channel. The specified string does not have to be a complete
name as it is matched from the start of the name. If the
string matches the start of multiple channel names, it is
unspecified which channel is returned. The matching is
performed case insensitively.
The return value is a Channel object representing the
channel found, or None if no channel could be identified from
the first parameter
"""
try:
x = int(id)
if x < 1000:
# assume that this is a channel's index
for ch in self.channels.values():
if x in ch.indexes:
return ch
else:
# assume that this is a channel's service ID
if self.channels.has_key(x):
return self.channels[x]
except:
# a string, well, must be a channel's name then
id = id.lower()
id_len = len(id)
for ch in self.channels.values():
if ch.name.lower()[0:id_len] == id:
return ch
return None
def retrievechannels(self):
"""Retrieves the list of channels from VDR via SVDRP and adds
them to the list of channels of this VDR object.
The remote endpoint of the SVDRP connection used is
identified by the host and port variables of this object.
The return value is the list of Channel objects retrieved
from VDR. If an error occurres the function returns None and
the channel list of this VDR object is not modified.
"""
if self.getsvdrp() is None:
return None
chans = self.svdrp.lstc()
if chans is not None:
self.channels = chans
return chans
def readchannels(self):
"""Reads a list of channels from VDR's channels.conf file and
adds them to this VDR object.
This function reads the file named by the channelfile
variable.
The return value is the list of Channel objects read from the
file.
"""
fh = open(self.channelsfile, 'r')
counter = 0
chans = {}
line = fh.readline()
while line:
line = line.strip()
if len(line) > 0 and line[0] != ':':
counter = counter + 1
c = channel.Channel(line, counter)
if chans.has_key(c.sid):
chans[c.sid].indexes.append(counter)
else:
chans[c.sid] = c
line = fh.readline()
self.channels = chans
return chans
def parseepg(self, fh):
"""Parses EPG data in the format of VDR's epg.data file and
adds it to this VDR object.
This function can also handle data with SVDRP line headers.
The fh arguments is the file handle to read the data from.
The return value is an integer containing the number of EPG
events parsed.
"""
channel = None
ev = None
counter = 0
line = fh.readline()
while line:
line = line.strip()
# special handling of data coming in via SVDRP
if line[0:4] == '215-':
line = line[4:]
if line[0:4] == '250 ':
line = fh.readline()
continue
if line[0] == 'C':
# channel start
tokens = line.split(None, 2)
ch_sid = int(tokens[1])
if self.channels.has_key(ch_sid):
channel = self.channels[ch_sid]
else:
channel = channel.Channel()
channel.sid = ch_sid
channel.name = tokens[2]
elif line[0] == 'E':
# event start
ev = event.Event()
ev.parseheader(line[2:])
ev.source = 'vdr'
ev.channel = channel
elif line[0] == 'T':
# title
ev.title = line[2:]
elif line[0] == 'S':
# subtitle
ev.subtitle = line[2:]
elif line[0] == 'D':
# description
ev.desc = line[2:]
elif line[0] == 'e':
# event end
channel.addevent(ev)
ev = None
counter = counter + 1
elif line[0] == 'c':
# channel end
self.channels[channel.sid] = channel
channel = None
else:
# unknown line identifier
print 'Unable to parse line ' + line
break
line = fh.readline()
fh.close()
return counter
def retrieveepg(self):
"""Retrieves EPG data via SVDRP from VDR and adds it to this
VDR object.
The remote endpoint of the SVDRP connection used is
identified by the host and port variables of this object.
The return value is an integer containing the number of EPG
events retrieved or None if an error occurred or no
connection could be established.
"""
counter = 0
if self.getsvdrp() is None:
return None
result = self.svdrp.write_cmd('lste')
fh = cstringio.cStringIO(result)
return parseepg(fh)
def readepg(self):
"""Reads EPG data from VDR's epg.data file and adds it to
this VDR object.
This function reads the file named by the epgfile variable.
The return value is an integer containing the number of EPG
events read.
"""
fh = open(self.epgfile, 'r')
return self.parseepg(fh)
def updateepg(self, channels):
"""Transmit EPG data of this object to VDR.
The channels argument is a list of Channel objects containing
the Event objects to be added to VDR's EPG list.
The return value is true if all EPG events were transmitted
successfully, false else.
"""
if self.getsvdrp() is None:
return None
return self.svdrp.pute(channels)
def retrieverecordings(self):
"""Retrieves all available information about recordings from
VDR via SVDRP and adds such Recording objects to this VDR
object.
The remote endpoint of the SVDRP connection used is
identified by the host and port variables of this object.
The return value is a list containing Recording objects or
None if an error occurred or no connection could be
established.
"""
if self.getsvdrp() is None:
return None
result = self.svdrp.lstr()
if result is not None:
self.recordings = result
return result
def readrecordings(self):
"""Scans the recordings directories for all available
information about recordings and adds such Recording objects
to this VDR object.
The videopath variable of this object identifies the path(s)
to be scanned.
The return value is a list containing Recording objects or
None if an error occurred.
"""
self.recordings = []
# what to do with every directory
def visit(arg, dirname, names):
if dirname[-4:] == '.rec':
r = recording.Recording(dirname)
arg.append(r)
# recursively search all video paths
for curpath in self.vpaths:
os.path.walk(curpath, visit, self.recordings)
# sort recordings by start time and set correct index
self.recordings.sort()
counter = 1
for rec in self.recordings:
rec.index = counter
counter = counter + 1
return self.recordings
def retrievetimers(self):
"""Retrieves all available information about timers from
VDR via SVDRP and adds such Timers objects to this VDR
object.
The remote endpoint of the SVDRP connection used is
identified by the host and port variables of this object.
The return value is a list containing Timer objects or
None if an error occurred or no connection could be
established.
"""
if self.getsvdrp() is None:
return None
result = self.svdrp.lstt()
if result is not None:
self.timers = result
return result
def readtimers(self):
"""Reads VDR's timer file and adds the found Timer objects
to this VDR object.
The timerfile variable of this object identifies the file
to be read.
The return value is a list containing Timer objects or
None if an error occurred.
"""
self.timers = []
try:
index = 1
fh = open(self.timerfile, 'r')
line = fh.readline()
while line:
t = timer.Timer(line.strip(), index)
if t.name is None:
print 'Could not parse line ' + str(index) + '
in file ' + self.timerfile + ': ' + line.strip()
self.timers.append(t)
line = fh.readline()
index = index + 1
fh.close()
except IOError:
return None
return self.timers
--- NEW FILE: __init__.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
--- NEW FILE: timer.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
#####################################################################
# Todo:
#####################################################################
# Find a nice way to represent the channel as an object
#####################################################################
# imports
#####################################################################
import re
import string
from types import *
#####################################################################
# module wide variables
#####################################################################
# a regex describing the format of a timer specification string
_pat = re.compile('((?P<index>\d+)
)?(?P<id>\d+):(?P<channel>\d+):(?P<day>\d{0,2}|.{7}):(?P<start>\d+):(?P<stop>\d+):(?P<prio>\d+):(?P<lifetime>\d+):(?P<name>[^:]*):(?P<summary>.*)')
#####################################################################
# module wide functions
#####################################################################
def parsetime(t):
"""Converts a string containing a start or stop time in VDR's
standard format ('hhmm') to the number of seconds since
midnight.
"""
return int(t[0:2]) * 3600 + int(t[2:4]) * 60
def strtime(t):
"""Converts the numeric start and stop time into a string
with VDR's standard format ('hhmm').
"""
return string.zfill(str(int(t / 3600)), 2) + string.zfill(str(int(t / 60) %
60), 2)
def parserecurrence(s):
"""Converts the given string from VDR's representation of
recurring timers to a list with 7 elements evaluating to true
or false.
"""
recurrence = [ 0 ] * 7
for counter in range(0, 7):
recurrence[counter] = (not (s[counter] == '-'))
return recurrence
def strrecurrence(r):
"""Converts the internal recurrence representation into a string
with VDR's format.
"""
s = ''
for x in r:
if x:
s = s + 'X'
else:
s = s + '-'
return s
#####################################################################
# class definitions
#####################################################################
class Timer:
"""This class represents a VDR timer.
The index variable is an integer containing the index of this
timer into the total list of timers of a VDR instance.
The name variable is a string containing the name of this timer
also serving as the name for the corresponding recording.
The summary variable is a string containing the name of this
timer also serving as the name for the corresponding recording.
The channel variable is an instance of class Channel containing
the channel this timer should record on.
The start variable is an integer containing the number of seconds
after midnight specifying the start time of the timer.
The stop variable is an integer containing the number of seconds
after midnight specifying the stop time of the timer.
The day variable is an integer. If the recurrence variable is
None, it contains the day of month on which the recording is to
take place. If the recurrence variable is not None, day specifies
the day of month on which the recurring recording is to take
place the first time.
The recurrence variable is a 7-element list containing values
evaluating either to true or false. Each element represents a
week day, element 0 being Monday through element 6 being Sunday.
Their values indicate whether the recording shall take place at
the corresponding week day. If the recurrence variable is not
None, day specifies the day of month on which the recurring
recording is to take place the first time.
If the recurrence variable is None, this is a one-shot timer.
The prio variable is an integer containing the priority of this
timer. Values may range between 1 and 99.
The lifetime variable is an integer containing the lifetime of
this timer in days. Values may range between 1 and 99.
The active variable is an integer evaluating to true if this
timer is active and evaluating to false otherwise.
"""
def __init__(self, s = None, index = None, vdr = None):
"""Creates a new Timer object.
The optional s argument is expected to be a string containing
a timer specification as can be found in VDR's timers.conf
file or as returned by the LSTT SVDRP command.
The optional index argument is expected to be an integer
containing the index of this timer into the global list of
timers of a VDR instance. The index of a timer can only be
derived from its specification string (i.e. the s argument)
when it it's in the SVDRP format. When reading VDR's
timers.conf file the timer indexes are encoded implicitely in
the line number and have thus to be passed explicitely to
this constructor.
The optional vdr argument is expected to an instance of class
VDR. When specified, this class can offer more advanced
abstractions than without it in which case it merely serves
as a data container.
"""
self.index = index
self.name = None
self.summary = None
self.channel = None
self.start = None
self.stop = None
self.day = None
self.recurrence = None
self.prio = None
self.lifetime = None
self.active = None
self.vdr = vdr
if s is not None:
self.parse(s)
def parse(self, s):
"""Parses a timer definition and updates the data of this
object accordingly.
The s argument is expected to be a string containing
a timer specification as can be found in VDR's timers.conf
file or as returned by the LSTT SVDRP command. Note that only
the SVDRP version allows to derive the index of a timer from
the specification string.
The return value evaluates to true if the string s could be
parsed successfully.
"""
if s is not None and isinstance(s, StringType) and len(s) > 5:
s = s.strip()
if s[0:3] == '250':
s = s[4:]
res = _pat.match(s)
if res is not None:
if res.group('index') is not None:
self.index = int(res.group('index'))
self.active = int(res.group('id'))
self.channel = int(res.group('channel'))
d = res.group('day')
if len(d) == 7:
self.day = None
self.recurrence = parserecurrence(d)
else:
self.day = int(d)
self.recurrence = None
self.start = parsetime(res.group('start'))
self.stop = parsetime(res.group('stop'))
self.prio = int(res.group('prio'))
self.lifetime = int(res.group('lifetime'))
self.name = res.group('name')
if self.name is None:
self.name = ''
self.summary = res.group('summary')
if self.summary is None:
self.summary = ''
return 1
return 0
def __cmp__(self, other):
"""Compares two Timer objects by their indexes.
"""
return cmp(self.index, other.index)
def __str__(self):
"""Returns a string representation of this object in the
format of VDR's timers.conf file.
"""
day = ''
if self.recurrence is not None:
day = strrecurrence(self.recurrence)
else:
day = str(self.day)
return string.join(map(str, [self.active, self.channel, day,
strtime(self.start), strtime(self.stop), self.prio, self.lifetime, self.name,
self.summary]), ':')
def getchannel(self):
"""Returns the channel this timer refers to.
In contrast to the numeric channel variable, this function
returns a channel object if the vdr variable is not None.
The return value is a instance of class Channel or None if
this timer refers to no or an invalid channel or the vdr
variable is None.
"""
if self.vdr and self.vdr.channels and self.channel:
return self.vdr.getchannel(self.channel)
return None
--- NEW FILE: svdrp.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
# import modules
import re
import string
import telnetlib
import time
from types import *
import channel
import recording
import timer
# SVDRP constants
SVDRP_PORT=2001
SVDRP_CMD_CHAN = 'chan'
SVDRP_CMD_CLRE = 'clre'
SVDRP_CMD_DELC = 'delc'
SVDRP_CMD_DELR = 'delr'
SVDRP_CMD_DELT = 'delt'
SVDRP_CMD_GRAB = 'grab'
SVDRP_CMD_HELP = 'help'
SVDRP_CMD_HITK = 'hitk'
SVDRP_CMD_LSTC = 'lstc'
SVDRP_CMD_LSTE = 'lste'
SVDRP_CMD_LSTR = 'lstr'
SVDRP_CMD_LSTT = 'lstt'
SVDRP_CMD_MESG = 'mesg'
SVDRP_CMD_MODC = 'modc'
SVDRP_CMD_MODT = 'modt'
SVDRP_CMD_MOVC = 'movc'
SVDRP_CMD_MOVT = 'movt'
SVDRP_CMD_NEWC = 'newc'
SVDRP_CMD_NEWT = 'newt'
SVDRP_CMD_NEXT = 'next'
SVDRP_CMD_PUTE = 'pute'
SVDRP_CMD_SCAN = 'scan'
SVDRP_CMD_STAT = 'stat'
SVDRP_CMD_UPDT = 'updt'
SVDRP_CMD_VOLU = 'volu'
SVDRP_CMD_QUIT = 'quit'
class SVDRP(telnetlib.Telnet):
"""A wrapper for VDR's SVDRP interface.
An SVDRP object represents a telnet session to a VDR instance.
SVDRP functionality is wrapped either at a text or at an object
level where appropriate.
Since the SVDRP class is derived from the telnetlib.Telnet class,
the plain telnet interface may also be used to drive the
protocol manually.
"""
def __init__(self, host = None, port = SVDRP_PORT):
self.host = host
self.port = port
if host is None:
telnetlib.Telnet.__init__(self)
else:
telnetlib.Telnet.__init__(self, self.host, self.port)
# consume all output
result = ''
while not result:
result = self.read_very_eager()
def open(self, host = None, port = SVDRP_PORT):
"""Connect to a host.
The optional port argument is the port number to connect to.
It defaults to the standard SVDRP port (2001).
Don't try to reopen an already connected instance.
"""
if host:
telnetlib.Telnet.open(self, host, port)
elif self.host:
telnetlib.Telnet.open(self, self.host, self.port)
def close(self):
self.write_cmd('quit')
telnetlib.Telnet.close(self)
def read_reply(self):
"""Read a SVDRP-style reply.
Reads input until a reply to an SVDRP command is found.
The function returns a string containing the complete reply.
The reply is expected to be delimited by a line starting with
a 3-digit number followed by a blank, followed by arbitrary
text and a terminating newline.
"""
pat = re.compile('^\d\d\d .*\n', re.M)
reply = ''
buffer = self.read_very_eager()
while not pat.search(buffer):
reply = reply + buffer
time.sleep(0.2)
buffer = self.read_very_eager()
reply = reply + buffer
return reply
def write_cmd(self, cmd):
"""Send text to the server and return its reply.
The cmd argument contains the string written to the
underlying telnet object. It is made sure that the string
ends in a newline character so that it is recognized as a
command by VDR.
The function returns a string containing VDR's reply.
"""
if len(cmd) == 0:
return ''
if cmd[-1] != '\n':
cmd = cmd + '\n'
self.read_very_eager()
self.write(cmd)
return self.read_reply()
def hitk(self, key):
"""Hit the given remote control key.
The key argument is expected to be a string containing one
of the key names understood by VDR.
The return value is 0 if the command failed and 1 otherwise.
"""
result = self.write_cmd('hitk ' + str(key) + '\n')
if result[0:3] == '250':
return 1
else:
print 'hitk returned: ' + result
return 0
def chan(self, chan, vdr = None):
"""Switch the current channel.
The channel argument can be a Channel object or a string
containing a channel name or index or being '+' or '-' for
switching to the next or previous channel.
The vdr argument is a VDR object from which fully qualified
Channel object can be retrieved.
The return value is a Channel object representing the
current channel if a VDR object was supplied, else it is the
string returned by VDR or None if the command failed.
"""
if isinstance(chan, channel.Channel):
result = self.write_cmd('chan ' + channel.name + '\n')
elif isinstance(chan, StringType) or isinstance(chan, IntType):
result = self.write_cmd('chan %s\n' % chan)
if result[0:3] == '250':
if vdr:
return vdr.getchannel(result.split(None, 2)[1])
else:
return result[3:]
else:
print 'chan returned: ' + result
return None
def lstc(self, id = None):
"""List channel details.
If the id argument is an integer the channel with that
index is listed. If the argument is a string all channels
containing that string as part of their name are listed.
If the argument is None, all channels are listed.
The return value is a list of Channel objects representing
the listed channels. If the command failed, None is returned.
"""
if id is None:
result = self.write_cmd('lstc\n')
else:
result = self.write_cmd('lstc ' + str(id) + '\n')
# check first line of output
if result[0:3] != '250':
print 'lstc returned: ' + result
return None
if id is None:
# return recording summary
return result[4:]
else:
# create Channel objects from output
channels = {}
counter = 1
for line in result.split('\n'):
if len(line) > 5:
tokens = line.strip()[4:].split(None, 1)
if len(tokens) > 1:
c = channel.Channel(tokens[-1],
counter)
channels[c.sid] = c
counter = counter + 1
return channels
def pute(self, channels):
"""Put data into the EPG list.
The channels argument is a list of Channel objects containing
the Event objects to be the EPG list.
The return value is true if all EPG events were transmitted
successfully, false else.
"""
result = self.write_cmd('pute\n')
if result[0:3] != '354':
print 'pute returned: ' + result
return 0
for ch in channels:
self.write(ch.getepgstr())
self.write('.\n')
result = self.read_reply()
if result[0:3] != '250':
print 'pute returned: ' + result
return 0
return 1
def lstr(self, index = None):
"""List VDR recordings.
The optional index argument is expected to be an integer
containing an index into the global list of recordings.
If no index argument is given, the return value is a list of
Recording objects. If an index is given, a string containing
the summary for that recording is returned.
If an error occurs, None is returned.
"""
result = ''
if index is None:
result = self.write_cmd('lstr\n')
else:
result = self.write_cmd('lstr ' + str(index) + '\n')
if result[0:3] == '550':
return ''
elif result[0:3] == '250':
return result.strip()[4:]
# check first line of output
if result[0:3] != '250':
print 'lstr returned: ' + result
return None
# create Recording objects from output
recs = []
for line in result.split('\n'):
if len(line) > 5 and line[:3] == '250':
r = recording.Recording(line)
r.setsource(self)
recs.append(r)
return recs
def lstt(self, index = None):
"""List VDR timers.
The optional index argument is expected to be an integer
containing an index into the global list of timers.
If no index argument is given, the return value is a list of
Timer objects. If an index is given, a Timer object
representing the timer with the specified index is
returned. If an error occurs, None is returned.
"""
result = ''
if index is None:
result = self.write_cmd('lstt\n')
else:
result = self.write_cmd('lstt ' + str(index) + '\n')
# check first line of output
if result[0:3] != '250':
print 'lstt returned: ' + result
return None
# create Recording objects from output
timers = []
for line in result.split('\n'):
if len(line) > 5 and line[:3] == '250':
t = timer.Timer(line)
timers.append(t)
return timers
--- NEW FILE: event.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
class Event:
"""Represents an event in the EPG data, i.e. a program.
"""
def __init__(self):
"""Constructs an Event object with all fields set to None.
"""
self.channel = None
self.id = None
self.start = None
self.dur = None
self.tableID = None
self.title = None
self.subtitle = None
self.desc = None
self.source = None
def __str__(self):
"""Returns a string representation for this event in the
format of VDR's epg.data file.
"""
s = 'E ' + str(self.id) + ' ' + str(self.start) + ' ' + str(self.dur)
if self.tableID is not None and self.tableID != 0:
s = s + ' ' + self.tableID
s = s + '\n'
if self.title is not None:
s = s + 'T ' + self.title + '\n'
if self.subtitle is not None:
s = s + 'S ' + self.subtitle + '\n'
if self.desc is not None:
s = s + 'D ' + self.desc + '\n'
s = s + 'e\n'
return s
def __cmp__(self, other):
"""Compares this event against another by their start times.
"""
return cmp(self.start, other.start)
def parseheader(self, str):
"""Parses a string containing an event specification in the
format of VDR's epg.data file.
The first argument is the specification string to parse.
Returns true if the specification string could be parsed
successfully, false else.
"""
tokens = str.split()
if len(tokens) < 3 or len(tokens) > 4:
return 0
self.id = int(tokens[0])
self.start = int(tokens[1])
self.dur = int(tokens[2])
if len(tokens) == 4:
self.tableID = tokens[3]
return 1
--- NEW FILE: recording.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
import os.path
import re
import string
import struct
import time
from types import *
import svdrp
class Recording:
"""This class represents a VDR recording.
Data about a recording can either be obtained via SVDRP or by
disk access (which gives more accurate information).
Note that when setting an SVDRP object as a recording's source,
this SVDRP object is implicitely accessed in some of the getXXX()
functions. Further accesses go to cache but the SVDRP connection
may not be closed until after the first access to those getXXX()
functions.
"""
def __init__(self, source = None, index = None):
"""Creates a new Recording object.
The optional source argument is expected to be an SVDRP object or a
string containing a recording description in the format as
returned by the SVDRP LSTR command or a string containing
the path to a recording.
The index is expected to be an integer containing the index
of this recording into the total list of recordings of a VDR
instance (base 1). If the source argument is a recording
description the index is derived from that description rather
than from the index argument which may thus be ommitted.
With SVDRP connections it is more efficient to run the LSTR
command once and create a Recording object per line than
creating multiple Recording object with an SVDRP object and
varying indexes as this requires as many executions of the
LSTR command.
"""
self.source = source
self.index = index
self.name = None
self.summary = None
self.start = None
self.prio = None
self.lifetime = None
self.marks = None
self.resume = None
if source is not None:
self.init(source, index)
def init(self, source, index = None):
"""Initialize the data associated with this recording object
from the given source.
The source argument is expected to be an SVDRP object or a
string containing a recording description in the format as
returned by the SVDRP LSTR command or a string containing
the path to a recording.
The index is expected to be an integer containing the index
of this recording into the total list of recordings of a VDR
instance (base 1). If the source argument is a recording
description the index is derived from that description rather
than from the index argument which may thus be ommitted.
With SVDRP connections it is more efficient to run the LSTR
command once and create a Recording object per line than
creating multiple Recording object with an SVDRP object and
varying indexes as this requires as many executions of the
LSTR command.
"""
if isinstance(source, svdrp.SVDRP) and index is not None:
pat = re.compile('250[ -]' + str(index) + ' ')
result = source.write_cmd('LSTR\n')
if result[0:3] != '250':
print 'lstr returned: ' + result
for line in result.split('\n'):
line = line.strip()
if pat.match(line):
self.init(line)
elif isinstance(source, StringType) and len(source) > 5 and source[:3]
== '250':
tokens = source.strip()[4:].split(None, 3)
if len(tokens) == 4:
self.index = int(tokens[0])
if len(tokens[1].split('.')) == 2:
tokens[1] = tokens[1] + '.' +
time.strftime('%Y')
self.start = int(time.mktime(time.strptime(tokens[1] +
' ' + tokens[2].replace('*', ''), '%d.%m.%Y %H:%M')))
self.name = tokens[3]
if tokens[2].find('*'):
self.resume = 0
else:
self.resume = -1
elif isinstance(source, StringType) and os.path.isdir(source):
pat =
re.compile('(/video\d*/)?(.*)/(\d{4}-\d\d-\d\d\.\d\d\.\d\d)\.(\d\d)\.(\d\d)\.rec/?')
result = pat.match(source)
if result is None:
raise TypeError, 'path of recording has unknown format'
self.index = index
self.name = result.group(2)
self.start = int(time.mktime(time.strptime(result.group(3),
'%Y-%m-%d.%H.%M')))
self.prio = int(result.group(4))
self.lifetime = int(result.group(5))
else:
raise TypeError, 'argument source has invalid type'
def getsource(self):
"""Returns the data source of this recording object.
The return value is either a SVDRP object or a string
containing the path to this recording.
"""
return self.source
def setsource(self, source):
"""Sets a data source for this recording object.
The source argument is expected to be either a SVDRP object
or a string containing the path to a recording.
"""
if isinstance(source, svdrp.SVDRP) or \
(isinstance(source, StringType) and \
os.path.isdir(source)):
self.source = source
else:
raise TypeError, 'argument source has invalid type'
def getindex(self):
"""Returns the index of this recording.
The return value is an integer representing the index of this
recording into the complete list of recordings of a VDR
instance.
"""
return self.index
def getname(self):
"""Returns the name of this recording.
The return value is a string containing the name of this
recording.
"""
return self.name
def getsummary(self):
"""Returns the summary of this recording.
Depending on the source given at construction time, the
summary for this recording is retrieved via SVDRP or from
the summary.vdr file.
The return value is a string containing the summary of this
recording.
"""
if self.summary is None:
if isinstance(self.source, svdrp.SVDRP):
self.summary = self.source.lstr(self.index)
elif isinstance(self.source, StringType) and
os.path.isdir(self.source):
fh = open(os.path.join(self.source, 'summary.vdr'),
'r')
self.summary = fh.read()
fh.close()
return self.summary
def getstart(self):
"""Returns the start time of this recording.
The return value is an integer containing the time when the
recording started as seconds since the epoch.
"""
return self.start
def getprio(self):
"""Return the priority of this recording.
The return value is an integer containing the priority of
this recording (which is derived from the corresponding
timer).
"""
return self.prio
def getlifetime(self):
"""Returns the lifetime of this recording.
The return value is an integer containing the number of days
since start this recording is guaranteed to not be deleted by
VDR.
"""
return self.lifetime
def getmarks(self):
"""Returns the editing marks of this recording.
The return value is a list of Mark objects representing the
editing marks of this recording.
"""
if self.marks is None:
if isinstance(self.source, StringType) and \
os.path.isdir(self.source) and \
os.path.isfile(os.path.join(self.source, 'marks.vdr')):
self.marks = []
fh = open(os.path.join(self.source, 'marks.vdr'), 'r')
line = fh.readline()
while line:
mk = mark.Mark(line.strip())
self.marks.append(mk)
line = fh.readline()
fh.close()
return self.marks
def getresume(self):
"""Returns the resume offset for this recording.
The return value is an integer containing the resume offset
of this recording.
If the recording does not have a resume offset, the return
value is -1. If the recording has an unknown resume offset
(may occur when retrieving recording data via SVDRP), the
return value is 0.
"""
if self.resume is None:
dfn = None
if isinstance(self.source, StringType) and \
os.path.isdir(self.source):
if os.path.isfile(os.path.join(self.source,
'resume.vdr')):
fh = open(os.path.join(self.source,
'resume.vdr'), 'r')
nr = fh.read()
fh.close()
self.resume = struct.unpack('l', nr)[0]
else:
self.resume = -1
elif isinstance(self.source, StringType) and len(self.source)
> 5 and self.source[:3] == '250':
dfn = self.source
elif isinstance(self.source, svdrp.SVDRP):
dfn = self.source.lstr(self.index)
if dfn is not None:
tokens = dfn.strip()[4:].split(None, 3)
if len(tokens) == 4:
if tokens[2].find('*'):
self.resume = 0
else:
self.resume = -1
return self.resume
def isnew(self):
"""Returns whether this recording is unwatched, i.e. whether
it does not have a resume offset.
"""
return self.getresume() == -1
def __cmp__(self, other):
"""Compares two Recording objects by their start times.
"""
return cmp(self.start, other.start)
--- NEW FILE: mark.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
import re
class Mark:
"""This class represents an editing mark of a VDR recording.
The seconds variable is an integer representing the offset of
this mark into the recording in seconds.
The frames variable is an integer representing the offset of
this mark into the recording relative to the position
indicated by the seconds variable. With 25 fps recordings the
value of this variable ranges between 0 and 24.
The comment variable is a string containing the comment of this
mark. If there is no comment associated with this mark, this
variable is None.
"""
_pat = re.compile('(\d\d):(\d\d):(\d\d)(\.(\d+))?( (.*))?')
def __init__(self, str = None):
"""Construct a new Mark object.
The optional str argument is a string containing the
specification of an editing mark in the format of VDR's
marks.vdr file.
"""
self.seconds = 0
self.frames = 0
self.comment = None
if str is not None:
self.parse(str)
def parse(self, str):
"""Parse a string for editing mark data and update this
object's data accordingly.
The str argument is a string containing the
specification of an editing mark in the format of VDR's
marks.vdr file.
"""
res = _pat.match(str)
self.seconds = int(res.group(1)) * 3600 + int(res.group(2)) * 60 +
int(res.group(3))
if result.group(4) is None:
self.frames = 0
else:
self.frames = int(result.group(5))
self.comment = result.group(7)
def __str__(self):
"""Returns a string representation of this mark in the format
of VDR's marks.vdr file.
"""
s = str(self.seconds / 3600) + ':' + str((self.seconds / 60) % 60) +
':' + str(self.seconds % 60)
if self.frames != 0:
s = s + '.' + str(self.frames)
if self.comment is not None:
s = s + ' ' + self.comment
return s
--- NEW FILE: README.vdrpylib ---
VDR Python Library ('vdrpylib')
-------------------------------
This is a programming library written in Python (http://python.org/) for the
Video Disk Recorder (VDR) project (http://www.cadsoft.de/people/kls/vdr/).
Please see the INSTALL file for details on how to install this program on your
computer.
For further information please refer to http://sourceforge.net/projects/vdrpylib/
The author can be contacted at [EMAIL PROTECTED]
Applications
------------
Also part of this package are the following applications based on vdrpylib:
- Extended Electronic Program Guide ('expg')
Please see the README files in the coresponding sub-directories for additional
information
Library Concepts and Overview
-----------------------------
The idea of this library is to represent the data available from an
instance of VDR as a class hierarchy. It shall relieve the programmer
of the burden to directly interact with VDR's interfaces like
configuration files or SVDRP. Instead a programmer can examine and
manipulate timers, recordings, etc. at the class level.
Class Hierarchy:
The central class of the library is the VDR class serving both as the
main data container and as a control interface to VDR. It collects
objects of these types:
- Channel
- Timer
- Recording
The Channel class collects objects of these types:
- Event (EPG events)
The Timer class only contains singular members.
The Recording class collects objects of these types:
- Mark (editing mark)
Class Dependencies:
The library tries to encapsulate all data entities in classes. Many
of those entities are associated with each other, e.g. timers and EPG
events are associated with channels.
VDR represents such associations in a very simple form (often an
index into a global list of the associated object). Thus, vdrpylib
needs to retrieve all available objects from VDR (channels in this
example) in order to provide a class view of such associations (so
that a timer object actually provides a channel object and not only
the integer index).
Of course this is rather expensive and may be unwanted if all the
programmer needs is a single object type (e.g. she may want to simply
parse the timers file).
Thus, vdrpylib uses the VDR class as a central data container which
allows classes to establish references to each other. If an object
like Timer is supplied with a VDR object, it can represent channels
as the objects it finds in the VDR object. If the Timer is not passed
an instance of class VDR, it merely serves itself as a container for
timer data.
--- NEW FILE: channel.py ---
#####################################################################
# Copyright 2002 Stefan Goetz
#####################################################################
# This file is part of vdrpylib.
#
# vdrpylib 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.
#
# vdrpylib is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with vdrpylib; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#####################################################################
import string
class Channel:
"""This class represents a program channel.
It is intended to a container for the technical channel data as
as for EPG Event objects.
VDR may place a single physical channel at several locations in
its global list of channels. This class does not represent a slot
in the global list of channels but a physical channel. Physical
channels are assumed to be uniquely identifiable by their SID.
The indexes variable is a list containing the indexes of this
channel in the global list of channels of a VDR instance.
The name variable is a string containing the name of this
channel.
The freq variable is an integer containing the frequency of this
channel in Hz.
The pol variable is a string of length one containing the
polariazation of this channel. 'h' stands for horizontal, 'v' for
vertical polarization.
The diseqc variable is an integer containing the DiSEqC code for
this channel.
The srate variable is an integer containing the symbol rate of
this channel.
The vpid variable is an integer containing the video PID of this
channel.
The apids variable is a list of integers containing the audio
PIDs of this channel.
The dpis variable is a list of integers containing the dolby
digital (AC3) PIDs of this channel.
The tpid variable is an integer containing the teletext PID of
this channel.
The ca variable is an integer defining how the channel can be
accessed over the available DVB cards.
The sid variable is an integer containing the service ID of this
channel.
The events variable is a list of Event objects sorted by start
time. The list should not be manipulated directly but only
through the XXXevent() functions of this class.
"""
def __init__(self, line = None, index = None):
self.indexes = []
self.name = None
self.freq = None
self.pol = None
self.diseqc = None
self.srate = None
self.vpid = None
self.apids = []
self.dpids = []
self.tpid = None
self.ca = None
self.sid = None
self.events = []
if line is not None:
self.parse(line)
if index is not None:
self.indexes.append(index)
def __str__(self):
"""Creates a string representation of this channel object
in the format of VDR's channels.conf file.
"""
apids = string.join([string.join(map(str, self.apids), ','),
string.join(map(str, self.dpids), ',')], ';')
return string.join(map(str, [self.name, self.freq, self.pol,
self.diseqc, self.srate, self.vpid, apids, self.tpid, self.ca, self.sid]), ':')
def parse(self, string):
"""Parses a standard channel specification as can be found in
VDR's channels.conf file.
All fields of this channel object are set according to the
specification string given in the first argument.
"""
tokens = string.strip().split(':')
self.name = tokens[0]
self.freq = int(tokens[1])
self.pol = tokens[2]
self.diseqc = int(tokens[3])
self.srate = int(tokens[4])
self.vpid = int(tokens[5])
apids = tokens[6]
self.tpid = int(tokens[7])
self.ca = int(tokens[8])
self.sid = int(tokens[9])
tokens = apids.split(';')
self.apids = map(int, tokens[0].split(','))
if len(tokens) == 2:
self.dpids = map(int, tokens[1].split(','))
def addevent(self, ev):
"""Add an EPG event to the list of events for this channel.
If the event has no ID one is assigned.
The first argument shall be an Event object representing
the event to be added. The start time of the event to add
must be later than the end of any other event of this
channel.
The return value is the ID of the event if it satisfied the
given restrictions and was added, else None is returned.
"""
if len(self.events) == 0 or self.events[-1].start +
self.events[-1].dur <= ev.start:
self.events.append(ev)
if ev.id is None:
if len(self.events) > 1:
ev.id = (self.events[-2].id + 1) % 65536
else:
ev.id = 0
return ev.id
else:
return None
def getepgstr(self):
"""Creates a string representation of this channel object
in the format of VDR's epgdata file including all contained
EPG events.
"""
s = 'C ' + str(self.sid)
if self.name is not None:
s = s + ' ' + self.name
s = s + '\n'
for ev in self.events:
s = s + str(ev)
s = s + 'c\n'
return s
-------------------------------------------------------
This SF.Net email is sponsored by BEA Weblogic Workshop
FREE Java Enterprise J2EE developer tools!
Get your free copy of BEA WebLogic Workshop 8.1 today.
http://ads.osdn.com/?ad_id=4721&alloc_id=10040&op=click
_______________________________________________
Freevo-cvslog mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog