Salta al contingut principal

Cómo programar add-ons en XBMC: Un add-on más completo


Este tutorial forma parte de la serie Cómo programar un add-on de XBMC



Estamos a mitad de camino de este tutorial, pero ya tienes el conocimiento necesario para hacer un add-on bastante completo. Espero que a estas alturas ya te hayas convencido de que es algo relativamente sencillo sin necesidad de saber demasiado de programación. Y ya sabes cómo obtener datos de una página HTML usando expresiones regulares, que es lo más difícil.


Así que antes de entrar en la recta final vamos construir un add-on paso a paso desde cero, con el objetivo de afianzar lo que has aprendido en las entregas anteriores y de paso divertirte un poco.


Te aconsejo que elijas un sitio web que te guste como fuente de vídeos, para mí he elegido hacer un add-on completo que permita ver los vídeos de Disney Junior tanto en la web oficial como en YouTube. No se trata de un add-on demasiado complejo, como podrás comprobar enseguida, pero no va a desmerecer en nada a cualquiera de los que puedes encontrar publicados en muchos repositorios :)


Puedes elegir tu propia fuente para hacer el add-on, siguiendo los mismos pasos que yo voy a dar, o construir conmigo este interesante proyecto.


Ficheros de identificación


Escribir el fichero addon.xml es el primer paso, así tendrás algo con lo que probar las primeras líneas de código que vayas escribiendo. Aquí va el mío donde verás que he cogido el del ejemplo de “Mi Mediacenter” cambiando el id del add-on (lo he llamado plugin.video.disneyjunior), el nombre (“Disney Junior”) y las descripciones:



<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.disneyjunior"
name="Disney Junior"
version="1.0.0"
provider-name="tvalacarta">
<requires>
<import addon="xbmc.python" version="2.0"/>
<import addon="plugin.video.youtube" version="3.0.0"/>
</requires>
<extension point="xbmc.python.pluginsource" library="default.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<summary lang="en">Disney Junior</summary>
<description lang="en">Watch online Disney Junior videos (spanish)</description>
<summary lang="es">Disney Junior</summary>
<description lang="es">Todos los vídeos de Disney Junior online en Español</description>
<platform>all</platform>
</extension>
</addon>

Hacer el logo y el fanart requerirá un poco de práctica al principio, pero basta con que sepas manejar un programa de edición de imágenes donde puedas recortar y cambiar de tamaño. Un logo a 256×256 en PNG, y un fanart a 1280×720 en JPG. Ambos sacados de la web de Disney Junior y con algo de ayuda de Google.


El add-on de Disney Junior para XBMC, con su logo y fanart


Un menú de navegación


Un add-on que tenga algo más que unos pocos vídeos necesita un menú para que el usuario pueda elegir lo que quiere hacer. En mi caso tengo bastante claro el menú que tiene que tener un add-on de Disney Junior, porque conozco los gustos de mi hija pequeña.


Primero una opción “Web oficial” que muestre todos los vídeos de la web oficial de Disney Junior, seguido de otra opción “Canal de YouTube” para acceder a las playlists que la cadena tiene en su canal oficial de YouTube. Luego una entrada para recurrir al buscador, ya que solemos ponerle capítulos de Mickey Mouse buscándolos en YouTube, y por último una opción para configurar el add-on.



Hacer el menú es muy sencillo porque no hay que llamar a ningún sitio, simplemente añadir 4 elementos a la lista de items. En el fichero “default.py” de este add-on está hecho con estas líneas.



