Recorder: changed into a twistd plugin, add minimum recording time, allow specifying directory where to store recordings

This commit is contained in:
Maurizio Porrato 2010-10-09 12:02:44 +02:00
parent 82ebc68e02
commit 4468c05d9c
2 changed files with 166 additions and 131 deletions

View File

@ -1,131 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# See LICENSE.txt for copyright info
from __future__ import with_statement
from frn.protocol.client import FRNClient, FRNClientFactory
from frn.user import FRNUser
from twisted.internet import reactor, task
from twisted.internet.defer import DeferredList
from twisted.python import log
import os, string, time, struct
safe_chars = string.ascii_letters+string.digits+' :;.,+-=$@'
WAV_HEADER = "524946460c06000057415645666d74201400000031000100401f00005906000041000000020040016661637404000000000000006461746100000000".decode('hex')
AUDIO_TIMEOUT = 5.0
def sanitizeFilename(name):
r = ''
for c in name:
if c in safe_chars:
r += c
return r
class FRNRecorder(FRNClient):
def getClientName(self, client_id):
if self.clientsById.has_key(client_id):
return self.clientsById[client_id]['on']
else:
return client_id
def buildRecordingName(self, client_id):
ts = time.localtime()
opname = sanitizeFilename(self.clients[client_id-1]['on'])
dirname = "/var/spool/grn/recordings/"+time.strftime("%Y%m/%d/%H", ts)
filename = time.strftime("%Y%m%d%H%M%S-", ts) + opname + '.wav'
try:
os.makedirs(dirname)
except OSError: pass
return dirname+'/'+filename
def textMessageReceived(self, client, message, target):
pass
def closeFile(self):
size = self.recordingFile.tell()-len(WAV_HEADER)
self.recordingFile.seek(0x30) # fact chunk value
self.recordingFile.write(struct.pack("<L", int(size/32.5)))
self.recordingFile.seek(0x38) # data chunk size
self.recordingFile.write(struct.pack("<L", size))
self.recordingFile.close()
def archiveRecording(self, from_id):
log.msg("%s stopped talking: starting playback." %
self.clients[from_id-1]['on'])
self.closeFile()
self.recordingFile = None
self.recordingOperator = None
def audioFrameReceived(self, from_id, frames):
if self.recordingOperator != self.clients[from_id-1]['on']:
if self.recordingOperator is not None:
self.closeFile()
self.recordingOperator = self.clients[from_id-1]['on']
self.recordingFile = file(self.buildRecordingName(from_id), 'wb')
self.recordingFile.write(WAV_HEADER)
self.recordingFile.write(frames)
try:
self.parrot_timer.reset(AUDIO_TIMEOUT)
except:
log.msg("%s started talking" %
self.clients[from_id-1]['on'])
self.parrot_timer = self.factory.reactor.callLater(
AUDIO_TIMEOUT, self.archiveRecording, from_id)
self.pong()
def loginResponse(self, info):
log.msg("Login: %s" % info['al'])
self.setStatus(1)
self.recordingOperator = None
self.recordingFile = None
def clientsListUpdated(self, clients):
self.clients = clients
self.clientsById = dict([(i['id'], i) for i in clients])
class FRNRecorderFactory(FRNClientFactory):
protocol = FRNRecorder
reactor = reactor
if __name__ == '__main__':
import sys
from os.path import dirname, join as pjoin
from ConfigParser import ConfigParser
log.startLogging(sys.stderr)
basedir = dirname(__file__)
acfg = ConfigParser()
acfg.read(['/etc/grn/accounts.conf',
pjoin(basedir,'accounts.conf'), 'accounts.conf'])
scfg = ConfigParser()
scfg.read(['/etc/grn/servers.conf',
pjoin(basedir,'servers.conf'), 'servers.conf'])
argc = len(sys.argv)
if argc >= 3:
server_name, network_name = sys.argv[2].split(':',1)
account_cfg = acfg.items(sys.argv[1])+[('network', network_name)]
server_cfg = scfg.items(server_name)
server = scfg.get(server_name, 'server')
port = scfg.getint(server_name, 'port')
d = dict(account_cfg)
user = FRNUser(
EA=d['email'],
PW=d['password'], ON=d['operator'],
BC=d['transmission'], DS=d['description'],
NN=d['country'], CT=d['city'], NT=d['network'])
reactor.connectTCP(server, port, FRNRecorderFactory(user))
reactor.run()
# vim: set et ai sw=4 ts=4 sts=4:

166
twisted/plugins/frnrecorder.py Executable file
View File

