Obtener información de una canción
El tema de esta entrada ha surgido a partir de la necesidad de obtener esta información de una canción, para un programa que en breve os presentaré en otra entrada del blog.
Esta parte no tiene que ver directamente con el tema de esa entrada, pero sí me ha parecido interesante y tal vez útil para otras personas, así que me he decidido a publicar estas líneas al respecto, ya que en la otra no tiene cabida.
El problema es sencillo; A partir de los datos básicos de un tema musical (título y/o artista) necesito obtener algo de información de esa canción. En mi caso me interesaba el título del álbum y una caratula (aunque se puede obtener más).
Hay varios servicios en Internet que os ofrecen la posibilidad de acceder a esta información.
http://www.freecovers.net/api/
En esta ocasión me he decantado por iTunes, por la facilidad de uso y porque no hace falta registrarse. Hay algunos otros cuya utilización es gratuita, pero requieren un registro y el uso de una APIKey. En este caso, por simple sencillez, me he decantado por este. Además, ya me ofrece (con creces) los datos que para este ejemplo estaba buscando.
En esta página podeís acceder a la información de la API para realizar búsquedas en iTunes.
Como podéis ver es bastante sencilla de utilizar.
Para el ejemplo utilizaremos, el parámetro de búsqueda term y un número de resultados limit, aunque hay más como podéis ver en la documentación.
Una vez realizamos la llamada, al webservice, la respuesta la obtenemos en formato JSON.
Por ejemplo, para una llamada como esta:
https://itunes.apple.com/search?term=Alex+Ubago+gaviotas&limit=2
Obtendremos usa respuesta como esta:
{ «resultCount»: 1, «results»: [ { «wrapperType»: «track», «kind»: «song», «artistId»: 496159, «collectionId»: 101175279, «trackId»: 101174058, «artistName»: «Alex Ubago», «collectionName»: «Cien Gaviotas Dónde Irán – Un Tributo a Duncan Dhu», «trackName»: «Esos Ojos Negros», «collectionCensoredName»: «Cien Gaviotas Dónde Irán – Un Tributo a Duncan Dhu», «trackCensoredName»: «Esos Ojos Negros», «collectionArtistId»: 36270, «collectionArtistName»: «Various Artists», «artistViewUrl»: «https://itunes.apple.com/us/artist/alex-ubago/id496159?uo=4″, «collectionViewUrl»: «https://itunes.apple.com/us/album/esos-ojos-negros/id101175279?i=101174058&uo=4″, «trackViewUrl»: «https://itunes.apple.com/us/album/esos-ojos-negros/id101175279?i=101174058&uo=4″, «previewUrl»: «http://a1292.phobos.apple.com/us/r1000/074/Music/e4/1e/fd/mzm.pzsyztgl.aac.p.m4a», «artworkUrl30»: «http://a1.mzstatic.com/us/r30/Music/8b/a2/d8/mzi.nrwneagp.30×30-50.jpg», «artworkUrl60»: «http://a5.mzstatic.com/us/r30/Music/8b/a2/d8/mzi.nrwneagp.60×60-50.jpg», «artworkUrl100»: «http://a5.mzstatic.com/us/r30/Music/8b/a2/d8/mzi.nrwneagp.100×100-75.jpg», «collectionPrice»: 12.99, «trackPrice»: 0.99, «releaseDate»: «2005-10-03T07:00:00Z», «collectionExplicitness»: «notExplicit», «trackExplicitness»: «notExplicit», «discCount»: 1, «discNumber»: 1, «trackCount»: 17, «trackNumber»: 3, «trackTimeMillis»: 230267, «country»: «USA», «currency»: «USD», «primaryGenreName»: «Latin», «radioStationUrl»: «https://itunes.apple.com/station/idra.101174058″ } ] } |
En este caso nos devuelve un único resultado (he realizado la búsqueda expresamente para que sólo devuelva 1), pero si hubiera más, dado que hemos añadido el parámetro limit=2, obtendríamos sólo los 2 primeros.
Vemos además que en la respuesta, el primer ítem nos devuelve el número de elementos que llegan en el resultado. A partir de ahí es fácil realizar el “parse” de la respuesta.
En el ejemplo que os adjunto más abajo he utilizado para hacerlo la librería LkJSON de Leonid Koninin, que podéis encontrar en SourceForge, para extraer los datos y que ya había utilizado en alguna entrada anterior. Si estáis utilizando una de las versiones nuevas de Delphi, sabéis que ya incluyen librería para JSON, incluso podéis obtener la respuesta y utilizar directamente componentes como TRESTResponse y TRESTResponseAdapter.
Con un código como el que veis a continuación podemos obtener la lista de elementos que nos devuelve la web y almacenarlos en un array; De forma similar podríamos utilizar un TDataSet (derivado).
TInfoTrack = record ArtistName:string; CollectionName:string; TrackName:string; Imagen100:string; Genero:string; Anyo:Integer; PathImage:string; end; TInfoTrackArray = array of TInfoTrack; …. var obj:TlkJSONobject; objData:TlkJSONobject; objRes:TlkJSONlist; i:integer; str:string; begin // Conseguir la cadena formateada. obj := TlkJSONobject.Create(); // proteccion try // Parse TlkJSONBase(obj) := TlkJSON.ParseText(FResponse.Text); // Objeto results? if (obj.IndexOfName('results') <> -1) then begin // lista de resultados TlkJSONBase(objRes) := obj.Field['results']; // array de productos SetLength(FInfoArray, objRes.Count); // Recorrer la lista de productos. for i := 0 to (objRes.Count - 1) do begin // Datos del producto TlkJSONBase(objData) := objRes.Child[i]; // Obtener datos FInfoArray[i].ArtistName := objData.Field['artistName'].Value; FInfoArray[i].CollectionName := objData.Field['collectionName'].Value; FInfoArray[i].TrackName := objData.Field['trackName'].Value; FInfoArray[i].Imagen100 := objData.Field['artworkUrl100'].Value; FInfoArray[i].Genero := objData.Field['primaryGenreName'].Value; Str := objData.Field['releaseDate'].Value; Str := Copy(Str, 1, 4); FInfoArray[i].Anyo := StrToIntDef(Str, 0); end; end; finally FreeAndNil(obj); end; end; |
Para hacer la llamada utilizamos los componentes de las Indy como es habitual, aunque en este caso, dado que al servidor se accede mediante el protocolo https, debemos utilizar las librerías necesarias para SSL (Secure Socket Layer) que podéis descargar desde aquí. Tened en cuenta que hay versiones diferentes para 32 y 64 bits.
// ini IdHTTP.ReadTimeout := 30000; IdSSL :=TIdSSLIOHandlerSocket.Create(nil); IdHTTP.IOHandler := IdSSL; IdSSL.SSLOptions.Method := sslvTLSv1; IdSSL.SSLOptions.Mode := sslmUnassigned; idHTTP.HandleRedirects := True; // Montamos la cadena a solicitar s := AnsiReplaceText(FArtista, ' ', '+') + '+' + AnsiReplaceText(FTitulo, ' ', '+'); s := Format(INI_URL, [s, FNumResults]); // Proteccion para solicitar datos try // Obtener resultado idHTTP.Get(s, ms); |
…
Todo ello lo he encapsulado en una clase que utiliza threads para hacer la llamada; Según los parámetros de Artista, Título de la canción y número máximo de resultados, obtendrás los datos de la petición y descarga una de las carátulas (100×100) que nos da la respuesta.
La clase tiene la siguiente definición:
//: Thread para obtener info. de una canción TInfoTrackThread = class(TThread) private FArtista: string; FTitulo: string; FResponse: TStrings; FInfoArray: TInfoTrackArray; FNumResults: integer; FID: Integer; // Realiza el Parse de la resouesta procedure ParseResponseIndo(TS:TStrings); // descarga la carátula procedure DownloadImage(URLImage, ImageName:string; var imgPath:string); public // Interno property ID:Integer read FID write FID; // artista que utilizamos para la búsqueda property Artista:string read FArtista; // Título que utilizamos para la búsqueda property Titulo:string read FTitulo; // Numero de resultados (máximo) property NumResults:integer read FNumResults write FNumResults; // La respuesta en formato JSON property Response:TStrings read FResponse write FResponse; // Array de resultados property InfoArray:TInfoTrackArray read FInfoArray write FInfoArray; // redefinir métodos de la clase base procedure Execute; override; constructor Create(ID:Integer; AArtista, ATitulo:string); destructor Destroy; override; end; |
Un ejemplo de la llamada podría ser este:
var th:TInfoTrackThread; begin ... // Crear el thread de búsqueda th := TInfoTrackThread.Create(edtArtista.Text, edtCancion.Text); th.NumResults := edtNumResults.Value; // ini th.FreeOnTerminate := True; th.OnTerminate := TerminateSearch; // ejecutar th.Resume; |
Una vez finalizada la ejecución podemos acceder a los datos recorriendo el array que nos devuelve el thread con tantos elementos como ítems llegan en la respuesta.
Utilizando esta clase he creado un ejemplo sencillo, que a partir del título y/o el artista os descarga un número de resultados coincidentes con estos términos. El propio thread descarga los datos y la carátula (una de ellas). Aquí podéis ver una pequeña secuencia de cómo funciona…
La unit que implementa el thread, podéis descargarla desde aquí.
Y el proyecto de ejemplo que la utiliza, podéis descargarlo en los siguientes links.
Un saludo y hasta la próxima.
Embarcadero MVP.
Analista y Programador de Sistemas Informáticos.
Estudios de Informática (Ingeniería Técnica Superior) en la UPC (Universidad Politécnica de Barcelona).
Llevo utilizando Delphi desde su versión 3. Especialista en diseño de componentes, Bases de Datos, Frameworks de Persistencia, Integración Continua, Desarrollo móvil,…
Hola! Como puedo instalar las JSON? Gracias
@Gonzalo
Hola Gonzalo.
Hay varias librerías para tratar JSON y cada una de ellas tendrá una instalación diferente.
Yo a menudo utilizo lkJSON que no requiere instalación, pues es una unit independiente, no un componente.
Buenso dias, lo que no termino de entender como y donde el servicio web se da cuenta se entera que le han enviado o solicitado una peticion?
@juan
Hola Juan.
Te agradezco el comentario y te pido dsculpas por que no contestarte antes (no me llegó el aviso).
Cuando utilizas el componente de las Indy y realizas un Get, realmente estás enviando una petición https al servidor.