]> git.parisson.com Git - telecaster-cgi.git/commitdiff
* Change package name to telecaster
authoryomguy <yomguy@parisson.com>
Mon, 23 Jun 2008 14:53:48 +0000 (14:53 +0000)
committeryomguy <yomguy@parisson.com>
Mon, 23 Jun 2008 14:53:48 +0000 (14:53 +0000)
* Detect last osscastv3 process with pgrep

etc/pre-barreau_conferences.xml
etc/telecaster.cfg [new file with mode: 0644]
etc/telecaster.xml [new file with mode: 0644]
etc/teleoddcast.cfg
telecaster.py [new file with mode: 0755]
teleoddcast.py
tools.py

index f0b17acd5a7ab3374ba03969a2ed9b2f09734b06..d6116ddae1c0525f6209d1c539b3c6a6acc6e4a6 100644 (file)
-<teleoddcast>
-    <url>http://augustins.pre-barreau.com</url>
-    <title>Augustins - Pré-Barreau</title>
+<telecaster>
+    <url>http://localhost</url>
+    <title>PRE-BARREAU - CRPDA ETE 2008 - ICP</title>
     <port>8000</port>
-    
+
     <department>
         <name>CRFPA</name>
         <conferences>
-            <conference>
-                <name>Liberté publiques - Cours</name>
-            </conference>
-            <conference>
-                <name>Note de synthèse - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit civil - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit civil - Corrections</name>
-            </conference>
-            <conference>
-                <name>Procédure administrative et contentieuse - Cours</name>
-            </conference>
-            <conference>
-                <name>Procédure administrative et contentieuse - Corrections</name>
-            </conference>
-            <conference>
-                <name>Procédure civile - Cours</name>
-            </conference>
-            <conference>
-                <name>Procédure civile - Corrections</name>
-            </conference>
-            <conference>
-                <name>Procédure pénale - Cours</name>
-            </conference>
-            <conference>
-                <name>Procédure pénale - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit commercial des affaires - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit commercial des affaires - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit communautaire et européen - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit communautaire et européen - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit fiscal - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit fiscal - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit pénal - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit pénal - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit de la famille et des personnes - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit de la famille et des personnes - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit patrimonial - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit patrimonial - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit du travail - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit du travail - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit administratif - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit administratif - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit public des activités économiques - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit public des activités économiques - Corrections</name>
-            </conference>
-            <conference>
-                <name>Droit international privé - Cours</name>
-            </conference>
-            <conference>
-                <name>Droit international privé - Corrections</name>
-            </conference>
-            <conference>
-                <name>Procédures collectives et sûretés - Cours</name>
-            </conference>
-            <conference>
-                <name>Procédures collectives et sûretés - Corrections</name>
-            </conference>
-            <conference>
-                <name>Procédure communautaire - Cours</name>
-            </conference>
-            <conference>
-                <name>Procédures civile d'exécution - Cours</name>
-            </conference>
-            <conference>
-                <name>Comptabilité privée - Cours</name>
-            </conference>
-            <conference>
-                <name>Finances publiques - Cours</name>
-            </conference>
-            <conference>
-                <name>REUNION</name>
-            </conference>
-            <conference>
-                <name>TEST</name>
-            </conference>
+          <conference>
+            <name>Droit_administratif_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_administratif_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_civil_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_civil_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_commercial_des_affaires_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_commercial_des_affaires_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_communautaire_et_europeen_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_communautaire_et_europeen_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_de_la_famille_et_des_personnes_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_de_la_famille_et_des_personnes_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_du_travail_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_du_travail_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_fiscal_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_fiscal_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_international_prive_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_international_prive_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_patrimonial_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_patrimonial_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_public_des_activites_economiques_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_public_des_activites_economiques_Cours</name>
+          </conference>
+          <conference>
+            <name>Droit_penal_Corrections</name>
+          </conference>
+          <conference>
+            <name>Droit_penal_Cours</name>
+          </conference>
+          <conference>
+            <name>Libertes_publiques_Cours</name>
+          </conference>
+          <conference>
+            <name>Note_de_synthese_Corrections</name>
+          </conference>
+          <conference>
+            <name>Procedure_administrative_et_contentieuse_Corrections</name>
+          </conference>
+          <conference>
+            <name>Procedure_administrative_et_contentieuse_Cours</name>
+          </conference>
+          <conference>
+            <name>Procedure_civile_Corrections</name>
+          </conference>
+          <conference>
+            <name>Procedure_civile_Cours</name>
+          </conference>
+          <conference>
+            <name>Procedures_collectives_et_suretes_Corrections</name>
+          </conference>
+          <conference>
+            <name>Procedures_collectives_et_suretes_Cours</name>
+          </conference>
+          <conference>
+            <name>Procedure_penale_Corrections</name>
+          </conference>
+          <conference>
+            <name>Procedure_penale_Cours</name>
+          </conference>
+          <conference>
+            <name>TEST</name>
+          </conference>
+
         </conferences>
     </department>
 
         </conferences>
     </department>
 
-</teleoddcast>
+</telecaster>
+
diff --git a/etc/telecaster.cfg b/etc/telecaster.cfg
new file mode 100644 (file)
index 0000000..4a10aca
--- /dev/null
@@ -0,0 +1,24 @@
+Server=localhost
+Port=8000
+ServerPassword=source2parisson
+ServerMountpoint=/Default_School_-_CRFPA_-_Droit_fiscal_Cours.ogg
+ServerPublic=0
+AutomaticReconnectSecs=10
+Encode=OggVorbis
+BitrateNominal=512
+NumberChannels=1
+OggQuality=1
+Samplerate=44100
+ServerType=Icecast2
+ExternalFile=/tmp
+#YP Settings
+ServerStreamURL=http://www.pre-barreau.com
+ServerName=Default_School_-_CRFPA_-_Droit_fiscal_Cours
+ServerDescription=Default_School_-_CRFPA_-_Droit_fiscal_Cours_-_1_-_azd_-_azd
+ServerGenre=Teaching
+#Advanced Settings
+LogLevel=1
+LogFile=oddcastv3.log
+SaveAsWAV=0
+OutputControl=SERVER,GENERAL,OUTPUT
+
diff --git a/etc/telecaster.xml b/etc/telecaster.xml
new file mode 100644 (file)
index 0000000..d6beea5
--- /dev/null
@@ -0,0 +1,28 @@
+<telecaster>
+    <infos>
+        <short_name>Default School</short_name>
+        <name>Default School</name>
+        <description>A school where you can learn</description>
+        <url>http://mydomain.com</url>
+        <genre>Other</genre>
+        <channels>1</channels>
+    </infos>
+    <server>
+        <host>localhost</host>
+        <port>8000</port>
+        <sourcepassword>source2parisson</sourcepassword>
+        <public>1</public>
+        <root_dir>/var/www/telecaster/</root_dir>
+        <odd_conf_file>etc/telecaster.cfg</odd_conf_file>
+        <lock_file>lock/telecaster.lock</lock_file>
+    </server>
+    <media>
+        <dir>/tmp/media</dir>
+        <format>ogg</format>
+        <bitrate>64</bitrate>
+        <ogg_quality>2</ogg_quality>
+        <samplerate>44100</samplerate>
+        <voices>2</voices>
+        <shuffle>1</shuffle>
+    </media>
+</telecaster>
index 2da2913cd1b944004204e617c4bb57cc3a4a9ce7..0565821814491400850ac60d2817f8920c469cd6 100644 (file)
@@ -1,7 +1,7 @@
 Server=localhost
 Port=8000
 ServerPassword=source2parisson
-ServerMountpoint=/Default_School_-_Computer_-_Conference_2.ogg
+ServerMountpoint=/Default_School_-_AE_-_Administratif_Cours.ogg
 ServerPublic=0
 AutomaticReconnectSecs=10
 Encode=OggVorbis
@@ -13,8 +13,8 @@ ServerType=Icecast2
 ExternalFile=/tmp
 #YP Settings
 ServerStreamURL=http://www.pre-barreau.com
-ServerName=Default_School_-_Computer_-_Conference_2
-ServerDescription=Default_School_-_Computer_-_Conference_2_-_2_-_azfpoh_-_opij
+ServerName=Default_School_-_AE_-_Administratif_Cours
+ServerDescription=Default_School_-_AE_-_Administratif_Cours_-_1_-_jf_-_jyf
 ServerGenre=Teaching
 #Advanced Settings
 LogLevel=1
diff --git a/telecaster.py b/telecaster.py
new file mode 100755 (executable)
index 0000000..ccc123a
--- /dev/null
@@ -0,0 +1,435 @@
+#!/usr/bin/python
+# *-* coding: utf-8 *-*
+"""
+   telecaster
+
+   Copyright (c) 2006-2007 Guillaume Pellerin <yomguy@altern.org>
+
+   This program 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.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+version = '0.3.2'
+
+import os
+import cgi
+import shutil
+import datetime
+import time
+import codecs
+import string
+import signal
+from tools import *
+from mutagen.oggvorbis import OggVorbis
+
+
+class Conference:
+    """A conference object including metadata"""
+    
+    def __init__(self, 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.conf = xml2dict(conf_file)
+        self.conf = self.conf['telecaster']
+        self.root_dir = self.conf['server']['root_dir']
+        self.media_dir = self.conf['media']['dir']
+        self.host = self.conf['server']['host']
+        self.port = self.conf['server']['port']
+        self.password = self.conf['server']['sourcepassword']
+        self.url = 'http://'+self.host+':'+self.port
+        self.odd_conf_file = self.conf['server']['odd_conf_file']
+        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 = '/' + clean_string(self.title) + '_-_' + \
+                                 clean_string(self.department) + '_-_' + \
+                                 clean_string(self.conference)+'.ogg'
+        self.lock_file = self.root_dir + os.sep + self.conf['server']['lock_file']
+        self.filename = self.ServerDescription + '.ogg'
+        self.output_dir = self.media_dir + os.sep + self.department + os.sep + self.date
+        self.file_dir = self.output_dir + os.sep + self.ServerName
+        self.uid = os.getuid()
+        self.odd_pid = get_pid('^oddcastv3 -n [^LIVE]', self.uid)
+        self.rip_pid = get_pid('streamripper ' + self.url + self.mount_point, self.uid)
+        self.bitrate = '64'
+        self.new_title = clean_string('_-_'.join(self.server_name)+'_-_'+self.professor+'_-_'+self.comment)
+        self.genre = 'Vocal'
+        self.encoder = 'TeleCaster by Parisson'
+
+    def set_oddcast_conf(self):
+        oddconf = open(self.odd_conf_file,'r')
+        lines = oddconf.readlines()
+        oddconf.close()
+        newlines = []
+        for line in lines:
+            if 'ServerDescription' in line.split('='):
+                newlines.append('ServerDescription=' + \
+                                self.ServerDescription.replace(' ','_') + '\n')
+                
+            elif 'ServerName' in line.split('='):
+                newlines.append('ServerName=' + self.ServerName + '\n')
+
+            elif 'ServerMountpoint' in line.split('='):
+                newlines.append('ServerMountpoint=' + self.mount_point + '\n')
+
+            elif 'ServerPassword' in line.split('='):
+                newlines.append('ServerPassword=' + self.password + '\n')
+                
+            else:
+                newlines.append(line)
+                
+        oddconf = open(self.odd_conf_file,'w')
+        oddconf.writelines(newlines)
+        oddconf.close()
+
+    def start_oddcast(self):
+        command = 'oddcastv3 -n "'+clean_string(self.conference)[0:16]+'" -c '+self.odd_conf_file+ \
+                  ' alsa_pcm:capture_1 alsa_pcm:capture_2 > /dev/null &'
+        os.system(command)
+        self.set_lock()
+        time.sleep(1)
+
+    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 start_rip(self):
+        if not os.path.exists(self.output_dir):
+            os.makedirs(self.output_dir)
+        command = 'streamripper ' + self.url + self.mount_point + \
+                  ' -d '+self.output_dir+' -D "%S" -s -t --quiet > /dev/null &'
+        os.system(command)
+
+    def stop_oddcast(self):
+        if len(self.odd_pid) != 0:
+            os.system('kill -9 '+self.odd_pid[0])
+        
+    def stop_rip(self):
+        if len(self.rip_pid) != 0:
+            os.system('kill -9 ' + self.rip_pid[0])
+        time.sleep(1)
+        date = datetime.datetime.now().strftime("%Y")
+        if os.path.exists(self.file_dir) and os.path.exists(self.file_dir + os.sep + 'incomplete'):
+            shutil.move(self.file_dir+os.sep+'incomplete'+os.sep+' - .ogg', self.file_dir+os.sep)
+            shutil.rmtree(self.file_dir+os.sep+'incomplete'+os.sep)
+            os.rename(self.file_dir+os.sep+' - .ogg', self.file_dir+os.sep+self.filename)
+
+    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(self):
+       file = self.file_dir + os.sep + self.filename
+       if os.path.exists(file):
+            audio = OggVorbis(file)
+            audio['TITLE'] = self.new_title
+            audio['ARTIST'] = self.professor
+            audio['ALBUM'] = self.title
+            audio['DATE'] = self.date
+            audio['GENRE'] = self.genre
+            audio['SOURCE'] = self.title
+            audio['ENCODER'] = self.encoder
+            audio['COMMENT'] = self.comment
+            audio.save()
+
+    def start(self):
+        self.set_lock()
+        self.set_oddcast_conf()
+        self.start_oddcast()
+        self.start_rip()
+
+    def stop(self):
+        self.stop_rip()
+        self.write_tags()
+        self.stop_oddcast()
+        self.del_lock()
+        self.encode_mp3()
+
+    def encode_mp3(self):
+        self.mp3_convert()
+
+    def start_mp3cast(self):        
+        item_id = item_id
+        source = source
+        metadata = metadata
+        args = get_args(options)
+        ext = get_file_extension()
+        args = ' '.join(args)
+        command = 'sox "%s" -q -w -r 44100 -t wav -c2 - | lame %s -' \
+                       % (source, args)
+        # Processing (streaming + cache writing)
+        stream = self.core_process(self.command,self.buffer_size,self.dest)
+        for chunk in stream:
+            yield chunk
+    
+    def core_process(self, command, buffer_size, dest):
+        """Encode and stream audio data through a generator"""     
+        __chunk = 0
+        file_out = open(dest,'w')
+        try:
+            proc = subprocess.Popen(command,
+                    shell = True,
+                    bufsize = buffer_size,
+                    stdin = subprocess.PIPE,
+                    stdout = subprocess.PIPE,
+                    close_fds = True)
+        except:
+            raise ExportProcessError('Command failure:', command, proc)
+        # Core processing
+        while True:
+            __chunk = proc.stdout.read(buffer_size)
+            status = proc.poll()
+            if status != None and status != 0:
+                raise ExportProcessError('Command failure:', command, proc)
+            if len(__chunk) == 0:
+                break
+            yield __chunk
+            file_out.write(__chunk)
+        file_out.close()
+
+
+class WebView:
+    """Gives the web CGI frontend"""
+    
+    def __init__(self, school_file):
+        self.conf = xml2dict(school_file)
+        self.conf = self.conf['telecaster']
+        self.url = self.conf['url']
+        self.port = self.conf['port']
+        self.title = self.conf['title']
+        self.departments = self.conf['department']
+        #print self.departments
+        #self.conferences = self.conf['department']['conferences']
+        self.len_departments = len(self.departments)
+        self.conference_nb_max = 40
+
+    def header(self):
+        # Required header that tells the browser how to render the HTML.
+        print "Content-Type: text/html\n"
+        print "<HTML>"
+        print "<HEAD>"
+        print "<TITLE>TeleCaster - "+self.title+"</TITLE>"
+        print "<link href=\"css/telecaster.css\" rel=\"stylesheet\" type=\"text/css\">"
+        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']
+            #print conferences
+            conferences_t = dict2tuple(conferences)
+            #print 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 '       formulaire.conference.options[j+1].value=text[j];'
+        print '}'
+        print '      formulaire.conference.selectedIndex=0;}'
+        print '</script>'
+        print "</HEAD>"
+        
+        print "<BODY BGCOLOR =\"#FFFFFF\">"
+        print "<div id=\"bg\">"
+        print "<div id=\"header\">"
+        print "<H3>&nbsp;TeleCaster - L'enregistrement et la diffusion audio en direct par internet</H3>"
+        print "</div>"
+
+    def colophon(self):
+        date = datetime.datetime.now().strftime("%Y")
+        print "<div id=\"colophon\">"
+        print "TeleCaster "+version+" &copy; <span>"+date+"</span>&nbsp;<a href=\"http://parisson.com\">Parisson</a>. Tous droits r&eacute;serv&eacute;s."
+        print "</div>"
+            
+    def footer(self):
+        print "</div>"
+        print "</BODY>"
+        print "</HTML>"
+
+    def start_form(self, message=''):
+        self.header()
+        print "<div id=\"main\">"
+        print "<h5><span style=\"color: red\">"+message+"</span></h5>"
+        print "<h5><span style=\"color: red\">Attention, il est important de remplir tous les champs, y compris le commentaire !</span></h5>"
+        print "<TABLE BORDER = 0>"
+        print "<form method=post action=\"telecaster.py\" name=\"formulaire\">"
+        print "<TR><TH align=\"left\">Titre :</TH><TD>"+self.title+"</TD></TR>"
+        print "<TR><TH align=\"left\">D&eacute;partement :</TH>"
+        print "<TD><select name=\"department\" onChange=\"choix(this.form)\">"
+        print "<option selected>...........Choisissez un d&eacute;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&eacute;rence :</TH>"
+        print "<TD><select name=\"conference\">"
+        print "<option selected>...........Choisissez une conf&eacute;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><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><INPUT type = text name = \"professor\"></TD><TR>"
+        print "<TR><TH align=\"left\">Commentaire :</TH><TD><INPUT type = text name = \"comment\"></TD></TR>"
+        print "</TABLE>"
+        print "<h5><a href=\""+self.url+":"+self.port+"/augustins.pre-barreau.com_live.ogg.m3u\">Cliquez ici pour &eacute;couter le flux continu 24/24 en direct</a></h5>"
+        print "</div>"
+        print "<div id=\"tools\">"
+        print "<INPUT TYPE = hidden NAME = \"action\" VALUE = \"start\">"
+        print "<INPUT TYPE = submit VALUE = \"Enregistrer\">"
+        print "</FORM>"
+        print "</div>"
+        self.colophon()
+        self.footer()
+
+    def encode_form(self, message=''):
+        self.header()
+        print "<div id=\"main\">"
+        print "<h5><span style=\"color: red\">"+message+"</span></h5>"
+        print "<h5><span style=\"color: red\">ENCODAGE EN COURS !</span></h5>"
+        print "</div>"
+        self.colophon()
+        self.footer()
+
+    def stop_form(self, conference_dict):
+        """Stop page"""
+        department = conference_dict['department']
+        conference = conference_dict['conference']
+        session = conference_dict['session']
+        professor = conference_dict['professor']
+        comment = conference_dict['comment']
+
+        self.header()
+        print "<div id=\"main\">"
+        print "<h4><span style=\"color: red\">Cette formation est en cours de diffusion :</span></h4>"
+        print "<hr>"
+        print "<TABLE BORDER = 0>"
+        print "<FORM METHOD = post ACTION = \"telecaster.py\">"
+        print "<TR><TH align=\"left\">Titre :</TH><TD>"+self.title+"</TD></TR>"
+        print "<TR><TH align=\"left\">D&eacute;partement :</TH><TD>"+department+"</TD><TR>"
+        print "<TR><TH align=\"left\">Conference :</TH><TD>"+conference+"</TD><TR>"
+        print "<TR><TH align=\"left\">Session :</TH><TD>"+session+"</TD><TR>"
+        print "<TR><TH align=\"left\">Professeur :</TH><TD>"+professor+"</TD><TR>"
+        print "<TR><TH align=\"left\">Commentaire :</TH><TD>"+comment+"</TD><TR>"
+        print "</TABLE>"
+        print "<hr>"
+        print "<h5><a href=\""+self.url+":"+self.port+"/"+clean_string(self.title) + \
+              "_-_"+clean_string(department)+"_-_"+clean_string(conference) + \
+              ".ogg.m3u\">Cliquez ici pour &eacute;couter cette formation en direct</a></h5>"
+        print "</div>"
+        print "<div id=\"tools\">"
+        print "<INPUT TYPE = hidden NAME = \"action\" VALUE = \"stop\">"
+        print "<INPUT TYPE = submit VALUE = \"STOP\">"
+        print "</FORM>"
+        print "</div>"
+        self.colophon()
+        self.footer()
+
+
+class TeleCaster:
+    """Manage the calls of Station and Webview to get the network and
+    disk streams"""
+
+    def __init__(self, conf_file, school_file):
+        """Main function"""
+        self.conf_file = conf_file
+        self.school_file = school_file
+        conf_t = xml2dict(self.conf_file)
+        self.conf = conf_t['telecaster']
+        self.title = self.conf['infos']['name']
+        self.root_dir = self.conf['server']['root_dir']
+        self.lock_file = self.root_dir + os.sep + self.conf['server']['lock_file']
+        self.odd_conf_file = self.conf['server']['lock_file']
+        self.title = self.conf['infos']['name']
+        self.uid = os.getuid()
+        self.odd_pid = get_pid('^oddcastv3 -n [^LIVE]', self.uid)
+
+    def main(self):
+        w = WebView(self.school_file)
+        form = cgi.FieldStorage()
+        
+        if self.odd_pid == [] and form.has_key("action") and \
+            form.has_key("department") and form.has_key("conference") and \
+            form.has_key("professor") and form.has_key("comment") and \
+            form["action"].value == "start":
+            
+            self.conference_dict = {'title': self.title,
+                        'department': form["department"].value,
+                        'conference': form["conference"].value,
+                        'session': form["session"].value,
+                        'professor': form["professor"].value,
+                        'comment': form["comment"].value}
+
+            s = Station(self.conf_file, self.conference_dict, self.lock_file)
+            s.start()
+            w.stop_form(self.conference_dict)
+            
+        elif self.odd_pid != [] and os.path.exists(self.lock_file) and not form.has_key("action"):
+            self.conference_dict = get_conference_from_lock(self.lock_file)
+            w.stop_form(self.conference_dict)
+
+        elif self.odd_pid != [] and form.has_key("action") and form["action"].value == "stop":
+            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()
+            #w.encode_form()
+            #s.encode_mp3()
+            #w.start_form('Please wait : encoding file to MP3...')
+            w.start_form()
+
+        elif self.odd_pid == []:
+            w.start_form()
+
+
+# Call main function.
+conf_file = 'etc/telecaster.xml'
+school_file = 'etc/pre-barreau_conferences.xml'
+
+if __name__ == '__main__':
+    t = TeleCaster(conf_file, school_file)
+    t.main()
+
index 65071062a35406efeb34ada174723ae306093652..a8202d331021995aea4f8412f8424dcfc7654205 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 # *-* coding: utf-8 *-*
 """
-   teleoddcast
+   telecaster
 
    Copyright (c) 2006-2007 Guillaume Pellerin <yomguy@altern.org>
 
@@ -29,6 +29,7 @@ import datetime
 import time
 import codecs
 import string
+import signal
 from tools import *
 from mutagen.oggvorbis import OggVorbis
 
@@ -56,7 +57,7 @@ class Station(Conference):
         Conference.__init__(self, conference_dict)
         self.date = datetime.datetime.now().strftime("%Y")
         self.conf = xml2dict(conf_file)
-        self.conf = self.conf['teleoddcast']
+        self.conf = self.conf['telecaster']
         self.root_dir = self.conf['server']['root_dir']
         self.media_dir = self.conf['media']['dir']
         self.host = self.conf['server']['host']
@@ -76,12 +77,12 @@ class Station(Conference):
         self.output_dir = self.media_dir + os.sep + self.department + os.sep + self.date
         self.file_dir = self.output_dir + os.sep + self.ServerName
         self.uid = os.getuid()
-        self.odd_pid = get_pid('^oddcastv3 -n [^LIVE]', self.uid)
+        self.odd_pid = get_pid('oddcastv3 -n', self.uid)
         self.rip_pid = get_pid('streamripper ' + self.url + self.mount_point, self.uid)
         self.bitrate = '64'
         self.new_title = clean_string('_-_'.join(self.server_name)+'_-_'+self.professor+'_-_'+self.comment)
         self.genre = 'Vocal'
-        self.encoder = 'TeleOddCast by Parisson'
+        self.encoder = 'TeleCaster by Parisson'
 
     def set_oddcast_conf(self):
         oddconf = open(self.odd_conf_file,'r')
@@ -135,7 +136,7 @@ class Station(Conference):
 
     def stop_oddcast(self):
         if len(self.odd_pid) != 0:
-            os.system('kill -9 ' + self.odd_pid[0])
+            os.system('kill -9 '+self.odd_pid[0])
         
     def stop_rip(self):
         if len(self.rip_pid) != 0:
@@ -224,7 +225,7 @@ class WebView:
     
     def __init__(self, school_file):
         self.conf = xml2dict(school_file)
-        self.conf = self.conf['teleoddcast']
+        self.conf = self.conf['telecaster']
         self.url = self.conf['url']
         self.port = self.conf['port']
         self.title = self.conf['title']
@@ -239,8 +240,8 @@ class WebView:
         print "Content-Type: text/html\n"
         print "<HTML>"
         print "<HEAD>"
-        print "<TITLE>TeleOddCast - "+self.title+"</TITLE>"
-        print "<link href=\"css/teleoddcast.css\" rel=\"stylesheet\" type=\"text/css\">"
+        print "<TITLE>TeleCaster - "+self.title+"</TITLE>"
+        print "<link href=\"css/telecaster.css\" rel=\"stylesheet\" type=\"text/css\">"
         print '<script language="Javascript" type="text/javascript" >'
         print 'function choix(formulaire)'
         print '{var j; var i = formulaire.department.selectedIndex;'