@ -0,0 +1,166 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2010 Maurizio Porrato <maurizio.porrato@gmail.com>
# See LICENSE.txt for copyright info
from __future__ import with_statement
from zope.interface import implements
from twisted.plugin import IPlugin
from twisted.application.service import IServiceMaker
from frn.protocol.client import FRNClient, FRNClientFactory
from frn.user import FRNUser
from twisted.application import internet
from twisted.internet import reactor, task
from twisted.internet.defer import DeferredList
from twisted.python import log, usage
import os, string, time, struct
from os.path import curdir, join as pjoin
from ConfigParser import ConfigParser
safe_chars = string.ascii_letters+string.digits+' :;.,+-=$@'
WAV_HEADER = "524946460c06000057415645666d74201400000031000100401f00005906000041000000020040016661637404000000000000006461746100000000".decode('hex')
AUDIO_TIMEOUT = 5.0
def sanitizeFilename(name):
r = ''
for c in name:
if c in safe_chars:
r += c
return r
class FRNRecorder(FRNClient):
def getClientName(self, client_id):
if self.clientsById.has_key(client_id):
return self.clientsById[client_id]['on']
else:
return client_id
def buildRecordingName(self, client_id):
ts = time.localtime()
opname = sanitizeFilename(self.clients[client_id-1]['on'])
dirname = self.factory.recdir+"/"+time.strftime("%Y%m/%d/%H", ts)
filename = time.strftime("%Y%m%d-%H%M%S-", ts) + opname + '.wav'
try:
os.makedirs(dirname)
except OSError: pass
return dirname+'/'+filename
def textMessageReceived(self, client, message, target):
pass
def closeFile(self):
size = self.recordingFile.tell()-len(WAV_HEADER)
time = size / 1625.0
if time > self.factory.mintime:
self.recordingFile.seek(0x30) # fact chunk value
self.recordingFile.write(struct.pack("<L", int(size/32.5)))
self.recordingFile.seek(0x38) # data chunk size
self.recordingFile.write(struct.pack("<L", size))
self.recordingFile.close()
else:
name = self.recordingFile.name
log.msg("Deleting short recording: %s" % (name,))
self.recordingFile.close()
os.remove(name)
def archiveRecording(self, from_id):
log.msg("%s stopped talking" %
self.clients[from_id-1]['on'])
self.closeFile()
self.recordingFile = None
self.recordingOperator = None
def audioFrameReceived(self, from_id, frames):
if self.recordingOperator != self.clients[from_id-1]['on']:
if self.recordingOperator is not None:
self.closeFile()
self.recordingOperator = self.clients[from_id-1]['on']
self.recordingFile = file(self.buildRecordingName(from_id), 'wb')
self.recordingFile.write(WAV_HEADER)
self.recordingFile.write(frames)
try:
self.parrot_timer.reset(AUDIO_TIMEOUT)
except:
log.msg("%s started talking" %
self.clients[from_id-1]['on'])
self.parrot_timer = self.factory.reactor.callLater(
AUDIO_TIMEOUT, self.archiveRecording, from_id)
self.pong()
def loginResponse(self, info):
log.msg("Login: %s" % info['al'])
self.setStatus(1)
self.recordingOperator = None
self.recordingFile = None
def clientsListUpdated(self, clients):
self.clients = clients
self.clientsById = dict([(i['id'], i) for i in clients])
class FRNRecorderFactory(FRNClientFactory):
protocol = FRNRecorder
reactor = reactor
def __init__(self, user, recdir="/var/spool/grn/recordings", mintime=2.0):
self.recdir = recdir
self.mintime = mintime
FRNClientFactory.__init__(self, user)
class Options(usage.Options):
optParameters = [
["confdir", "c", curdir, "Directory containing config files"],
["recdir", "d", "/var/spool/grn/recordings", "Directory where to save recordings"],
["mintime", "m", 2.0, "Minimun recording duration"]
]
def parseArgs(self, serverdef):
basedir = self['confdir']
acfg = ConfigParser()
acfg.read(['/etc/grn/accounts.conf',
pjoin(basedir,'accounts.conf'), 'accounts.conf'])
scfg = ConfigParser()
scfg.read(['/etc/grn/servers.conf',
pjoin(basedir,'servers.conf'), 'servers.conf'])
account_name, server_name, network_name = serverdef.split(':', 2)
self['server'] = scfg.get(server_name, 'server')
self['port'] = scfg.getint(server_name, 'port')
self['backup_server'] = scfg.get(server_name, 'backup_server')
self['backup_port'] = scfg.getint(server_name, 'backup_port')
self['operator'] = acfg.get(account_name, 'operator')
self['email'] = acfg.get(account_name, 'email')
self['password'] = acfg.get(account_name, 'password')
self['description'] = acfg.get(account_name, 'description')
self['country'] = acfg.get(account_name, 'country')
self['city'] = acfg.get(account_name, 'city')
self['network'] = network_name
class FRNRecorderServiceMaker(object):
implements(IServiceMaker, IPlugin)
tapname = "frnrecorder"
description = "Freeradionetwork recorder"
options = Options
def makeService(self, options):
return internet.TCPClient(options['server'], options['port'],
FRNRecorderFactory(
FRNUser(
EA=options['email'],
PW=options['password'], ON=options['operator'],
BC="Crosslink", DS=options['description'],
NN=options['country'], CT=options['city'], NT=options['network']),
options['recdir'], options['mintime']))
serviceMaker = FRNRecorderServiceMaker()
# vim: set et ai sw=4 ts=4 sts=4: