--- /dev/null
+Guillaume Pellerin <yomguy@parisson.com>
+++ /dev/null
-==================
-INSTALL TeleCaster
-==================
-
-
-1. Operating System
-===================
-
-TeleCaster now only works on GNU/Linux systems. The installer and the following instructions
-are based on Debian like software management so that it should work on Debian (>= Lenny)
-or Ubuntu / Kubuntu (>= 10.4). So please install one of these OS before.
-
-
-2. Install dependencies
-=======================
-
-Needed::
-
- sudo aptitude update
-
- sudo aptitude install python python-dev python-xml python-libxml2 python-setuptools python-twitter python-liblo python-mutagen \
- icecast2 apache2 apache2-suexec jackd libjack-dev vorbis-tools procps meterbridge fluxbox \
- vnc4server vncviewer swh-plugins jack-rack libshout3 libshout3-dev libmad0-dev libogg-dev g++
-
-Warning: on Debian Squeeze or recent Ubuntu, change libjack-dev to libjack-jackd2-dev
-
-Optional::
-
- sudo aptitude install libfaac-dev libmp3lame-dev libflac-dev
-
-Note that obtaining and installing a preempt RT kernel is STRONGLY advised to get a good audio (JACK) stability.
-Moreover, edit the pam conf file to get RT "su" pam limits at boot::
-
- sudo vi /etc/pam.d/su
-
-Uncomment::
-
- session required pam_limits.so
-
-
-3. Install TeleCaster
-=====================
-
-Untar the archive. For example::
-
- tar xzf telecaster-0.5.tar.gz
-
-Run the install script::
-
- cd telecaster-0.5/
- sudo python install.py
-
-
-4. Configuration
-================
-
-Edit the following files to setup TeleCaster. Please be careful with the XML syntax::
-
- /etc/telecaster/telecaster.xml
-
-and, ONLY if needed::
-
- /etc/default/jackd
- /etc/default/vncserver
-
-
-5. Start audio deamons
-======================
-
-Just reboot your machine or start the deamons manually::
-
- sudo /etc/init.d/jackd start
- sudo /etc/init.d/vncserver start
-
-
-6. Configure Apache2
-====================
-
-Configure your apache VirtualHost editing /etc/apache2/sites-available/telecaster.conf
-
-Enable the VirtualHost::
-
- sudo a2ensite telecaster.conf
-
-Maybe remove the default VirtualHost::
-
- sudo rm /etc/apache2/sites-enabled/000-default
-
-Reload Apache::
-
- sudo /etc/init.d/apache2 reload
-
-
-7. Usage
-========
-
-Browse the TeleCaster web control page:
-
- http://localhost/telecaster/telecaster.py
-
-Fill in the form and start any free recording and broadcasting stream !
-
-To change the form options, just edit the conf file as root::
-
- sudo vi /etc/telecaster/telecaster.xml
-
-
-8. Contact
-==========
-
-Any questions, suggestions ? Please post a ticket on the dev platform:
-
- http://svn.parisson.org/telecaster
-
-or contact the main developer:
-
- Guillaume Pellerin <yomguy@parisson.com>
+++ /dev/null
-#!/bin/sh
-
-# Start TeleCaster video channel
-
-WIDTH=640
-HEIGHT=480
-#WIDTH=1024
-#HEIGHT=576
-
-gst-launch v4l2src device=/dev/video0 ! video/x-raw-yuv, width=$WIDTH, height=$HEIGHT \
- ! queue ! ffmpegcolorspace \
- ! queue ! vp8enc speed=2 threads=4 quality=9.0 ! queue ! muxout. \
- webmmux streamable=true name=muxout \
- ! queue ! tcpserversink host=127.0.0.1 port=9000 protocol=none
-
# -*- coding: utf-8 -*-
+
from setuptools import setup, find_packages
-import os
-import telecaster
CLASSIFIERS = ['Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Science/Research', 'Intended Audience :: Education', 'Programming Language :: Python', 'Programming Language :: JavaScript', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', 'Topic :: Multimedia :: Sound/Audio', 'Topic :: Multimedia :: Sound/Audio :: Analysis', 'Topic :: Multimedia :: Sound/Audio :: Players', 'Topic :: Scientific/Engineering :: Information Analysis', 'Topic :: System :: Archiving', ]
setup(
- name = "TeleCaster",
+ name = "telecaster-client",
url = "http://parisson.com",
- description = "open web audio CMS",
+ description = "Audio and video live recording and streaming module for the e-learning system TeleForma (Python, Django, Javascript)",
long_description = open('README.rst').read(),
author = "Guillaume Pellerin",
author_email = "yomguy@parisson.com",
- version = '0.6',
+ version = '0.8',
install_requires = [
'django>=1.4.0',
'django-json-rpc',
+++ /dev/null
-Guillaume Pellerin <yomguy@parisson.com>
+++ /dev/null
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# Copyright Guillaume Pellerin (2006-2010)
-
-# <yomguy@parisson.com>
-
-# 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 <yomguy@parisson.com>
-
-# ONLY FOR GNU/LINUX Debian
-
-import os, sys
-import platform
-import shutil
-
-def remove_svn(path):
- for root, dirs, files in os.walk(path):
- for dir in dirs:
- if '.svn' in dir:
- shutil.rmtree(root + os.sep + dir)
-
-app_dir = os.getcwd()
-
-user = 'telecaster'
-home = '/home/' + user
-if not os.path.exists(home):
- print 'Please give some informations for the new "telecaster" user :'
- os.system('adduser ' + user)
-
-# compiling edcast-jack
-os.chdir(app_dir + '/tools/edcast-jack')
-os.system('./configure; make; sudo make install')
-
-# installing deefuzzer
-os.chdir(app_dir + '/tools/deefuzzer')
-os.system('sudo python install.py')
-
-os.chdir(app_dir)
-install_dir = '/var/www/telecaster'
-if os.path.exists(install_dir):
- shutil.rmtree(install_dir)
-shutil.copytree(app_dir, install_dir,ignore=shutil.ignore_patterns('edcast-jack*', 'deefuzzer*', '*.svn*'))
-os.system('chown -R ' + user + ':' + user + ' ' + install_dir)
-
-conf_dir = '/etc/telecaster'
-if not os.path.exists(conf_dir):
- os.mkdir(conf_dir)
-else:
- in_files = os.listdir('conf'+conf_dir)
- for file in in_files:
- if not os.path.exists(conf_dir+os.sep+file) and not '.svn' in file:
- shutil.copy('conf'+conf_dir+os.sep+file, conf_dir+os.sep+file)
-
-
-daemons = ['jackd', 'vncserver']
-dir = '/etc/init.d/'
-for daemon in daemons:
- path = dir + daemon
- shutil.copy('conf'+path, dir)
-
-dir = '/etc/default/'
-for daemon in daemons:
- path = dir + daemon
- if not os.path.exists(path):
- shutil.copy('conf'+path, dir)
-
-init_link = '/etc/rc2.d/S97jackd'
-if not os.path.islink(init_link):
- os.symlink('/etc/init.d/jackd ', init_link)
-
-init_link = '/etc/rc2.d/S99vncserver'
-if not os.path.islink(init_link):
- os.symlink('/etc/init.d/vncserver ', init_link)
-
-home_dirs = ['fluxbox', 'vnc']
-for dir in home_dirs:
- home_dir = home + '/.' + dir
- if not os.path.exists(home_dir):
- shutil.copytree('conf/home/'+dir, home_dir, ignore=shutil.ignore_patterns('*.svn*'))
- os.system('chown -R ' + user + ':' + user + ' ' + home_dir)
-
-dir = 'media'
-home_dir = home + os.sep + dir
-if not os.path.exists(home_dir):
- shutil.copytree('conf/home/'+dir, home_dir, ignore=shutil.ignore_patterns('*.svn*'))
- os.system('chown -R ' + user + ':' + user + ' ' + home_dir)
-
-apache_conf = '/etc/apache2/sites-available/telecaster.conf'
-if not os.path.exists(apache_conf):
- shutil.copy('conf'+apache_conf, apache_conf)
-os.system('/etc/init.d/apache2 reload')
-
-log_dirs = ['/var/log/telecaster', '/var/log/deefuzzer']
-for dir in log_dirs:
- if not os.path.exists(dir):
- os.mkdir(dir)
- os.system('chown -R ' + user + ':' + user + ' ' + dir)
-
-print """
- Installation successfull !
-
- Now, please :
- - configure your telecaster editing /etc/telecaster/telecaster.xml
- - configure your apache VirtualHost editing /etc/apache2/sites-available/telecaster.conf
-
- And use the TeleCaster system browsing http://localhost/telecaster/telecaster.py
-
- See README for more infos.
- """
-
+++ /dev/null
-#!/usr/bin/python
-# Capture 3 seconds of stereo audio from alsa_pcm:capture_1/2; then play it back.
-#
-# Copyright 2003, Andrew W. Schmeder
-# This source code is released under the terms of the GNU Public License.
-# See LICENSE for the full text of these terms.
-
-import Numeric
-import jack
-import time
-
-class JackInput:
- "A JACK connexion input in TeleOddCast"
-
- def __init__(self, dict):
- self.host = dict['host']
- self.name = dict['name']
- self.jack = jack()
- self.buffer_size = self.jack.get_buffer_size()
- self.sample_rate = float(self.jack.get_sample_rate())
- print "Buffer Size:", N, "Sample Rate:", Sr
- self.power = True
- self.capture = Numeric.zeros((2, self.buffer_size), 'f')
-
-
- def attach(self):
- jack.attach(self.name)
-
- def get_ports(self):
- return self.jack.get_ports()
-
- def register_ports(self):
- self.jack.register_port("in_1", self.jack.IsInput)
- self.jack.register_port("in_2", self.jack.IsInput)
-
- def activate(self):
- self.jack.activate()
-
- def stop(self):
- self.power = False
-
- def connect(self):
- self.jack.connect("alsa_pcm:capture_1", self.name+":in_1")
- self.jack.connect("alsa_pcm:capture_2", self.name+":in_2")
- #jack.connect(self.name+":out_1", "alsa_pcm:playback_1")
- #jack.connect(self.name+":out_2", "alsa_pcm:playback_2")
-
- def process(self):
- while True:
- try:
- if not self.power:
- break
- self.jack.process(__chunk, capture[:,self.buffer_size])
- yield __chunk
- except self.jack.InputSyncError:
- print "Input Sync"
- pass
- except self.jack.OutputSyncError:
- print "Output Sync"
- pass
-
- def desactivate(self):
- self.jack.deactivate()
-
- def detach(self):
- self.jack.detach()
+++ /dev/null
-\r
-//OBJECTS\r
-\r
-//objects inside the RSS2Item object\r
-function RSS2Enclosure(encElement)\r
-{\r
- if (encElement == null)\r
- {\r
- this.url = null;\r
- this.length = null;\r
- this.type = null;\r
- }\r
- else\r
- {\r
- this.url = encElement.getAttribute("url");\r
- this.length = encElement.getAttribute("length");\r
- this.type = encElement.getAttribute("type");\r
- }\r
-}\r
-\r
-function RSS2Guid(guidElement)\r
-{\r
- if (guidElement == null)\r
- {\r
- this.isPermaLink = null;\r
- this.value = null;\r
- }\r
- else\r
- {\r
- this.isPermaLink = guidElement.getAttribute("isPermaLink");\r
- this.value = guidElement.childNodes[0].nodeValue;\r
- }\r
-}\r
-\r
-function RSS2Source(souElement)\r
-{\r
- if (souElement == null)\r
- {\r
- this.url = null;\r
- this.value = null;\r
- }\r
- else\r
- {\r
- this.url = souElement.getAttribute("url");\r
- this.value = souElement.childNodes[0].nodeValue;\r
- }\r
-}\r
-\r
-//object containing the RSS 2.0 item\r
-function RSS2Item(itemxml)\r
-{\r
- //required\r
- this.title;\r
- this.link;\r
- this.description;\r
-\r
- //optional vars\r
- this.author;\r
- this.comments;\r
- this.pubDate;\r
-\r
- //optional objects\r
- this.category;\r
- this.enclosure;\r
- this.guid;\r
- this.source;\r
-\r
- var properties = new Array("title", "link", "description", "author", "comments", "pubDate");\r
- var tmpElement = null;\r
- for (var i=0; i<properties.length; i++)\r
- {\r
- tmpElement = itemxml.getElementsByTagName(properties[i])[0];\r
- if (tmpElement != null)\r
- eval("this."+properties[i]+"=tmpElement.childNodes[0].nodeValue");\r
- }\r
-\r
- this.category = new RSS2Category(itemxml.getElementsByTagName("category")[0]);\r
- this.enclosure = new RSS2Enclosure(itemxml.getElementsByTagName("enclosure")[0]);\r
- this.guid = new RSS2Guid(itemxml.getElementsByTagName("guid")[0]);\r
- this.source = new RSS2Source(itemxml.getElementsByTagName("source")[0]);\r
-}\r
-\r
-//objects inside the RSS2Channel object\r
-function RSS2Category(catElement)\r
-{\r
- if (catElement == null)\r
- {\r
- this.domain = null;\r
- this.value = null;\r
- }\r
- else\r
- {\r
- this.domain = catElement.getAttribute("domain");\r
- this.value = catElement.childNodes[0].nodeValue;\r
- }\r
-}\r
-\r
-//object containing RSS image tag info\r
-function RSS2Image(imgElement)\r
-{\r
- if (imgElement == null)\r
- {\r
- this.url = null;\r
- this.link = null;\r
- this.width = null;\r
- this.height = null;\r
- this.description = null;\r
- }\r
- else\r
- {\r
- imgAttribs = new Array("url","title","link","width","height","description");\r
- for (var i=0; i<imgAttribs.length; i++)\r
- if (imgElement.getAttribute(imgAttribs[i]) != null)\r
- eval("this."+imgAttribs[i]+"=imgElement.getAttribute("+imgAttribs[i]+")");\r
- }\r
-}\r
-\r
-//object containing the parsed RSS 2.0 channel\r
-function RSS2Channel(rssxml)\r
-{\r
- //required\r
- this.title;\r
- this.link;\r
- this.description;\r
-\r
- //array of RSS2Item objects\r
- this.items = new Array();\r
-\r
- //optional vars\r
- this.language;\r
- this.copyright;\r
- this.managingEditor;\r
- this.webMaster;\r
- this.pubDate;\r
- this.lastBuildDate;\r
- this.generator;\r
- this.docs;\r
- this.ttl;\r
- this.rating;\r
-\r
- //optional objects\r
- this.category;\r
- this.image;\r
-\r
- var chanElement = rssxml.getElementsByTagName("channel")[0];\r
- var itemElements = rssxml.getElementsByTagName("item");\r
-\r
- for (var i=0; i<itemElements.length; i++)\r
- {\r
- Item = new RSS2Item(itemElements[i]);\r
- this.items.push(Item);\r
- //chanElement.removeChild(itemElements[i]);\r
- }\r
-\r
- var properties = new Array("title", "link", "description", "language", "copyright", "managingEditor", "webMaster", "pubDate", "lastBuildDate", "generator", "docs", "ttl", "rating");\r
- var tmpElement = null;\r
- for (var i=0; i<properties.length; i++)\r
- {\r
- tmpElement = chanElement.getElementsByTagName(properties[i])[0];\r
- if (tmpElement!= null)\r
- eval("this."+properties[i]+"=tmpElement.childNodes[0].nodeValue");\r
- }\r
-\r
- this.category = new RSS2Category(chanElement.getElementsByTagName("category")[0]);\r
- this.image = new RSS2Image(chanElement.getElementsByTagName("image")[0]);\r
-}\r
-\r
-//PROCESSES\r
-\r
-//uses xmlhttpreq to get the raw rss xml\r
-function getRSS(url)\r
-{\r
- //call the right constructor for the browser being used\r
- if (window.ActiveXObject)\r
- xhr = new ActiveXObject("Microsoft.XMLHTTP");\r
- else if (window.XMLHttpRequest)\r
- xhr = new XMLHttpRequest();\r
- else\r
- alert("not supported");\r
-\r
- //prepare the xmlhttprequest object\r
- xhr.open("GET",url,true);\r
- xhr.setRequestHeader("Cache-Control", "no-cache");\r
- xhr.setRequestHeader("Pragma", "no-cache");\r
- xhr.onreadystatechange = function() {\r
- if (xhr.readyState == 4)\r
- {\r
- if (xhr.status == 200)\r
- {\r
- if (xhr.responseText != null)\r
- processRSS(xhr.responseXML);\r
- else\r
- {\r
- alert("Failed to receive RSS file from the server - file not found.");\r
- return false;\r
- }\r
- }\r
- else\r
- alert("Error code " + xhr.status + " received: " + xhr.statusText);\r
- }\r
- }\r
-\r
- //send the request\r
- xhr.send(null);\r
-}\r
-\r
-//processes the received rss xml\r
-function processRSS(rssxml)\r
-{\r
- RSS = new RSS2Channel(rssxml);\r
- showRSS(RSS);\r
-}\r
-\r
-//shows the RSS content in the browser\r
-function showRSS(RSS)\r
-{\r
- //default values for html tags used\r
- var imageTag = "<img id='chan_image'";\r
- var startItemTag = "<div id='item'>";\r
- var startTitle = "<div id='item_title'>";\r
- var startLink = "<div id='item_link'>";\r
- var startDescription = "<div id='item_description'>";\r
- var endTag = "</div>";\r
-\r
- //populate channel data\r
- var properties = new Array("title","link","description","pubDate","copyright");\r
- for (var i=0; i<properties.length; i++)\r
- {\r
- property = properties[i]\r
- eval("document.getElementById('chan_"+property+"').innerHTML = ''");\r
- curProp = eval("RSS."+property);\r
- if (curProp != null){\r
- eval("document.getElementById('chan_"+property+"').innerHTML = curProp");\r
- }\r
- }\r
-\r
- //show the image\r
- document.getElementById("chan_image_link").innerHTML = "";\r
- if (RSS.image.src != null)\r
- {\r
- document.getElementById("chan_image_link").href = RSS.image.link;\r
- document.getElementById("chan_image_link").innerHTML = imageTag\r
- +" alt='"+RSS.image.description\r
- +"' width='"+RSS.image.width\r
- +"' height='"+RSS.image.height\r
- +"' src='"+RSS.image.url\r
- +"' "+"/>";\r
- }\r
-\r
- //populate the items\r
- document.getElementById("chan_items").innerHTML = "";\r
- for (var i=0; i<RSS.items.length; i++)\r
- {\r
- item_html = startItemTag;\r
- item_html += (RSS.items[i].title == null) ? "" : startTitle + RSS.items[i].title + endTag;\r
- item_html += (RSS.items[i].description == null) ? "" : startDescription + RSS.items[i].description + endTag;\r
- item_html += (RSS.items[i].link == null) ? "" : startLink + "<a href='" + RSS.items[i].link + "'>" + RSS.items[i].link + "</a>" + endTag;\r
- item_html += endTag;\r
- document.getElementById("chan_items").innerHTML += item_html;\r
- }\r
-\r
- //we're done\r
- //document.getElementById("chan").style.visibility = "visible";\r
- return true;\r
-}\r
-\r
-var xhr;\r
-\r
-\r
-function callExternalScript(url){\r
- var n = document.createElement("script");\r
- n.setAttribute("type", "text/javascript");\r
- n.setAttribute("src", url);\r
- document.getElementsByTagName("head")[0].appendChild(n);\r
-}\r
+++ /dev/null
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# *-* coding: utf-8 *-*
-"""
- telecaster
-
- Copyright (c) 2006-2010 Guillaume Pellerin <yomguy@parisson.org>
-
-# 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 <yomguy@parisson.com>
-
-"""
-
-import os
-import pwd
-import datetime
-import time
-import urllib
-import liblo
-from tools import *
-from mutagen.oggvorbis import OggVorbis
-from mutagen.id3 import ID3, TIT2, TP1, TAL, TDA, TDAT, TDRC, TCO, COM
-#import jack
-
-
-class Conference:
- """A conference object including metadata"""
-
- def __init__(self, dict):
- self.dict = dict
- self.title = dict['title']
- self.department = dict['department']
- self.conference = dict['conference']
- self.session = dict['session']
- self.professor = dict['professor']
- self.comment = dict['comment']
-
- def get_info(self):
- return self.title, self.department, self.conference, self.session, self.professor, self.comment
-
-
-class Station(Conference):
- """Control the Oddcastv3-jack thread which send audio data to the icecast server
- and the Streamripper thread which write audio on the hard disk"""
-
- def __init__(self, conf_file, conference_dict, lock_file):
- Conference.__init__(self, conference_dict)
- self.date = datetime.datetime.now().strftime("%Y")
- self.time = datetime.datetime.now().strftime("%x-%X")
- self.time_txt = self.time.replace('/','_').replace(':','_').replace(' ','_')
- self.conf = xml2dict(conf_file)
- self.lock_file = lock_file
- self.conf = self.conf['telecaster']
- self.user = pwd.getpwuid(os.getuid())[0]
- self.user_dir = '/home' + os.sep + self.user + os.sep + '.telecaster'
- self.rec_dir = self.conf['media']['rec_dir']
- self.deefuzzer_default_conf_file = self.conf['deefuzzer']['conf']
- self.deefuzzer_user_file = self.user_dir + os.sep + 'deefuzzer.xml'
- self.bitrate = self.conf['media']['bitrate']
- self.dict['Bitrate'] = str(self.bitrate) + ' kbps'
- self.record = str_to_bool(self.conf['media']['record'])
- self.rec_dir = self.conf['media']['rec_dir']
- self.play_dir = self.conf['media']['play_dir']
- self.ogg_quality = self.conf['media']['ogg_quality']
- self.format = self.conf['media']['format']
- self.channels = int(self.conf['media']['channels'])
- self.description = [self.title, self.department, self.conference, self.session, self.professor, self.comment]
- self.server_name = [self.title, self.department, self.conference]
- self.ServerDescription = clean_string('-'.join(self.description))
- self.ServerName = clean_string('_-_'.join(self.server_name))
- self.mount_point = self.ServerName
- self.filename = clean_string('_-_'.join(self.description[1:])) + '-' + self.time_txt + '.' + self.format
- self.output_dir = self.rec_dir + os.sep + self.date + os.sep + self.department
- self.file_dir = self.output_dir + os.sep + self.ServerName
- self.uid = os.getuid()
- self.odd_pid = get_pid('^edcast_jack', self.uid)
- self.deefuzzer_pid = get_pid('/usr/bin/deefuzzer '+self.deefuzzer_user_file, self.uid)
- self.new_title = clean_string('-'.join(self.server_name)+'-'+self.session+'-'+self.professor+'-'+self.comment)
- self.short_title = clean_string('-'.join(self.conference)+'-'+self.session+'-'+self.professor+'-'+self.comment)
- self.genre = self.conf['infos']['genre']
- self.encoder = 'TeleCaster by Parisson'
-
- if not os.path.exists(self.file_dir):
- os.makedirs(self.file_dir)
-
- self.jack_inputs = []
- if 'jack' in self.conf:
- jack_inputs = self.conf['jack']['input']
- if len(jack_inputs) > 1:
- for jack_input in jack_inputs:
- self.jack_inputs.append(jack_input['name'])
- else:
- self.jack_inputs.append(jack_inputs['name'])
-
- self.deefuzzer_dict = xml2dict(self.deefuzzer_default_conf_file)
- self.deefuzzer_osc_ports = []
- self.server_ports = []
-
- for station in self.deefuzzer_dict['deefuzzer']['station']:
- if station['control']['mode'] == '1':
- self.deefuzzer_osc_ports.append(station['control']['port'])
- self.server_ports.append(station['server']['port'])
- if station['server']['host'] == 'localhost' or station['server']['host'] == '127.0.0.1':
- self.conf['play_port'] = station['server']['port']
- else:
- self.conf['play_port'] = '8000'
-
- def deefuzzer_setup(self):
- i = 0
- for station in self.deefuzzer_dict['deefuzzer']['station']:
- station['infos']['short_name'] = self.mount_point
- station['infos']['name'] = self.ServerName
- station['infos']['description'] = self.ServerDescription.replace(' ','_')
- station['infos']['genre'] = self.genre
- station['media']['bitrate'] = self.bitrate
- station['media']['dir'] = self.play_dir
- station['media']['voices'] = str(len(self.jack_inputs))
- station['record']['dir'] = self.file_dir
- station['relay']['mode'] = '1'
- station['relay']['author'] = self.professor
- self.deefuzzer_dict['deefuzzer']['station'][i] = station
- i += 1
- self.deefuzzer_xml = dicttoxml(self.deefuzzer_dict)
-
- def deefuzzer_write_conf(self):
- conf_file = open(self.deefuzzer_user_file,'w')
- conf_file.write(self.deefuzzer_xml)
- conf_file.close()
-
- def deefuzzer_start(self):
- command = 'deefuzzer ' + self.deefuzzer_user_file + ' > /dev/null &'
- os.system(command)
- self.set_lock()
-
- def set_lock(self):
- lock = open(self.lock_file,'w')
- lock_text = clean_string('_*_'.join(self.description))
- lock_text = lock_text.replace('\n','')
- lock.write(lock_text)
- lock.close()
-
- def del_lock(self):
- os.remove(self.lock_file)
-
- def deefuzzer_stop(self):
- if len(self.deefuzzer_pid) != 0:
- os.system('kill -9 '+self.deefuzzer_pid[0])
-
- def rec_stop(self):
- if len(self.deefuzzer_pid) != 0:
- for port in self.deefuzzer_osc_ports:
- target = liblo.Address(int(port))
- liblo.send(target, '/record', 0)
-
- def mp3_convert(self):
- os.system('oggdec -o - '+ self.file_dir+os.sep+self.filename+' | lame -S -m m -h -b '+ self.bitrate + \
- ' --add-id3v2 --tt "'+ self.new_title + '" --ta "'+self.professor+'" --tl "'+self.title+'" --ty "'+self.date+ \
- '" --tg "'+self.genre+'" - ' + self.file_dir+os.sep+self.ServerDescription + '.mp3 &')
-
- def write_tags_ogg(self):
- file = self.file_dir + os.sep + self.filename
- if os.path.exists(file):
- audio = OggVorbis(file)
- audio['TITLE'] = self.new_title.decode('utf8')
- audio['ARTIST'] = self.professor.decode('utf8')
- audio['ALBUM'] = self.title.decode('utf8')
- audio['DATE'] = self.date.decode('utf8')
- audio['GENRE'] = self.genre.decode('utf8')
- audio['SOURCE'] = self.title.decode('utf8')
- audio['ENCODER'] = self.encoder.decode('utf8')
- audio['COMMENT'] = self.comment.decode('utf8')
- audio.save()
-
- def write_tags_mp3(self):
- file = self.file_dir + os.sep + self.filename
- if os.path.exists(file):
- os.system('mp3info -t "a" -a "a" '+file)
- audio = ID3(file)
- #tag = tags.__dict__['TITLE']
- audio.add(TIT2(encoding=3, text=self.new_title.decode('utf8')))
- #tag = tags.__dict__['ARTIST']
- audio.add(TP1(encoding=3, text=self.professor.decode('utf8')))
- #tag = tags.__dict__['ALBUM']
- audio.add(TAL(encoding=3, text=self.title.decode('utf8')))
- #tag = tags.__dict__['DATE']
- audio.add(TDRC(encoding=3, text=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
- #tag = tags.__dict__['GENRE']
- audio.add(TCO(encoding=3, text=self.genre.decode('utf8')))
- #tag = tags.__dict__['COMMENT']
- #audio.add(COM(encoding=3, text=self.comment))
- audio.save()
-
- def start(self):
- self.set_lock()
- self.deefuzzer_setup()
- self.deefuzzer_write_conf()
- self.deefuzzer_start()
-
- def stop(self):
- self.rec_stop()
- time.sleep(2)
- self.deefuzzer_stop()
- self.del_lock()
+++ /dev/null
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# *-* coding: utf-8 *-*
-"""
- telecaster
-
- Copyright (c) 2006-2010 Guillaume Pellerin <yomguy@parisson.org>
-
-# 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 <yomguy@parisson.com>
-"""
-
-version = '0.5.2'
-
-
-import os
-import sys
-import pwd
-import cgi
-import cgitb
-import time
-from tools import *
-from webview import *
-from station import *
-cgitb.enable()
-
-
-class TeleCaster:
- """Manage the calls of Station and Webview to get the network and
- disk streams"""
-
- def __init__(self, conf_file):
- """Main function"""
- self.conf_file = conf_file
- conf_dict = xml2dict(self.conf_file)
- self.conf = conf_dict['telecaster']
- self.title = self.conf['infos']['name']
- self.log_file = self.conf['log']
- self.logger = Logger(self.log_file)
- self.uid = os.getuid()
- self.url = self.conf['infos']['url']
- self.user = pwd.getpwuid(os.getuid())[0]
- self.user_dir = '/home' + os.sep + self.user + os.sep + '.telecaster'
- if not os.path.exists(self.user_dir):
- os.makedirs(self.user_dir)
- self.lock_file = self.user_dir + os.sep + 'telecaster.lock'
-
- def transition_head(self):
- html_file = open('telecaster_starting_head.html', 'r')
- html = html_file.read()
- html_file.close()
- return html
-
- def transition_foot(self):
- html_file = open('telecaster_starting_foot.html', 'r')
- html = html_file.read()
- html_file.close()
- return html
-
- def main(self):
- edcast_pid = get_pid('edcast_jack', self.uid)
- deefuzzer_pid = get_pid('/usr/bin/deefuzzer '+self.user_dir+os.sep+'deefuzzer.xml', self.uid)
- writing = edcast_pid != []
- casting = deefuzzer_pid != []
- form = WebView(self.conf, version)
-
- if deefuzzer_pid == [] and form.has_key("action") and \
- form.has_key("department") and form.has_key("conference") and \
- form.has_key("session") and form["action"].value == "start":
-
- self.conference_dict = {'title': '',
- 'department': '',
- 'conference': '',
- 'session': '',
- 'professor': '',
- 'comment': ''}
-
- for data in self.conference_dict:
- if not form.has_key(data):
- self.conference_dict[data] = 'Inconnu'
- else:
- value = form.getfirst(data)
- if '....' in value:
- self.conference_dict[data] = 'Inconnu'
- else:
- self.conference_dict[data] = value
-
- self.conference_dict['title'] = self.title
- s = Station(self.conf_file, self.conference_dict, self.lock_file)
- s.start()
- time.sleep(2)
- self.logger.write_info('starting')
- self.main()
-
- elif deefuzzer_pid != [] and os.path.exists(self.lock_file) and not form.has_key("action"):
- self.conference_dict = get_conference_from_lock(self.lock_file)
- form.stop_form(self.conference_dict, writing, casting)
- self.logger.write_info('started')
-
- elif deefuzzer_pid and form.has_key("action") and form["action"].value == "stop":
- self.logger.write_info('stopping')
- if os.path.exists(self.lock_file):
- self.conference_dict = get_conference_from_lock(self.lock_file)
- s = Station(self.conf_file, self.conference_dict, self.lock_file)
- s.stop()
- time.sleep(2)
- self.main()
-
- elif deefuzzer_pid == []:
- form.start_form(writing, casting)
- self.logger.write_info('stopped')
-
- elif deefuzzer_pid != []:
- os.system('kill -9 '+deefuzzer_pid[0])
- self.main()
-
-
-conf_file = '/etc/telecaster/telecaster.xml'
-
-if __name__ == '__main__':
- sys.stderr = sys.stdout
- t = TeleCaster(conf_file)
- t.main()
-
-
--- /dev/null
+#!/usr/bin/python
+# Capture 3 seconds of stereo audio from alsa_pcm:capture_1/2; then play it back.
+#
+# Copyright 2003, Andrew W. Schmeder
+# This source code is released under the terms of the GNU Public License.
+# See LICENSE for the full text of these terms.
+
+import Numeric
+import jack
+import time
+
+class JackInput:
+ "A JACK connexion input in TeleOddCast"
+
+ def __init__(self, dict):
+ self.host = dict['host']
+ self.name = dict['name']
+ self.jack = jack()
+ self.buffer_size = self.jack.get_buffer_size()
+ self.sample_rate = float(self.jack.get_sample_rate())
+ print "Buffer Size:", N, "Sample Rate:", Sr
+ self.power = True
+ self.capture = Numeric.zeros((2, self.buffer_size), 'f')
+
+
+ def attach(self):
+ jack.attach(self.name)
+
+ def get_ports(self):
+ return self.jack.get_ports()
+
+ def register_ports(self):
+ self.jack.register_port("in_1", self.jack.IsInput)
+ self.jack.register_port("in_2", self.jack.IsInput)
+
+ def activate(self):
+ self.jack.activate()
+
+ def stop(self):
+ self.power = False
+
+ def connect(self):
+ self.jack.connect("alsa_pcm:capture_1", self.name+":in_1")
+ self.jack.connect("alsa_pcm:capture_2", self.name+":in_2")
+ #jack.connect(self.name+":out_1", "alsa_pcm:playback_1")
+ #jack.connect(self.name+":out_2", "alsa_pcm:playback_2")
+
+ def process(self):
+ while True:
+ try:
+ if not self.power:
+ break
+ self.jack.process(__chunk, capture[:,self.buffer_size])
+ yield __chunk
+ except self.jack.InputSyncError:
+ print "Input Sync"
+ pass
+ except self.jack.OutputSyncError:
+ print "Output Sync"
+ pass
+
+ def desactivate(self):
+ self.jack.deactivate()
+
+ def detach(self):
+ self.jack.detach()
+++ /dev/null
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# *-* coding: utf-8 *-*
-"""
- telecaster
-
- Copyright (c) 2006-2010 Guillaume Pellerin <yomguy@parisson.org>
-
-# 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 <yomguy@parisson.com>
-"""
-
-import os
-import cgi
-import datetime
-import time
-import string
-from tools import *
-from cgi import FieldStorage
-
-class WebView(FieldStorage):
- """Gives the web CGI frontend"""
-
- def __init__(self, conf, version):
- FieldStorage.__init__(self)
- self.conf = conf
- self.version = version
- self.interfaces = ['eth0', 'eth1', 'eth2', 'eth0-eth2','eth3']
- ip = ''
- for interface in self.interfaces:
- try:
- ip = get_ip_address(interface)
- if ip:
- self.ip = ip
- break
- except:
- self.ip = 'localhost'
- if 'url' in self.conf['infos']:
- self.url = 'http://' + self.conf['infos']['url']
- else:
- self.url = 'http://' + self.ip
- self.rss_url = self.url+'/rss/telecaster.xml'
- self.port = '8000'
- self.acpi = acpi.Acpi()
- self.format = self.conf['media']['format']
- self.short_name = self.conf['infos']['short_name']
- self.title = self.conf['infos']['name'] + ' - ' + self.conf['infos']['description']
- self.departments = self.conf['department']
- self.professors = self.conf['professor']
- self.professors.sort()
- self.comments = self.conf['comment']
- self.len_departments = len(self.departments)
- self.len_professors = len(self.professors)
- self.conference_nb_max = 40
- self.professor_nb_max = 40
- self.refresh = False
- self.refresh_value = 20
- self.uid = os.getuid()
- self.casting = False
- self.writing = False
-
- def header(self):
- # Required header that tells the browser how to render the HTML.
- print "Content-Type: text/html\n\n"
- print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
- print "<HTML>"
- print "<HEAD>"
- print "<TITLE>TeleCaster - "+self.title+"</TITLE>"
- print "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">"
-
- print "<link href=\"/telecaster/css/telecaster.css\" rel=\"stylesheet\" type=\"text/css\">"
-
- def javascript(self):
- print '<script language="Javascript" type="text/javascript" >'
- print 'function choix(formulaire)'
- print '{var j; var i = formulaire.department.selectedIndex;'
- print 'if (i == 0)'
- print 'for(j = 1; j < '+ str(self.len_departments) + '; j++)'
- print 'formulaire.conference.options[j].text="";'
- print 'else{'
- print ' switch (i){'
- for k in range(0, self.len_departments):
- department = self.departments[k]
- conferences = department['conferences']
- conferences_t = dict2tuple(conferences)
- conferences = '"'+'","'.join(conferences_t)+'"'
- print ' case '+str(k+1)+' : var text = new Array('+conferences+'); '
- print ' break;'
- print ' }'
- print ' for(j = 0; j<'+str(self.conference_nb_max)+'; j++)'
- print ' formulaire.conference.options[j+1].text=text[j];'
- print '}'
- print ' formulaire.conference.selectedIndex=0;}'
- print '</script>'
-
- # rss ajax
- #print "<script type=\"text/javascript\" src=\"js/rssajax.js\"></script>"
- #print "<script type=\"text/javascript\">"
- #print " function rss_reload(url) {"
- #print " getRSS(url)"
- #print " setTimeout(\"rss_reload(\'\" + url + \"\')\", 10000);}"
- #print "</script>"
-
- def sub_header(self):
- if self.refresh:
- print "<meta http-equiv=\"refresh\" content=\"" + str(self.refresh_value) + "; URL=telecaster.py\">"
- print "</HEAD>\n"
- #print "<BODY bgcolor =\"#ffffff\" onload=\"rss_reload(\'" + self.rss_url + "\');\">"
- print "<BODY>"
- print "<div class=\"bg\">"
- print "<div class=\"header\">"
- print "<img src=\"img/logo_telecaster_wh.png\" alt=\"logo_telecaster\">"
- print "<div class=\"title_main\"> TeleCaster - Audio Web Live Recording</div>"
- print "</div>"
-
- def colophon(self):
- date = datetime.datetime.now().strftime("%Y")
- print "<div class=\"colophon\">"
- print "TeleCaster "+self.version+" © <span>"+date+"</span> <a href=\"http://parisson.com\">Parisson SARL</a>. Tous droits réservés."
- print "</div>"
-
- def footer(self):
- print "</div>"
- print "</BODY>"
- print "</HTML>"
-
- def hardware_data(self):
- jackd_pid = get_pid('jackd', self.uid)
- if jackd_pid == []:
- jackd_pid = get_pid('jackdbus', self.uid)
- self.acpi.update()
- self.power_state = self.acpi.charging_state()
- if self.power_state == 0:
- power_info = "<span style=\"color: red\">batterie</span>"
- elif self.power_state == 1 or self.power_state == 2:
- power_info = "<span style=\"color: green\">secteur</span>"
- else:
- power_info = ""
-
- if self.acpi.percent() == 127:
- batt_charge = '<span style=\"color: green\">100 %</span>'
- else:
- percent = self.acpi.percent()
- if percent < 10:
- batt_charge = '<span style=\"color: red\"><b>'+str(percent)+' %</b></span>'
- else:
- batt_charge = '<span style=\"color: green\">'+str(percent)+' %</span>'
-
- if self.ip == 'localhost':
- ip_info = '<span style=\"color: red\"><b>'+self.ip+'</b></span>'
- else:
- ip_info = '<span style=\"color: green\">'+self.ip+'</span>'
-
- if jackd_pid == []:
- jackd_info = '<span style=\"color: red\"><b>OFF</b></span>'
- else:
- jackd_info = '<span style=\"color: green\">On</span>'
-
- if self.casting:
- casting = '<span style=\"color: green\">On</span>'
- else:
- casting = '<span style=\"color: red\"><b>OFF</b></span>'
-
- print "<div class=\"hardware\">"
- print "<div class=\"title\">Status</div>"
- print "<table class=\"hardware\">"
- print "<tr><td>Name</td><TD> : </TD>"
- print "<td>%s</td></tr>" % self.conf['infos']['url']
- print "<tr><td>IP address</td><TD> : </TD>"
- print "<td>%s</td></tr>" % ip_info
- print "<tr><td>Power</td><TD> : </TD>"
- print "<td>%s</td></tr>" % power_info
- print "<tr><td>Battery charge</td><TD> : </TD>"
- print "<td>%s</td></tr>" % batt_charge
- try:
- print "<tr><td>Temp core 1</td><TD> : </TD><td>%s</td></tr>" % self.acpi.temperature(0)
- except:
- pass
- try:
- print "<tr><td>Temp core 2</td><TD> : </TD><td>%s</td></tr>" % self.acpi.temperature(1)
- except:
- pass
- print "<tr><td>JACK audio server</td><TD> : </TD>"
- print "<td>%s</td></tr>" % jackd_info
- print "<tr><td>Encoder</td><TD> : </TD>"
- print "<td>%s</td></tr>" % casting
- print "<tr><td><div class=\"buttons\">"
- if self.writing:
- print "<button class=\"positive\"><img src=\"img/drive_add.png\" alt=\"\">Recording...</button>"
- else:
- print "<button class=\"negative\"><img src=\"img/drive_error.png\" alt=\"\">NOT Recording !</button>"
- print "</div></td>"
- print "<td></td>"
- print "<td><div class=\"buttons\">"
- if not self.ip == 'localhost' and self.writing:
- print "<button class=\"positive\"><img src=\"img/transmit_add.png\" alt=\"\">Broadcasting...</button>"
- else:
- print "<button class=\"negative\"><img src=\"img/transmit_error.png\" alt=\"\">NOT Broadcasting !</button>"
- print "</div>"
- print "</td></tr>"
- print "</table>"
- print "</div>"
-
-
- def start_form(self, writing, casting, message=''):
- self.casting = writing
- self.writing = casting
- self.refresh = False
- self.mount_point = 'telecaster_live.' + self.format
- self.header()
- self.javascript()
- self.sub_header()
- self.hardware_data()
-
- print "<form method=\"post\" action=\"telecaster.py\" name=\"formulaire\">"
- print "<div class=\"main\">"
- print "<table class=\"form\">"
- print "<TR><TH align=\"left\">Titre</TH><TD> : </TD><TD>"+self.title+"</TD></TR>"
- print "<TR><TH align=\"left\">Département</TH><TD> : </TD>"
- print "<TD><select name=\"department\" onChange=\"choix(this.form)\">"
- print "<option>...........Choisissez un département...........</option>"
- for department in self.departments:
- print "<option value=\""+department['name']+"\">"+department['name']+"</option>"
- print "</select></TD></TR>"
- print "<TR><TH align=\"left\">Conférence</TH><TD> : </TD>"
- print "<TD><select name=\"conference\">"
- print "<option>...........Choisissez une conférence...........</option>"
- for i in range(1,self.conference_nb_max):
- print "<option></option>"
- print "</select></TD></TR>"
- print "<TR><TH align=\"left\">Session</TH><TD> : </TD><TD><select name=\"session\">"
- for i in range(1,21):
- print "<option value=\""+str(i)+"\">"+str(i)+"</option>"
- print "</select></TD></TR>"
- print "<TR><TH align=\"left\">Professeur</TH><TD> : </TD>"
- print "<TD><INPUT type = text name = \"professor\"></TD></TR>"
- print "<TR><TH align=\"left\">Commentaire</TH><TD> : </TD>"
- print "<TD><INPUT type = text name = \"comment\"></TD></TR>"
- print "</table>"
- print "</div>"
- print "<div class=\"tools\">"
- print "<div class=\"buttons\">"
- print "<button type=\"submit\" class=\"positive\"><img src=\"img/arrow_refresh.png\" alt=\"\">Refresh</button>"
- print "<button type=\"submit\" name=\"action\" value=\"start\" class=\"negative\"><img src=\"img/stop.png\" alt=\"\">Record</button>"
- print "<a href=\"http://"+self.ip+":"+self.port+"/"+self.mount_point+"\"><img src=\"img/control_play_blue.png\" alt=\"\">Play Live</a>"
- print "<a href=\"/archives/\"><img src=\"img/folder_go.png\" alt=\"\">Archives</a>"
- print "<a href=\"/trash/\"><img src=\"img/bin.png\" alt=\"\">Trash</a>"
- print "</div>"
- print "</div>"
- print "</form>"
- self.colophon()
- self.footer()
-
- def stop_form(self, conference_dict, writing, casting):
- department = conference_dict['department']
- conference = conference_dict['conference']
- session = conference_dict['session']
- professor = conference_dict['professor']
- comment = conference_dict['comment']
- self.mount_point ='_-_'.join([self.short_name,department,conference])+'.'+self.format
- self.writing = writing
- self.casting = casting
- self.refresh = True
-
- self.header()
- self.sub_header()
- self.hardware_data()
-
- print "<form method=\"post\" action=\"telecaster.py\">"
- print "<div class=\"main\">"
- print "<table class=\"form\">"
- print "<TR><TH align=\"left\">Titre</TH><TD> : </TD><TD>"+self.title+"</TD></TR>"
- print "<TR><TH align=\"left\">Département</TH><TD> : </TD><TD>"+department+"</TD></TR>"
- print "<TR><TH align=\"left\">Conference</TH><TD> : </TD><TD>"+conference+"</TD></TR>"
- print "<TR><TH align=\"left\">Session</TH><TD> : </TD><TD>"+session+"</TD></TR>"
- print "<TR><TH align=\"left\">Professeur</TH><TD> : </TD><TD>"+professor+"</TD></TR>"
- print "<TR><TH align=\"left\">Commentaire</TH><TD> : </TD><TD>"+comment+"</TD></TR>"
- print "</table>"
- print "</div>"
- print "<div class=\"tools\">"
- print "<div class=\"buttons\">"
- print "<button type=\"submit\"><img src=\"img/arrow_refresh.png\" alt=\"\">Refresh</button>"
- print "<a href=\"http://"+self.ip+":"+self.port+"/"+self.mount_point+"\"><img src=\"img/control_play_blue.png\" alt=\"\">Play</a>"
- print "<button type=\"submit\" name=\"action\" value=\"stop\" class=\"negative\"><img src=\"img/cancel.png\" alt=\"\">Stop</button>"
- print "<a href=\"/archives/\"><img src=\"img/folder_go.png\" alt=\"\">Archives</a>"
- print "<a href=\"/trash/\"><img src=\"img/bin.png\" alt=\"\">Trash</a>"
- print "</div>"
- print "</div>"
- print "</form>"
-
- self.colophon()
- self.footer()
-