@@ -270,13 +271,13 @@ class WebView:
         print "<BODY BGCOLOR =\"#FFFFFF\">"
         print "<div id=\"bg\">"
         print "<div id=\"header\">"
-        print "<H3>&nbsp;TeleOddCast - L'enregistrement et la diffusion audio en direct par internet</H3>"
+        print "<H3>&nbsp;TeleCaster - L'enregistrement et la diffusion audio en direct par internet</H3>"
         print "</div>"
 
     def colophon(self):
         date = datetime.datetime.now().strftime("%Y")
         print "<div id=\"colophon\">"
-        print "TeleOddCast "+version+" &copy; <span>"+date+"</span>&nbsp;<a href=\"http://parisson.com\">Parisson</a>. Tous droits r&eacute;serv&eacute;s."
+        print "TeleCaster "+version+" &copy; <span>"+date+"</span>&nbsp;<a href=\"http://parisson.com\">Parisson</a>. Tous droits r&eacute;serv&eacute;s."
         print "</div>"
             
     def footer(self):
@@ -284,12 +285,13 @@ class WebView:
         print "</BODY>"
         print "</HTML>"
 
-    def start_form(self):
+    def start_form(self, message=''):
         self.header()
         print "<div id=\"main\">"
+        print "<h5><span style=\"color: red\">"+message+"</span></h5>"
         print "<h5><span style=\"color: red\">Attention, il est important de remplir tous les champs, y compris le commentaire !</span></h5>"
         print "<TABLE BORDER = 0>"
