Quienes usan Rhythmbox, seguramente han utilizado el complemento (plugin) «Lyrics» que busca las letras de canciones en conocidos sitios web que guardan cientos de letras.
El complemento «Lyrics» es una colección de pequeños programas Python que buscan la página web con la letra, que luego se conviert a texto plano y finalmente se muestra una ventana con la letra de la canción, la búsqueda se hace en sitios específicos.
Mi encuentro con este complemento se dió buscando apoyar mi aprendizaje de Portugués, por que pienso que al escuchar música en un idioma y cantarla tiene efectos positivos en la pronunciación y entrenamiento del oído a reconocer sonidos de otro idioma. Para mi mala suerte, el complemento Lyrics no busca letras en sitios de lengua portuguesa, en particular del Brasil. Así que me puse a desempolvar mis oxidadas habilidades para leer y escribir código, y de paso a recordar mis mínimos conocimientos de python. El trabajo me tomó unos días y finalmente tengo casi terminado el código que busca letras de canciones en portugués, estoy usando el sitio de Terra brasilero, que gentilmente proporciones cientos de letras.
Aún queda corregir el siguiente error que se presenta al recuperar algunas letras de canciones:
Gtk-CRITICAL **: gtk_text_buffer_emit_insert: assertion `g_utf8_validate (text, len, NULL)' failed
No hice una investigación profunda del tema pero hasta donde pude ver el problema se debe al tipo de codificación de la página web. En los próximos días voy a indagar mas el tema y cuando esté resuelto será buena idea limpiar el código y ponerse en contacto con el desarrollador del componentente Lyrics y ver si acepta mi contribución de código.
Para los interesados en como se hizo, aquí van algunas simples notas:
- En mi Ubuntu Hardy el código del complemento «Lyrics» está en /usr/lib/rhythmbox/plugins/lyrics/
LyricsConfigureDialog.pycontiene las opciones para habilitar los sitios de letras, en el diálogo de preferencias del componenteLyricsParse.pyes el código maestro que busca la letra de la canción en cada uno de los sitios de letras.SitioParser.pycontiene el código específico que consulta el sitio de letras, analiza la respuesta y prepara el resultado final.- Y como siempre: use the force, read the source code
Actualización
(Vierenes 17-Abr-2009 16:39)
Esta tarde me puse a corregir los errores, y a mejorar la conversión de entidades HTML para representar caracteres del alfabeto portugués a su valor correcto en UTF-8. Otra vez San Google(tm) fue de gran ayuda para resolver los problemas.
El error:
Gtk-CRITICAL **: gtk_text_buffer_emit_insert: assertion `g_utf8_validate (text, len, NULL)' failed
Se debía al uso de caracteres no UTF-8 para mostrar via GtkTextBuffer, esta simple línea corrigió el asunto:
result = result.decode('iso-8859-1').encode('UTF-8')
Para convertir entidades HTML a UTF-8 tomé el método _replace_entity de django/utils/text.py.
Aquí está la versión final del código (perdón por la identación, es culpa del blog).
# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
#
# Copyright (C) 2007 Hardy Beltran Monasterios
#
# 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, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
.
import urllib
import rb
import re
import sys
.
# Deal with html entitys and utf-8
# Code taken from
# django/utils/text.py
.
from htmlentitydefs import name2codepoint
.
pattern = re.compile("&(\w+?);")
.
def _replace_entity(match):
text = match.group(1)
if text[0] == u'#':
text = text[1:]
try:
if text[0] in u'xX':
c = int(text[1:], 16)
else:
c = int(text)
return unichr(c)
except ValueError:
return match.group(0)
else:
try:
return unichr(name2codepoint[text])
except (ValueError, KeyError):
return match.group(0)
.
def unescape_entities(text):
return pattern.sub(_replace_entity, text)
.
.
class TerraParser (object):
def __init__(self, artist, title):
self.artist = artist
self.title = title
.
def search(self, callback, *data):
path = 'http://letras.terra.com.br/'
.
artist = urllib.quote(self.artist)
title = urllib.quote(self.title)
join = urllib.quote(' - ')
.
wurl = 'winamp.php?t=%s%s%s' % (artist, join, title)
print >> sys.stderr, 'Searching Lyric: %s' % path+wurl
.
loader = rb.Loader()
loader.get_url (path + wurl, self.got_lyrics, callback, *data)
.
def got_lyrics(self, result, callback, *data):
if result is None:
callback (None, *data)
return
.
if result is not None:
result = result.decode('iso-8859-1').encode('UTF-8')
if re.search('Música não encontrada', result):
callback (None, *data)
elif re.search('', result):
callback(self.parse_lyrics(result), *data)
else:
callback (None, *data)
else:
callback (None, *data)
.
.
def parse_lyrics(self, source):
source = re.split('', source, 1)[1]
source = re.split('', source, 2)
# Parse artist and title
song = re.sub('', '', source[0])
song = re.split('\n', song)
title = "%s - %s\n\n" % (song[1], song[0])
# Parse lyrics
lyrics = re.split('

