Anywhere.FM Python CLI Player
Wednesday, 20. August 2008, 22:46:00
Le dernier bug rencontré était, comme expliqué dans un précédent, le décodage de certaines chaines de caractères qui ne respectaient pas le format UTF-8. Le responsable était finalement le site lui-même ou plutôt les utilisateurs qui envoit des données au serveur à l'aide d'un navigateur utilisant un autre encodage.
Ainsi dans mon cas, le décodage de mon profil échouait car dans les données retournées se trouvait entre autres le nom mal encodé Ted Gärdestad correspondant à l'un des artistes les plus écoutés par une personne dans ma liste d'amis (la fonctione RPC get_complete_profiles du site renvoie beaucoup d'infos superflues :-| )
Pour résoudre ce souci il fallait intervenir sur la méthode readString de la classe Decoder de PyAMF (module amf3).
Comme je ne voulais pas et d'ailleurs ne pouvais pas modifier le code de PyAMF (installé sous forme de "egg" sur mon système), j'ai plutôt imaginé une technique de "Python API Hooking".
En fait il s'agit simplement de la redéfinition puis d'un écrasement d'une méthode d'une classe Python... mais avouez que dit comme ça, ça en jette beaucoup moins
La vérification ajoutée à la méthode readString consistait à lever une exception si le passage en unicode échouait et dans ce cas, à remplacer les mauvais caractères par des underscore. Ce qui donnait :
from pyamf.amf3 import Decoder
from string import maketrans
in_string="".join([chr(i) for i in range(126,256)])
out_string="_"*130
trans=maketrans(in_string,out_string)
def my_readString(object, use_references=True):
def readLength():
x = object.readUnsignedInteger()
return (x >> 1, x & 1 == 0)
length, is_reference = readLength()
if use_references and is_reference:
return object.context.getString(length)
buf = object.stream.read(length)
try:
result = unicode(buf, "utf8")
except UnicodeDecodeError:
buf=buf.translate(trans)
result = unicode(buf, "utf8")
if len(result) != 0 and use_references:
object.context.addString(result)
return result
sav_readString=Decoder.readString
Decoder.readString=my_readString
L'opération de récupération et de correction de la liste de lecture "Entire Library" pouvant prendre un certain temps si la liste de lecture est importante, j'ai préféré diviser le programme en deux : le premier code récupère la liste de lecture et l'enregistre sous forme d'une liste Python dans un fichier ; le second est le lecteur qui charge la liste de lecture et va lire de façon aléatoire et unique un titre de la bibliothèque
Au final on gagne largement en charge réseau et en temps d'accès par rapport à l'utilisation du site Internet et on n'a pas à subir les utilisations mémoire et processeur gourmandes du plugin Flash
Vous avez donc get_library.py qui donne un résultat de ce style :
Connection on the server... Connexion successfull! user_id = 5418 Hooking the PyAMF Decoder Getting the Entire Library playlist... Library dumped!
qui génère un fichier library.py utilisé par le lecteur player.py qui joue les flux musicaux et donne un affichage dans ce genre :
Connecting on the server... Connexion successfull user_id = 5418 White America - Eminem ( The Eminem Show ) Troisième tour - Rapaces ( 2000 ) Les Poubelles du Coeur - Parabellum ( 1984-2004 ) Question de fun - Les Shériff ( La Saga Des Sheriff (disc 2) ) Alcohol - Dropkick Murphys ( Live On St. Patrick's Day ) Marilyn Moore - Sonic Youth ( Evol ) Come Back, Baby - Ramones ( The Chrysalis Years (disc 1) ) The Watcher - Dr. Dre ( 2001 ) ...
La partie lecteur nécessite la présence de mplayer sur le système. Seul regret : on ne peut pas trop jouer sur l'interface de mplayer pour avoir quelque chose de moins verbeux et agréable... une simple barre de défilement de la lecture en cours (à l'instar de wget) m'aurait bien plu... mais c'est une autre histoire