-        print "<form method=post action=\"teleoddcast.py\" name=\"formulaire\">"
+        print "<form method=post action=\"telecaster.py\" name=\"formulaire\">"
         print "<TR><TH align=\"left\">Titre :</TH><TD>"+self.title+"</TD></TR>"
         print "<TR><TH align=\"left\">D&eacute;partement :</TH>"
         print "<TD><select name=\"department\" onChange=\"choix(this.form)\">"
@@ -334,7 +336,7 @@ class WebView:
         print "<h4><span style=\"color: red\">Cette formation est en cours de diffusion :</span></h4>"
         print "<hr>"
         print "<TABLE BORDER = 0>"
-        print "<FORM METHOD = post ACTION = \"teleoddcast.py\">"
+        print "<FORM METHOD = post ACTION = \"telecaster.py\">"
         print "<TR><TH align=\"left\">Titre :</TH><TD>"+self.title+"</TD></TR>"
         print "<TR><TH align=\"left\">D&eacute;partement :</TH><TD>"+department+"</TD><TR>"
         print "<TR><TH align=\"left\">Conference :</TH><TD>"+conference+"</TD><TR>"
@@ -356,7 +358,7 @@ class WebView:
         self.footer()
 
 
-class TeleOddCast:
+class TeleCaster:
     """Manage the calls of Station and Webview to get the network and
     disk streams"""
 
