gnuradionetwork/twisted/plugins/frnrecorder.py

181 lines
6.4 KiB
Python
Executable File

#!/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 buildMessageLogName(self):
ts = time.localtime()
dirname = self.factory.recdir+"/"+time.strftime("%Y%m/%d/%H", ts)
filename = time.strftime("%Y%m%d-%H-", ts) + 'messages.txt'
try:
os.makedirs(dirname)
except OSError: pass
return dirname+'/'+filename
def textMessageReceived(self, client, message, target):
if target == 'A':
f = open(self.buildMessageLogName(), 'a')
ts = time.strftime("%Y-%m-%d %H:%M:%S")
f.write("[%s] [%s] %s\r\n" %
(ts, self.getClientName(client), message))
f.close()
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: