--- /dev/null
+# -*- coding: utf-8 -*-
+from mp3 import *
+from ogg import *
+from flac import *
+from tools import *
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007-2009 Guillaume Pellerin <yomguy@parisson.com>
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# 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 <pellerin@parisson.com>
+
+import os
+import string
+import subprocess
+from mutagen.flac import FLAC
+from tempfile import NamedTemporaryFile
+
+class Flac:
+ """Defines methods to export to FLAC"""
+
+ def __init__(self, media):
+ self.media = media
+ self.item_id = ''
+ self.source = ''
+ self.metadata = {}
+ self.options = {}
+ self.description = ''
+ self.dest = ''
+ self.quality_default = '-5'
+ self.info = []
+ self.buffer_size = 0xFFFF
+
+ def get_format(self):
+ return 'FLAC'
+
+ def get_file_extension(self):
+ return 'flac'
+
+ def get_mime_type(self):
+ return 'application/flac'
+
+ def get_description(self):
+ return 'FIXME'
+
+ def get_file_info(self):
+ try:
+ file1, file2 = os.popen4('metaflac --list "'+self.dest+'"')
+ info = []
+ for line in file2.readlines():
+ info.append(clean_word(line[:-1]))
+ self.info = info
+ return self.info
+ except:
+ raise IOError('ExporterError: metaflac is not installed or ' + \
+ 'file does not exist.')
+
+ def set_cache_dir(self,path):
+ """Set the directory where cached files should be stored. Does nothing
+ if the exporter doesn't support caching.
+
+ The driver shouldn't assume that this method will always get called. A
+ temporary directory should be used if that's not the case.
+ """
+ self.cache_dir = path
+
+ def decode(self):
+ try:
+ file_name, ext = get_file_name(self.source)
+ dest = self.cache_dir+os.sep+file_name+'.wav'
+ os.system('flac -d -o "'+dest+'" "'+self.source+'"')
+ self.source = dest
+ return dest
+ except:
+ raise IOError('ExporterError: decoder is not compatible.')
+
+ def write_tags(self):
+ media = FLAC(self.media)
+ for tag in self.metadata.keys():
+ if tag == 'COMMENT':
+ media['DESCRIPTION'] = unicode(self.metadata[tag])
+ else:
+ media[tag] = unicode(self.metadata[tag])
+ try:
+ media.save()
+ except:
+ raise IOError('ExporterError: cannot write tags.')
+
+ def get_tags(self):
+ metadata = {}
+ audio = FLAC(self.media)
+ if audio.has_key('title'):
+ metadata['title'] = audio['title'][0]
+ if audio.has_key('artist'):
+ metadata['artist'] = audio['artist'][0]
+ if audio.has_key('album'):
+ metadata['album'] = audio['album'][0]
+ if audio.has_key('year'):
+ metadata['year'] = audio['year'][0]
+ if audio.has_key('comment'):
+ metadata['comment'] = audio['comment'][0]
+ if audio.has_key('genre'):
+ metadata['genre'] = audio['genre'][0]
+ if audio.has_key('tracktotal'):
+ metadata['tracktot'] = int(audio['tracktotal'][0])
+ else:
+ metadata['tracktot'] = None
+ if audio.has_key('tracknumber'):
+ metadata['trackn'] = int(audio['tracknumber'][0])
+ if metadata['tracktot'] != None:
+ metadata['track_str'] = "%s / %s" % (metadata['trackn'], metadata['tracktot'])
+ else:
+ metadata['track_str'] = str(metadata['trackn'])
+ if audio.has_key('mcn'):
+ metadata['MCN'] = audio['mcn'][0]
+ if audio.has_key('isrc'):
+ metadata['ISRC'] = audio['isrc'][0]
+
+ print self.media
+ print "Sample rate: ", audio.info.sample_rate
+ print "Nr. of channels: ", audio.info.channels
+ print "Bits per sample: ", audio.info.bits_per_sample
+ print "Total nr. of samples:", audio.info.total_samples
+ print "Length in secs: ", audio.info.length
+ print "MD5: ", audio.info.md5_signature
+
+ return metadata
+
+ def get_args(self,options=None):
+ """Get process options and return arguments for the encoder"""
+ args = []
+ if not options is None:
+ self.options = options
+ if not ('verbose' in self.options and self.options['verbose'] != '0'):
+ args.append('-s')
+ if 'flac_quality' in self.options:
+ args.append('-f ' + self.options['flac_quality'])
+ else:
+ args.append('-f ' + self.quality_default)
+ else:
+ args.append('-s -f ' + self.quality_default)
+
+ #for tag in self.metadata.keys():
+ #value = clean_word(self.metadata[tag])
+ #args.append('-c %s="%s"' % (tag, value))
+ #if tag in self.dub2args_dict.keys():
+ #arg = self.dub2args_dict[tag]
+ #args.append('-c %s="%s"' % (arg, value))
+
+ return args
+
+ def process(self, item_id, source, metadata, options=None):
+ self.item_id = item_id
+ self.source = source
+ self.metadata = metadata
+ self.args = self.get_args(options)
+ self.ext = self.get_file_extension()
+ self.args = ' '.join(self.args)
+ self.command = 'sox "%s" -s -q -b 16 -r 44100 -t wav -c2 - | flac -c %s - ' % (self.source, self.args)
+
+ # Pre-proccessing
+ self.dest = self.pre_process(self.item_id,
+ self.source,
+ self.metadata,
+ self.ext,
+ self.cache_dir,
+ self.options)
+
+ # Processing (streaming + cache writing)
+ stream = self.core_process(self.command, self.buffer_size, self.dest)
+
+ for chunk in stream:
+ pass
+
+ self.write_tags(self.dest)
+ file = open(self.dest,'r')
+
+ while True:
+ chunk = file.read(self.buffer_size)
+ if len(chunk) == 0:
+ break
+ yield chunk
+
+ file.close()
+
+ # Post-proccessing
+ #self.post_process(self.item_id,
+ #self.source,
+ #self.metadata,
+ #self.ext,
+ #self.cache_dir,
+ #self.options)
+
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright Guillaume Pellerin (2006-2009)
+
+# <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>
+
+import os
+import string
+from mutagen.easyid3 import EasyID3
+from mutagen.mp3 import MP3
+from tools import *
+
+EasyID3.valid_keys["comment"]="COMM::'XXX'"
+EasyID3.valid_keys["copyright"]="TCOP::'XXX'"
+
+class Mp3:
+ """A MP3 file object"""
+
+ def __init__(self, media):
+ self.media = media
+ self.item_id = ''
+ self.source = self.media
+ self.options = {}
+ self.bitrate_default = '192'
+ self.cache_dir = os.sep + 'tmp'
+ self.keys2id3 = {'title': 'TIT2',
+ 'artist': 'TPE1',
+ 'album': 'TALB',
+ 'date': 'TDRC',
+ 'comment': 'COMM',
+ 'genre': 'TCON',
+ 'copyright': 'TCOP',
+ }
+ self.metadata = self.get_file_metadata()
+ self.description = self.get_description()
+ self.mime_type = self.get_mime_type()
+ self.media_info = get_file_info(self.media)
+ self.file_name = self.media_info[0]
+ self.file_title = self.media_info[1]
+ self.file_ext = self.media_info[2]
+ self.extension = self.get_file_extension()
+ self.size = os.path.getsize(media)
+ #self.args = self.get_args()
+
+ def get_format(self):
+ return 'MP3'
+
+ def get_file_extension(self):
+ return 'mp3'
+
+ def get_mime_type(self):
+ return 'audio/mpeg'
+
+ def get_description(self):
+ return "MPEG audio Layer III"
+
+ def get_file_metadata(self):
+ m = MP3(self.media, ID3=EasyID3)
+ metadata = {}
+ for key in self.keys2id3.keys():
+ try:
+ metadata[key] = m[key][0]
+ except:
+ metadata[key] = ''
+ return metadata
+
+ def decode(self):
+ try:
+ os.system('sox "'+self.media+'" -s -q -r 44100 -t wav "' \
+ +self.cache_dir+os.sep+self.item_id+'"')
+ return self.cache_dir+os.sep+self.metadata['title']+'.wav'
+ except:
+ raise IOError('ExporterError: decoder is not compatible.')
+
+ def write_tags(self):
+ """Write all ID3v2.4 tags by mapping dub2id3_dict dictionnary with the
+ respect of mutagen classes and methods"""
+ id3 = id3.ID3(self.media)
+ for tag in self.metadata.keys():
+ if tag in self.dub2id3_dict.keys():
+ frame_text = self.dub2id3_dict[tag]
+ value = self.metadata[tag]
+ frame = mutagen.id3.Frames[frame_text](3,value)
+ try:
+ id3.add(frame)
+ except:
+ raise IOError('ExporterError: cannot tag "'+tag+'"')
+ try:
+ id3.save()
+ except:
+ raise IOError('ExporterError: cannot write tags')
+
+ def get_args(self, options=None):
+ """Get process options and return arguments for the encoder"""
+ args = []
+ if not options is None:
+ self.options = options
+ if not ( 'verbose' in self.options and self.options['verbose'] != '0' ):
+ args.append('-S')
+ if 'mp3_bitrate' in self.options:
+ args.append('-b ' + self.options['mp3_bitrate'])
+ else:
+ args.append('-b '+self.bitrate_default)
+ #Copyrights, etc..
+ args.append('-c -o')
+ else:
+ args.append('-S -c -o')
+
+ for tag in self.metadata.keys():
+ if tag in self.dub2args_dict.keys():
+ arg = self.dub2args_dict[tag]
+ value = self.metadata[tag]
+ args.append('--' + arg)
+ args.append('"' + value + '"')
+
+ return args
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright Guillaume Pellerin (2006-2009)
+
+# <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>
+
+import os
+import string
+from mutagen.oggvorbis import OggVorbis
+from tools import *
+
+
+class Ogg:
+ """An OGG file object"""
+
+ def __init__(self, media):
+ self.media = media
+ self.media_obj = OggVorbis(self.media)
+ self.item_id = ''
+ self.source = self.media
+ self.options = {}
+ self.bitrate_default = '192'
+ self.cache_dir = os.sep + 'tmp'
+ self.keys2ogg = {'title': 'title',
+ 'artist': 'artist',
+ 'album': 'album',
+ 'date': 'date',
+ 'comment': 'comment',
+ 'genre': 'genre',
+ }
+ self.metadata = self.get_file_metadata()
+ self.description = self.get_description()
+ self.mime_type = self.get_mime_type()
+ self.media_info = get_file_info(self.media)
+ self.file_name = self.media_info[0]
+ self.file_title = self.media_info[1]
+ self.file_ext = self.media_info[2]
+ self.extension = self.get_file_extension()
+ self.size = os.path.getsize(media)
+ #self.args = self.get_args()
+
+ def get_format(self):
+ return 'OGG'
+
+ def get_file_extension(self):
+ return 'ogg'
+
+ def get_mime_type(self):
+ return 'application/ogg'
+
+ def get_description(self):
+ return 'FIXME'
+
+ def get_file_info(self):
+ try:
+ file_out1, file_out2 = os.popen4('ogginfo "'+self.dest+'"')
+ info = []
+ for line in file_out2.readlines():
+ info.append(clean_word(line[:-1]))
+ self.info = info
+ return self.info
+ except:
+ raise IOError('ExporterError: file does not exist.')
+
+ def set_cache_dir(self,path):
+ self.cache_dir = path
+
+ def get_file_metadata(self):
+ metadata = {}
+ for key in self.keys2ogg.keys():
+ try:
+ text = self.media_obj[key][0].decode()
+ metadata[key] = text
+ except:
+ metadata[key] = ''
+ return metadata
+
+ def decode(self):
+ try:
+ os.system('oggdec -o "'+self.cache_dir+os.sep+self.item_id+
+ '.wav" "'+self.source+'"')
+ return self.cache_dir+os.sep+self.item_id+'.wav'
+ except:
+ raise IOError('ExporterError: decoder is not compatible.')
+
+ def write_tags(self):
+ for tag in self.metadata.keys():
+ self.media_obj[tag] = str(self.metadata[tag])
+ media_obj.save()
+
+ def get_args(self,options=None):
+ """Get process options and return arguments for the encoder"""
+ args = []
+ if not options is None:
+ self.options = options
+ if not ('verbose' in self.options and self.options['verbose'] != '0'):
+ args.append('-Q ')
+ if 'ogg_bitrate' in self.options:
+ args.append('-b '+self.options['ogg_bitrate'])
+ elif 'ogg_quality' in self.options:
+ args.append('-q '+self.options['ogg_quality'])
+ else:
+ args.append('-b '+self.bitrate_default)
+ else:
+ args.append('-Q -b '+self.bitrate_default)
+
+ for tag in self.metadata.keys():
+ value = clean_word(self.metadata[tag])
+ args.append('-c %s="%s"' % (tag, value))
+ if tag in self.dub2args_dict.keys():
+ arg = self.dub2args_dict[tag]
+ args.append('-c %s="%s"' % (arg, value))
+
+ return args
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright Guillaume Pellerin (2006-2009)
+
+# <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>
+
+import os
+
+def get_file_info(media):
+ file_name = media.split(os.sep)[-1]
+ file_title = file_name.split('.')[:-1]
+ file_title = '.'.join(file_title)
+ file_ext = file_name.split('.')[-1]
+ return file_name, file_title, file_ext