From eb08b39f4f7b3eb2fa15019df36f9483e359b63e Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Thu, 11 Nov 2010 17:18:22 +0100 Subject: [PATCH] add a cache to the webview (analyzers and graphers) --- telemeta/cache.py | 120 ++++++++++++++++++++++++++++++++++++++++ telemeta/util/logger.py | 8 +-- telemeta/web/base.py | 82 +++++++++++++-------------- 3 files changed, 159 insertions(+), 51 deletions(-) create mode 100644 telemeta/cache.py diff --git a/telemeta/cache.py b/telemeta/cache.py new file mode 100644 index 00000000..b5f2fd9a --- /dev/null +++ b/telemeta/cache.py @@ -0,0 +1,120 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2006-2010 Guillaume Pellerin + +# + +# This software is a computer program whose purpose is to stream audio +# and video data through icecast2 servers. + +# This software is governed by the CeCILL license under French law and +# abiding by the rules of distribution of free software. You can use, +# modify and/ or redistribute the software under the terms of the CeCILL +# license as circulated by CEA, CNRS and INRIA at the following URL +# "http://www.cecill.info". + +# As a counterpart to the access to the source code and rights to copy, +# modify and redistribute granted by the license, users are provided only +# with a limited warranty and the software's author, the holder of the +# economic rights, and the successive licensors have only limited +# liability. + +# In this respect, the user's attention is drawn to the risks associated +# with loading, using, modifying and/or developing or reproducing the +# software by the user in light of its specific status of free software, +# that may mean that it is complicated to manipulate, and that also +# therefore means that it is reserved for developers and experienced +# professionals having in-depth computer knowledge. Users are therefore +# encouraged to load and test the software's suitability as regards their +# requirements in conditions enabling the security of their systems and/or +# data to be ensured and, more generally, to use and operate it in the +# same conditions as regards security. + +# The fact that you are presently reading this means that you have had +# knowledge of the CeCILL license and that you accept its terms. + +# Author: Guillaume Pellerin + +import os +import xml.dom.minidom +#import xml.dom.ext + + +class TelemetaCache(object): + + def __init__(self, dir, params=None): + self.dir = dir + self.params = params + self.files = self.get_files() + + def get_files(self): + list = [] + for root, dirs, files in os.walk(self.dir): + for file in files: + list.append(file) + return list + + def exists(self, file): + self.files = self.get_files() + return file in self.files + + def write_bin(self, data, file): + path = self.dir + os.sep + file + f = open(path, 'w') + f.write(data) + f.close() + + def read_bin(self, file): + path = self.dir + os.sep + file + f = open(path, 'r') + data = f.read() + f.close() + return data + + def read_stream_bin(self, file): + path = self.dir + os.sep + file + chunk_size = 0x1000 + f = open(path, 'r') + while True: + _chunk = f.read(chunk_size) + if not len(_chunk): + break + yield _chunk + f.close() + + def write_stream_bin(self, chunk, file_object): + file_object.write(chunk) + + def read_analyzer_xml(self, file): + list = [] + path = self.dir + os.sep + file + doc = xml.dom.minidom.parse(path) + for data in doc.documentElement.getElementsByTagName('data') : + name = data.getAttribute('name') + id = data.getAttribute('id') + unit = data.getAttribute('unit') + value = data.getAttribute('value') + list.append({'name': name, 'id': id, 'unit': unit, 'value': value}) + return list + + def write_analyzer_xml(self, data_list, file): + path = self.dir + os.sep + file + doc = xml.dom.minidom.Document() + root = doc.createElement('telemeta') + doc.appendChild(root) + for data in data_list: + name = data['name'] + id = data['id'] + unit = data['unit'] + value = data['value'] + node = doc.createElement('data') + node.setAttribute('name', name) + node.setAttribute('id', id) + node.setAttribute('unit', unit) + node.setAttribute('value', str(value)) + root.appendChild(node) + f = open(path, "w") + f.write(xml.dom.minidom.Document.toprettyxml(doc)) +# xml.dom.ext.PrettyPrint() + f.close() diff --git a/telemeta/util/logger.py b/telemeta/util/logger.py index 41ed0086..f9a032c8 100644 --- a/telemeta/util/logger.py +++ b/telemeta/util/logger.py @@ -12,12 +12,6 @@ class Logger: self.logger.addHandler(self.hdlr) self.logger.setLevel(logging.INFO) - def write_info(self, message): - self.logger.info(message) - - def write_error(self, message): - self.logger.error(message) - def debug(self, msg): - self.logger.error(message) + self.logger.error('DEBUG : ' + message) diff --git a/telemeta/web/base.py b/telemeta/web/base.py index aa98265a..2165a825 100644 --- a/telemeta/web/base.py +++ b/telemeta/web/base.py @@ -56,21 +56,13 @@ from django.core.exceptions import ObjectDoesNotExist from telemeta.util.unaccent import unaccent from telemeta.util.unaccent import unaccent_icmp from telemeta.util.logger import Logger +from telemeta.cache import TelemetaCache import telemeta.web.pages as pages def render(request, template, data = None, mimetype = None): return render_to_response(template, data, context_instance=RequestContext(request), mimetype=mimetype) -def stream_from_file(file): - chunk_size = 0x1000 - f = open(file, 'r') - while True: - _chunk = f.read(chunk_size) - if not len(_chunk): - break - yield _chunk - f.close() def stream_from_processor(decoder, processor): while True: @@ -88,7 +80,7 @@ class WebView: decoders = timeside.core.processors(timeside.api.IDecoder) encoders= timeside.core.processors(timeside.api.IEncoder) analyzers = timeside.core.processors(timeside.api.IAnalyzer) - logger = Logger('/tmp/telemeta.log') + cache = TelemetaCache(settings.TELEMETA_DATA_CACHE_DIR) def index(self, request): """Render the homepage""" @@ -123,11 +115,12 @@ class WebView: else: grapher_id = 'waveform' - analyzers = [{'name':'','id':'','unit':'','value':''}] - # TODO: override timeside analyzer process when caching : write results to XML file in data/ - self.analyzer_mode = 1 + file = public_id + '.xml' - if self.analyzer_mode: + if self.cache.exists(file): + analyzers = self.cache.read_analyzer_xml(file) + else: + analyzers = [] analyzers_sub = [] if item.file: audio = os.path.join(os.path.dirname(__file__), item.file.path) @@ -139,25 +132,27 @@ class WebView: self.pipe = self.pipe | subpipe self.pipe.run() - for analyzer in analyzers_sub: - if item.file: - value = analyzer.result() - if analyzer.id() == 'duration': - approx_value = int(round(value)) - item.approx_duration = approx_value - item.save() - value = datetime.timedelta(0,value) - else: - value = 'N/A' - - analyzers.append({'name':analyzer.name(), - 'id':analyzer.id(), - 'unit':analyzer.unit(), - 'value':str(value)}) + for analyzer in analyzers_sub: + if item.file: + value = analyzer.result() + if analyzer.id() == 'duration': + approx_value = int(round(value)) + item.approx_duration = approx_value + item.save() + value = datetime.timedelta(0,value) + else: + value = 'N/A' + + analyzers.append({'name':analyzer.name(), + 'id':analyzer.id(), + 'unit':analyzer.unit(), + 'value':str(value)}) + + self.cache.write_analyzer_xml(analyzers, file) return render(request, template, {'item': item, 'export_formats': formats, - 'visualizers': graphers, 'visualizer_id': grapher_id,'analysers': analyzers, + 'visualizers': graphers, 'visualizer_id': grapher_id,'analysers': analyzers, #FIXME analysers 'audio_export_enabled': getattr(settings, 'TELEMETA_DOWNLOAD_ENABLED', False) }) @@ -168,6 +163,7 @@ class WebView: pass def item_visualize(self, request, public_id, visualizer_id, width, height): + item = MediaItem.objects.get(public_id=public_id) mime_type = 'image/png' grapher_id = visualizer_id for grapher in self.graphers: @@ -177,21 +173,19 @@ class WebView: if grapher.id() != grapher_id: raise Http404 - media = settings.TELEMETA_DATA_CACHE_DIR + \ - os.sep + '_'.join([public_id, grapher_id, width, height]) + '.png' - - #graph.set_colors((255,255,255), 'purple') + file = '_'.join([public_id, grapher_id, width, height]) + '.png' - if not os.path.exists(media): - item = MediaItem.objects.get(public_id=public_id) - audio = os.path.join(os.path.dirname(__file__), item.file.path) - decoder = timeside.decoder.FileDecoder(audio) - graph = grapher(width=int(width), height=int(height)) - pipe = decoder | graph - pipe.run() - graph.render(media) - - response = HttpResponse(stream_from_file(media), mimetype = mime_type) + if not self.cache.exists(file): + if item.file: + item = MediaItem.objects.get(public_id=public_id) + audio = os.path.join(os.path.dirname(__file__), item.file.path) + decoder = timeside.decoder.FileDecoder(audio) + graph = grapher(width=int(width), height=int(height)) + pipe = decoder | graph + pipe.run() + graph.render(self.cache.dir + os.sep + file) + + response = HttpResponse(self.cache.read_stream_bin(file), mimetype = mime_type) return response def list_export_extensions(self): -- 2.39.5