44 # Main menu
45 def main_list(params):
46 plugintools.log("disneyjunior.main_list "+repr(params))
47
48 plugintools.add_item(
49 action="disneyweb",
50 title=plugintools.get_localized_string(T_OFFICIAL_WEBSITE) ,
51 url="http://www.disney.es/disney-junior/contenido/video.jsp" ,
52 folder=True )
53
54 plugintools.add_item(
55 action="youtube_playlists",
56 title=plugintools.get_localized_string(T_YOUTUBE_CHANNEL),
57 url="http://gdata.youtube.com/feeds/api/users/"+YOUTUBE_CHANNEL_ID+"/playlists?v=2&start-index=1&max-results=30",
58 folder=True )
59
60 plugintools.add_item( action="search",
61 title=plugintools.get_localized_string(T_SEARCH) )
62
63 plugintools.add_item( action="preferences",
64 title=plugintools.get_localized_string(T_PREFERENCES),
65 folder = False )

Observarás que los títulos no están escritos directamente:



title = "Canal de YouTube"

sino que utilizan la función get_localized_string de Plugin Tools y un identificador numérico para la cadena de texto:



title = plugintools.get_localized_string(30003)

o mejor el identificador numérico de la cadena lo ponemos como una constante para mayor legibilidad:



T_YOUTUBE_CHANNEL=30003
title = plugintools.get_localized_string(T_YOUTUBE_CHANNEL)

Esto es así por el sistema que proporciona XBMC para tener un add-on donde los textos están adaptados al idioma de cada usuario. Es un poco lioso, pero simple y potente.


La idea es que a los textos que quieres traducir les asocias un identificador numérico (30003 en el ejemplo), y los escribes en un fichero “strings.xml” dentro del directorio “resources/language/Spanish”, “resources/language/English”, etc. Uno para cada idioma.



<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<strings>
<string id="30001">Última búsqueda</string>
<string id="30002">Web oficial</string>
<string id="30003">Canal de YouTube</string>
<string id="30004">Buscador ...</string>
<string id="30005">Configuración ...</string>
<string id="30006">ID del canal de YouTube ...</string>
<string id="30007">Idioma para los resultados del buscador</string>
</strings>

Luego los pones en tu código usando la función get_localized_string a la que le pasas el identificador numérico.


Si el usuario tiene su XBMC en inglés, se leerá el fichero strings.xml del directorio “resources/language/English”. Si lo tiene en español, de “resources/language/Spanish”, etc. Si no pones literales en el idioma del usuario se cogerá por defecto los que estén en inglés, así que no olvides poner al menos ese idioma.


Mostrando los vídeos de la web oficial


Abriendo la zona de vídeos de la web de Disney Junior encontrarás el reproductor, y la lista de vídeos disponibles debajo. A mi hija le encanta porque pones un vídeo, y a partir de ahí los reproduce uno detrás de otro en carrusel.



Si le das en Firefox a “Herramientas / Desarrollador web / Código fuente de la página” accedes al HTML, y si buscas dentro del HTML la cadena del título de uno de los vídeos encontrarás enseguida el bloque de HTML que representa a cada vídeo.



Yo lo que suelo hacer para facilitar el trabajo es copiar el HTML que me interesa en cuanto lo localizo, lo pego en el módulo Python que estoy desarrollando dentro de un bloque de comentario, y lo utilizo como punto de partida para construir la expresión regular.



'''
<div class="promo" style="background-image: url(/cms_res/disney-junior/images/promo_support/promo_holders/video.png);">
<a href="/disney-junior/contenido/video/canta_con_dj_arcoiris.jsp " class="promoLinkTracking"><img src="/cms_res/disney-junior/images/video/canta_dj_arco_iris_164x104.jpg" class="promo_image" alt=""/></a>
<div class="promo_title_3row"><p>Canta con DJ: La canción del arco iris</p></div>
<a class="playlist_button_large" href="" ref="canta_con_dj_arcoiris"><img src="/cms_res/disney-junior/images/promo_support/playlist_add_icon.png" alt="" /></a>
</div>
'''

A continuación pego el html tal como está dentro de una variable, para ir sustituyendo paso a paso y cometer los menos errores posibles:



pattern ='<div class="promo" style="background-image: url(/cms_res/disney-junior/images/promo_support/promo_holders/video.png);">'
pattern+='<a href="/disney-junior/contenido/video/canta_con_dj_arcoiris.jsp " class="promoLinkTracking"><img src="/cms_res/disney-junior/images/video/canta_dj_arco_iris_164x104.jpg" class="promo_image" alt=""/></a>'
pattern+='<div class="promo_title_3row"><p>Canta con DJ: La canción del arco iris</p></div>'
pattern+='<a class="playlist_button_large" href="" ref="canta_con_dj_arcoiris"><img src="/cms_res/disney-junior/images/promo_support/playlist_add_icon.png" alt="" /></a>'
pattern+='</div>'

Y entonces empiezo a sustituir. Del primer tag sólo me interesa el principio ya que sirve para marcar el comienzo del patrón, así que dejo un trozo y luego utilizo el patrón [^<]+ para saltar al siguiente tag.



pattern = '<div class="promo"[^<]+'

El segundo tag del bloque tiene el enlace al vídeo en el atributo href y entre comillas, así que uso el patrón [^"]+ y lo marco entre paréntesis porque me interesa el contenido. Y a continuación salto al siguiente tag con [^<]+:



pattern += '<a href="([^"]+)"[^<]+'

Ahora viene un tag de imagen que necesito para tener la captura del vídeo, aunque esté en la misma línea del texto lo dejo separado para mayor legibilidad.


Cojo la URL de la imagen con el mismo patrón [^"]+ que antes, también entre paréntesis, y salto al siguiente tag con [^<]+:



pattern += '<img src="([^"]+)"[^<]+'

Ya tengo la URL del vídeo y su imagen, sólo necesito el título para poder mostrar la información completa en el add-on. Así que salto los tags que vienen a continuación hasta encontrar el tag <p> que contiene el título. Como es un valor que está entre tags y no en un atributo, utilizo el patrón ([^<]+).



pattern += '</a[^<]+'
pattern += '<div[^<]+'
pattern += '<p>([^<]+)</p>'

Podría haber usado (.*?) para saltar todos los elementos e ir directamente al título, pero intento guardar ese recurso cuando es realmente necesario. De esa forma consigo que la carga de los items sea lo más rápida posible.


Antes de poner el código de este bloque nos falta un detalle. Para evitar que haya algún elemento que coincida con el patrón que acabamos de escribir, pero que no corresponda con un vídeo, voy a limitar la zona de la página donde usaré la expresión regular al bloque donde están los vídeos contenidos.


Observa que todos los vídeos de la página están dentro de una zona que empieza con un <div> concreto, y acaban en una zona delimitada por la siguiente que también empieza con otro <div> concreto.



(aquí están todos los vídeos…)



Para delimitar el área de trabajo de la expresión regular a la zona entre estos dos tags, leo la página con read y la guardo en la variable “data”



data = plugintools.read( params.get("url") )

para inmediatamente después quedarme con la parte donde están los vídeos utilizando el patrón <div id=”video_main_promos_inner”>(.*?)<div id=”content_index_navigation”>. Que viene a ser “todo lo que está entre el primer div y el segundo”.



data = plugintools.find_single_match( data , '<div id="video_main_promos_inner">(.*?)<div id="content_index_navigation">')

Ya tengo todo lo necesario para escribir la función “disneyweb” que extrae los vídeos y los añade a la lista de items de XBMC:



67 # Show all videos from the official website
68 def disneyweb(params):
69 plugintools.log("disneyjunior.disneyweb "+repr(params))
70
71 # Fetch video list page
72 data = plugintools.read( params.get("url") )
73 data = plugintools.find_single_match( data , '<div id="video_main_promos_inner">(.*?)<div id="content_index_navigation">')
74
75 # Extract items
76 '''
77 <div class="promo" style="background-image: url(/cms_res/disney-junior/images/promo_support/promo_holders/video.png);">
78 <a href="/disney-junior/contenido/video/canta_con_dj_arcoiris.jsp " class="promoLinkTracking"><img src="/cms_res/disney-junior/images/video/canta_dj_arco_iris_164x104.jpg" class="promo_image" alt=""/></a>
79 <div class="promo_title_3row"><p>Canta con DJ: La canción del arco iris</p></div>
80 <a class="playlist_button_large" href="" ref="canta_con_dj_arcoiris"><img src="/cms_res/disney-junior/images/promo_support/playlist_add_icon.png" alt="" /></a>
81 </div>
82 '''
83 pattern = '<div class="promo"[^<]+'
84 pattern += '<a href="([^"]+)"[^<]+'
85 pattern += '<img src="([^"]+)"[^<]+'
86 pattern += '</a[^<]+'
87 pattern += '<div[^<]+'
88 pattern += '<p>([^<]+)</p>'
89 matches = plugintools.find_multiple_matches(data,pattern)

En este punto tengo en la variable “matches” la lista de videos, donde cada elemento de la lista es a su vez una lista con url, imagen y título, sólo falta añadirlos a la lista de items con la función add_item de Plugin Tools.




91 for scrapedurl, scrapedthumbnail, scrapedtitle in matches:
92 # Not the better way to parse XML, but clean and easy
93 title = scrapedtitle
94 thumbnail = urlparse.urljoin( params.get("url") , scrapedthumbnail )
95 url = urlparse.urljoin( params.get("url") , scrapedurl.strip() )
96 plot = ""
97
98 # Appends a new item to the xbmc item list
99 plugintools.add_item( action="disneyweb_play" , title=title , plot=plot , url=url ,thumbnail=thumbnail , isPlayable=True, folder=False )

Observa que la URL y la imagen (o thumbnail) se añaden usando primero la función estándar de Python “urljoin”, lo que viene bien cuando te encuentras con URL relativas en lugar de absolutas. Esta función crea una URL utilizando el primer parámetro como URL base, y el segundo como ruta relativa.


Y observa también que el elemento que añadimos a la lista de items no es una carpeta (folder=False), porque cuando el usuario seleccione uno de estos items simplemente se reproducirá el vídeo con la función “disneyweb_play”. No va a ser necesario que el usuario descienda más niveles, y por eso lo marcamos como “no carpeta”.



Reproducción del vídeo


Cuando el usuario elija un vídeo de la lista anterior, XBMC llamará a la acción “disneyweb_play” y le pasará como parámetro la URL del vídeo elegido entre otras cosas.


Lo primero que hay que hacer es leer esa URL para tener la información del vídeo, usando la función read nuevamente:



101 # Play one video from the official website
102 def disneyweb_play(params):
103 plugintools.log("disneyjunior.disneyweb_play "+repr(params))
104
105 # Fetch page
106 data = plugintools.read( params.get("url") )

Y ahora utilizar esa información para deducir la URL que hay que darle al reproductor de XBMC. En este caso es fácil porque la URL utiliza el protocolo “RTMPE”, y está en el código HTML separada en dos líneas diferentes de forma bastante clara:



config.firstVideoSource = 'winnie_heffa_woozles.mp4';
config.htmlContainerName = 'video_player';
config.loop = false;
config.autoplay = true;
config.rtmpeServer = 'rtmpe://cp121902.edgefcs.net/ondemand/';

Así que sólo tenemos que extraer los dos fragmentos y componerlos para tener la URL completa.



108 url_start = plugintools.find_single_match( data , "config.rtmpeServer \= '([^']+)'")
109 plugintools.log("disneyjunior.disneyweb_play url_start="+url_start)
110
111 url_end = plugintools.find_single_match( data , "config.firstVideoSource \= '([^']+)'")
112 plugintools.log("disneyjunior.disneyweb_play url_end="+url_end)
113
114 url = url_start + url_end
115 plugintools.log("disneyjunior.disneyweb_play url="+url)
116
117 plugintools.play_resolved_url( url )

Con play_resolved_url le decimos a XBMC que está listo para la reproducción, así que directamente empezará a verse el vídeo.


Próxima entrega


La extensión de esta entrada me obliga a dividirla en dos, todavía queda ver cómo montar la sección del canal de YouTube aunque resulta similar a la del ejemplo que vimos al principio de esta serie de tutoriales.


También veremos cómo funciona el buscador y la configuración, pero si has llegado hasta aquí y quieres echarle un vistazo al add-on terminado descargándotelo desde este enlace.



http://www.mimediacenter.info/descargas/plugin.video.disneyjunior-1.0.0.zip



Si no tienes hijos en edad de La Casa de Mickey Mouse, pónselo a tus sobrinos. Seguro que sus padres te preguntan cómo pueden instalárselo ellos también :)






via Mi media center http://www.mimediacenter.info/2013/01/12/como-programar-add-ons-en-xbmc-un-add-on-mas-completo/

Comentaris

Entrades populars d'aquest blog

15 Tutoriales CSS3 para mejorar tus paginas web

15 Tutoriales CSS3 para mejorar tus paginas web : Les dejo una pequeña recopilación de tutoriales CSS3 que espero sean de utilidad para ustedes, intentamos hacer una recopilación bastante completa para crear impresionantes diseños web con CSS3 y aprovechar las bondades de CSS3 incluso para aplicar efectos, son un total de 15 tutoriales CSS3 gratis . Crear menu dropdown con CSS3 Crear breadcrumbs con estilo Transiciones de paginas con CSS3 Crear timeline con CSS3 y jQuery Reproductor de video con HTML5, CSS3 y jQuery Crear efecto acordion CSS3 Aplicar degradado a texto Crear texto en curva con CSS3 y jQuery Aplicar textura a texto con Magic Pill Crear slider de imagenes con CSS3 y jQuery Rotar texto con CSS3 Crear menu vertical con CSS3 Crear formulario con HTML5 y CSS3 Crear efecto de imagenes apiladas con CSS3 Aplicar estilos para imagenes con CSS3  

Averiguar la Salud del Disco Duro, con Crystal Disk Info [Windows]

Averiguar la Salud del Disco Duro, con Crystal Disk Info [Windows] : El actual “cuello de botella” en nuestras PCs; es decir, donde todo el rendimiento de nuestra PC llega a estancarse , es en el Disco Duro. Si bien los procesadores han evolucionado considerablemente en velocidad / rendimiento, el RAM no sólo es más económico, sino más veloz, y las tarjetas de video siguen innovando con cada generación, los discos duros han permanecido idénticos desde hace años, limitados por la física. Y es que un disco duro tradicional sólo tiene un máximo de velocidad con el que puede girar (medido en revoluciones por minuto, o RPM) que, a su vez, limita la velocidad de lectura y escritura. En pocas palabras, a pesar de que nuestras PCs pueden procesar información mucho más rápido que hace 5 años, los discos duros siguen leyendo (y escribiendo) esta información prácticamente a la misma velocidad. Esto ha cambiado con la llegada de los SSD, los Discos de Estado Sólo que no están limitados por la velo...

Learn Composition from the Photography of Henri Cartier-Bresson

“Do you see it?” This question is a photographic mantra. Myron Barnstone , my mentor, repeats this question every day with the hopes that we do “see it.” This obvious question reminds me that even though I have seen Cartier-Bresson’s prints and read his books, there are major parts of his work which remain hidden from public view. Beneath the surface of perfectly timed snap shots is a design sensibility that is rarely challenged by contemporary photographers. Henri Cartier-Bresson. © Martine Franck Words To Know 1:1.5 Ratio: The 35mm negative measures 36mm x 24mm. Mathematically it can be reduced to a 3:2 ratio. Reduced even further it will be referred to as the 1:1.5 Ratio or the 1.5 Rectangle. Eyes: The frame of an image is created by two vertical lines and two horizontal lines. The intersection of these lines is called an eye. The four corners of a negative can be called the “eyes.” This is extremely important because the diagonals connecting these lines will form the breakdown ...