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 🙂

19 comentarios

  1. Gracias. Sigo con mucho interés este tutorial. Tenía ganas de empezar con algo práctico. Saludos.

  2. Gracias a tí por el comentario 🙂

    Cuesta un montón prepararlo, espero que te sirva.

  3. Muy buen tutorial!! pero se me hace muy complicado! no por flojera sino porq hay muchas cosas q no entiendo! digamos q no soy muy cuando se trata de hacer estas cosas… pero el to esta muy bueno, y aunque no entiendo mucho , se nota q esta bien explicado! Felicitaciones man! tremendo trabajo

  4. Gracias 🙂

    Es complicado si no tienes conocimientos de programación, pero si los tienes y coges un add-on ya hecho para modificarlo no es tan difícil.

    Si quieres hacer un addon propio léete los tutoriales desde el principio, coge uno sencillito y modifícalo con el sitio que te interese.

    En caso de que no te funcione postéalo aquí (o en el foro) y te intento echar una mano.

  5. Buenas yo quiero saber donde meto las url ejemplo quiero coger mis canales del plus usar la url y ponerla pero nose donde ponerla porque si lo conseguimos podemos verlo sin problemas de que nos fallen los canales cuando mas lo necesitamos de esta forma y estando pendiente podemos estar seguro de que no tendremos problema y así tener lo que todos queremos ,a mi si me ayudan un poco yo hago lo que sea por tener nuestro plugin como toca y compartirlo entre todos nosotros un salaudo .

  6. Yo creo que lo que necesitas es el add-on «Livestream»…

    http://forum.xbmc.org/showthread.php?tid=97116

  7. Héctor Segarra 06/06/2013 en 17:52

    Lo tengo pero desinstale para poner frodo 12,2 , y yo diría que falla más
    Entonces puse de nuevo el frodo 12.00 y ahora me falla
    Mucho cosa que antes no me pasaba , quería saber si alguien
    Me puede ayudar y revisarlo por Team mismo para saber que es lo que pasa

  8. Si los canales funcionan, Livestream no tendría que fallar… en cualquier caso lo mejor es usar siempre la última versión estable de XBMC.

    La verdad es que no uso ese add-on 🙁

  9. Los canales para Livestream se añaden por medio de un archivo xml muy fácil de editar. El problema viene a la hora de dumpear el streaming para obtener la dirección rtmp de la señal de video.

    Lo mejor es pasarse por el foro xbmc: http://forum.xbmc.org/showthread.php?tid=120418

  10. Ante todo gracias Jesus por tomarte el trabajo de elaborar estos excelente tutoriales explicando paso a paso cada línea, sobre todo a lo que se refiere a las expresiones regulares que al principio cuando uno veía esa parte del código en verdad desanimaba un poco hasta que comencé a leer tu tutorial, además de colocar a disposición tu librería plugin tools que facilita la elaboración de los add-on.
    Bueno me he colocado ya a practicar un poco con una página y hasta ahora tengo las siguientes dudas:

    1. He logrado que extraer, generar la lista y reproducir algunos videos en mp4 pero algunos videos no se reproducen, después con ayuda plugintools.log y el log de xbmc descubrí que los video que fallan son los que tiene espacio en el nombre del archivo .mp4. ¿Cómo hago para que plugintools.play_resolved_url( url ) reproduzca video con espacio en el nombre?

    2. En algunas página en Firefox cuan doy mostrar código fuente de la página no muestra la zona donde están los videos, pero si selecciono la zona donde están los video y le dio ver código fuente seleccionado si muestra el código que necesito, yo creo porque está escrito utilizando javascript, hay alguna forma de capturar esta zona con data = plugintools.read( params.get(«url») )

    Saludos

  11. Gracias 🙂

    La respuesta a la 1 es fácil, en http no puedes poner espacios en blanco en una URL. Sustituye los espacios en blanco por «%20» de esta manera para que funcione:

    «http://1.2.3.4/una prueba.mp4»
    pasa a ser «http://1.2.3.4/una%20prueba.mp4»

    Esto se llama urlencode y también se aplica a caracteres especiales, pero normalmente no hay que preocuparse mucho.

    En cuanto a la 2 ya depende de cada sitio. Una técnica habitual es usar ajax para descargar los contenidos, de forma que la página tiene el «esqueleto» y luego se van descargando las páginas interiores. Si te instalas Firebug podrás ver las llamadas que se hacen, y si buscas el título de un vídeo usando el buscador de Firebug enseguida encontrarás en que página está.

  12. Gracias por tu pronta y acertada repuesta 🙂 me ha funcionado remplazar los espacio con %20 ahora todos los video se reproducen, en cuanto el otro problema no conozco nada ajax y apenas estoy aprendiendo python que por cierto me gustado este lenguaje para poder crear los add–on, pero al menos ya tengo por donde comenzar a investigar para resolver el problemas gracias por la orientación.

  13. Esto trucos valen oro 😀 en otra parte de los tutoriales te preguntaba sobre el problema que tenía al no poder obtener el contenido que me interesaba porque utilizaba javascript, bueno con este ejemplo práctico y tu explicación de cómo se comunica el navegador con el servidor, firebug y la función read_body_and_headers el logrado obtener lo que necesitaba… con firebug averigue exactamente la url donde hacer la petición así como los valores y cabecera para hacer las petición post con la nueva función read_body_and_headers.
    Mucha gracias¡¡

  14. Me alegro de haber ayudado 🙂

    Firebug es fundamental para estas cosas, y si usas Chrome tiene algo muy parecido en la opción de «Herramientas para desarrolladores».

  15. Instale el Plugim pero no me muestra nada en pagina web disney; hayque modificar algo?

  16. La página de Disney Junior ha cambiado desde que escribí esta entrada, por eso no funcionará. Hay que actualizar los patrones.

  17. Hola, soy nuevo en esto, he instalado el programa Xbmc y desde sistema/ajuste/addon he metido el .zip de live Streampro, pelis a la carta y alguno más.
    el que me interesa es el Live Stream porque quiero ver el canal +
    así no me funciona, he visto que tengo que meter una url, he intentado con varias que he visto en los foros pero me da error y sigo sin ver los canales.
    si me pudieseis ayudar os lo agradecería

    muchas gracias
    sl2

  18. Hola alguien pue de hacer un addons de esta página
    tutvgratis.tv
    son canales en vivo del cable

  19. Amigos gracias a todos por contestar . Siento no haber leído esta información antes juarrox gracias a todos los compañeros pero ahí me comento el rtm quiero sacar . De esta forma supongamos que tenemos todos una smartv en mi caso la tengo bien puesta te deja añadir canales iptv bien pues ahora quiero compartir mi lista con alguno de vosotros eso es lo que quiero hacer

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *