From 1960fd5299e1bcc0eeddbd417d7fbfb7d6c0a6ec Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Fri, 3 Apr 2009 21:10:24 +0000 Subject: [PATCH] add metadata functions --- tools/__init__.py | 2 + tools/mp3.py | 114 +++++++++++++++++++++++++++++++++++++++ tools/ogg.py | 134 ++++++++++++++++++++++++++++++++++++++++++++++ tools/tools.py | 33 ++++++++++++ 4 files changed, 283 insertions(+) create mode 100644 tools/mp3.py create mode 100644 tools/ogg.py create mode 100644 tools/tools.py diff --git a/tools/__init__.py b/tools/__init__.py index 3181cdb..89f1866 100644 --- a/tools/__init__.py +++ b/tools/__init__.py @@ -1,3 +1,5 @@ # -*- coding: utf-8 -*- from xmltodict import * from PyRSS2Gen import * +from mp3 import * +from ogg import * diff --git a/tools/mp3.py b/tools/mp3.py new file mode 100644 index 0000000..7b8c591 --- /dev/null +++ b/tools/mp3.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2006-2009 Guillaume Pellerin +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://svn.parisson.org/telemeta/TelemetaLicense. +# +# Author: Guillaume Pellerin + +import os +import string +from mutagen.easyid3 import EasyID3 +from mutagen.mp3 import MP3 + +EasyID3.valid_keys["comment"]="COMM::'XXX'" + +class Mp3: + """An 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', + } + self.metadata = self.get_metadata() + self.description = self.get_description() + self.mime_type = self.get_mime_type() + self.extension = self.get_file_extension() + #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_metadata(self): + m = MP3(self.media, ID3=EasyID3) + metadata = {} + for key in self.keys2id3.keys(): + try: + metadata[key] = m[key][0] + except: + pass + 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 + diff --git a/tools/ogg.py b/tools/ogg.py new file mode 100644 index 0000000..b0fef85 --- /dev/null +++ b/tools/ogg.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2007 Parisson SARL +# Copyright (c) 2006-2007 Guillaume Pellerin +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://svn.parisson.org/telemeta/TelemetaLicense. +# +# Author: Guillaume Pellerin + +import os +import string +import subprocess + +from telemeta.export.core import * +from telemeta.export.api import IExporter +from mutagen.oggvorbis import OggVorbis + +class OggExporter(ExporterCore): + """Defines methods to export to OGG Vorbis""" + + implements(IExporter) + + def __init__(self): + self.item_id = '' + self.metadata = {} + self.description = '' + self.info = [] + self.source = '' + self.dest = '' + self.options = {} + self.bitrate_default = '192' + self.buffer_size = 0xFFFF + self.dub2args_dict = {'creator': 'artist', + 'relation': 'album' + } + + 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 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): + media = OggVorbis(self.dest) + for tag in self.metadata.keys(): + media[tag] = str(self.metadata[tag]) + media.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 + + 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 -r 44100 -t wav -c2 - | oggenc %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: + yield chunk + + # Post-proccessing + #self.post_process(self.item_id, + #self.source, + #self.metadata, + #self.ext, + #self.cache_dir, + #self.options) + diff --git a/tools/tools.py b/tools/tools.py new file mode 100644 index 0000000..e2db650 --- /dev/null +++ b/tools/tools.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (c) 2007-2009 Guillaume Pellerin +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://svn.parisson.org/deefuzz/wiki/DefuzzLicense. +# +# Author: Guillaume Pellerin + +import os +import re +import sys +import time +import datetime +import string +import random + +def clean_word(word) : + """ Return the word without excessive blank spaces, underscores and + characters causing problem to exporters""" + word = re.sub("^[^\w]+","",word) #trim the beginning + word = re.sub("[^\w]+$","",word) #trim the end + word = re.sub("_+","_",word) #squeeze continuous _ to one _ + word = re.sub("^[^\w]+","",word) #trim the beginning _ + #word = string.replace(word,' ','_') + #word = string.capitalize(word) + dict = '&[];"*:,' + for letter in dict: + word = string.replace(word,letter,'_') + return word \ No newline at end of file -- 2.39.5