@@ -365,7 +367,7 @@ class TeleOddCast:
         self.conf_file = conf_file
         self.school_file = school_file
         conf_t = xml2dict(self.conf_file)
-        self.conf = conf_t['teleoddcast']
+        self.conf = conf_t['telecaster']
         self.title = self.conf['infos']['name']
         self.root_dir = self.conf['server']['root_dir']
         self.lock_file = self.root_dir + os.sep + self.conf['server']['lock_file']
@@ -403,6 +405,7 @@ class TeleOddCast:
                 self.conference_dict = get_conference_from_lock(self.lock_file)
             s = Station(self.conf_file, self.conference_dict, self.lock_file)
             s.stop()
+            #w.start_form('Please wait : encoding file to MP3...')
             w.start_form()
 
         elif self.odd_pid == []:
@@ -410,10 +413,10 @@ class TeleOddCast:
 
 
 # Call main function.
-conf_file = 'etc/teleoddcast.xml'
-school_file = 'etc/default_conferences.xml'
+conf_file = 'etc/telecaster.xml'
+school_file = 'etc/pre-barreau_conferences.xml'
 
 if __name__ == '__main__':
-    t = TeleOddCast(conf_file, school_file)
+    t = TeleCaster(conf_file, school_file)
     t.main()
 
index 4e28098b42993a48c360fb637c07bad436fc976c..6970619796bb31333df9773124306ba3757fb0ba 100644 (file)
--- a/tools.py
+++ b/tools.py
@@ -52,7 +52,7 @@ def xml2dict(conf_file):
 
 def get_pid(proc,uid):
     """Get a process pid filtered by arguments and uid"""
-    (list1, list2) = os.popen4('pgrep -f -U '+str(uid)+' '+'"'+proc+'"')
+    (list1, list2) = os.popen4('pgrep -n -f -U '+str(uid)+' '+'"'+proc+'"')
     pids = list2.readlines()
     if pids != '':
         for pid in pids: