Entradas etiquetadas como "pelisalacarta"

Guía de desarrollo de pelisalacarta: Componentes internos del plugin

Martes, abril 26th, 2011

Esta entrada forma parte de una serie.

Puedes leer la entrada anterior de la serie en “Cómo funciona la actualización automática”

Si te interesa no te pierdas la sección guías de desarrollo

Hacer un plugin de XBMC es muy sencillo, lo que queda patente si echamos un vistazo al escaso número de líneas de código de algunos de los plugins más populares. Otra cosa es que la documentación que hay es escasa, y no muy buena.

Para simplificar el desarrollo de canales en pelisalacarta añadí en las primeras versiones algunos módulos comunes, recogiendo operaciones básicas como la descarga de páginas o la actualización automática. De esta forma me evitaba tener que pensar en resolver problemas comunes, y de paso simplificaba la vida a los colaboradores en el desarrollo.

Ahora en la nueva rama 3.X para no liarse mucho con el funcionamiento en cada plataforma he agrupado un poco mejor los elementos.

El paquete “core”

En esta nueva versión los módulos independientes de la plataforma se han agrupado todos en un paquete “core”, para organizar el código fuente y simplificar las dependencias entre módulos.

Podemos encontrar módulos para resolver muchas de las necesidades básicas de pelisalacarta:

  • config: Permite leer parámetros de configuración de forma independiente de la plataforma donde se ejecuta el plugin. La magia consiste en identificar en qué plataforma está corriendo y abrir el módulo “config” apropiado en el paquete “platform”. No funciona mal, aunque a medida que se van añadiendo plataformas la cosa se complica.
  • logger: Un sistema sencillo para escribir información de depuración, pero que sorprendentemente también cambia mucho de una plataforma a otra.
  • downloadtools: Herramientas para descargar los ficheros, algo que en Python no está realmente bien resuelto. Este módulo tiene también funciones auxiliares interesantes para tratar el encoding.
  • library: La aportación de Jurrabi para añadir series a la biblioteca de XBMC.
  • samba: Un wrapper sobre smb/nmb, dos librerías de Python para tratar la conectividad con carpetas compartidas de Windows.
  • updater: Verifica cada vez que inicias el plugin si hay nuevas versiones, y en caso afirmativo las descarga. La descompresión del ZIP se hace con el módulo ziptools, cuyo códig saqué buscándolo en Google
  • xbmctools: Es donde está el menú que sale cuando eliges una película. Ha crecido tanto que hay que reescribirlo, no es un buen ejemplo de programación en Python :(
  • scrapertools: Básicamente está todo lo necesario para la descarga de las páginas, se encarga de cosas tan exóticas como cambiar las cabeceras, gestionar las cookies, envios POST/GET, etc. Todo lo necesario para hacer creer a los sitios que en realidad XBMC es un navegador estándar.

El paquete “pelisalacarta/channels”

Contiene un módulo Python (un fichero .py) para cada canal y un ficheros xml asociado a cada uno de ellos para la actualización automática individual. Se llama así en lugar de llamarse simplemente “channels” con la idea de que algún día pelisalacarta, tvalacarta y otros plugins podrían llegar a ocupar la misma base de código fuente.

El paquete “platform”

La capacidad multiplataforma de pelisalacarta se basa en dos aspectos:

  • Python: Es un lenguaje interpretado, que funciona en todas las plataformas gracias a que hay un intérprte para prácticamente cualquier sistema. Además es fácilmente empotrable dentro de otras aplicaciones, lo que lo convierte en una elección muy popular para hacer extensiones de sistemas existentes.
  • El paquete estándar “platform” que proporciona al plugin funciones comunes de forma que no es necesario conocer su implementación específica en cada plataforma.

Hay un directorio para cada plataforma, que es ejecutado de forma selectiva desde diferentes puntos del plugin, y que proporciona:

  • Programa principal: Es aquí donde se almacena el punto inicial de ejecución.
  • Configuración: Es donde de verdad se implementa el acceso a los parámetros de configuración, traducciones y a parámetros genéricos. Es importante especialmente la función “get_data_path” para obtener una ruta donde poder escribir ficheros, y la función “get_runtime_path” para saber cual es la ruta principal del plugin.
  • Logging: Desde un simple “print” en unas plataformas, hasta sistemas más exóticos como el de XBMC o Plex.

El paquete “server”

Este paquete ya estaba en versiones anteriores, y agrupa los conectores de los diferentes servidores. Es aquí donde está el importante módulo “servertools” que se encarga de la detección de vídeos en una página: Basta con que le des una cadena con una página HTML y te devuelve un listado de los vídeos que encuentra incluyendo además en qué servidor están alojados.

El directorio “resources”

Además de recoger las imágenes como en versiones anteriores, en este directorio se almacenan ahora los ficheros adicionales requeridos por las diferentes plataformas. Es como el cuarto trastero :)

El script build.xml

Un script de ANT se encarga de empaquetar pelisalacarta en cada una de sus versiones. Esto simplifica mucho el trabajo a la hora de preparar las distribuciones más habituales (xbmc), y hace posible el empaquetado de las versiones más exóticas como la de Plex Media Server, que tiene una estructura de directorios muy peculiar. Y aún queda trabajo para perfeccionarlo.

Guía de desarrollo de pelisalacarta: Cómo funciona la actualización automática

Domingo, abril 24th, 2011

Esta entrada forma parte de una serie.

Puedes leer la entrada anterior de la serie en “Análisis de un canal multiplataforma”

Puedes leer la entrada siguiente en “Componentes internos del plugin”

Si te interesa no te pierdas la sección guías de desarrollo

Pelisalacarta 3.0 incorpora un mecanismo que permite a cualquier colaborador con acceso al SVN actualizar un canal que ha dejado de funcionar, o incluso modificar la lista de canales para añadir canales nuevos o quitar los que no funcionen.

El mecanismo es muy sencillo, y se basa en que hay un fichero junto a cada canal que identifica el número de versión. Si tienes un canal versión “2″ en tu pelisalacarta y alguien sube un canal con versión “3″, el plugin lo descarga y actualiza automáticamente.

Por supuesto esto sólo es posible si el usuario ha marcado esta opción entre los parámetros de configuración del plugin.

Actualizar un canal

Supongamos que queremos arreglar un fallo en cinetube, porque algún cambio en la página ha hecho que deje de funcionar o simplemente porque queremos añadir alguna mejora. Los pasos que hay que dar son sencillos:

  • Abrir el fichero cinetube.py y hacer las modificaciones
  • Subirlo el fichero cinetube.py al Subversion antes de publicar cualquier cambio
  • Abrir el fichero cinetube.xml e incrementar el número de versión. No hay versiones parciales, es un simple contador. Opcionalmente puede ponerse la descripción del cambio realizado, aunque actualmente no se utiliza. Si no hay fichero xml asociado al canal será necesario crearlo, poniendo como indicador de versión el “1″.
  • Subir el fichero cinetube.xml al Subversion. En este momento estará disponible para que todo el mundo se lo descargue.

Añadir un canal

Si lo que quieres es añadir un canal, el proceso es el mismo pero tienes que incrementar la versión de la lista de canales:

  • Crear el fichero .py del canal y grabarlo en el directorio “pelisalacarta/channels”.
  • Modificar el fichero channelselector.py para añadir el canal, indicando si es genérico o no. Si es un canal nuevo inclúyelo también en el apartado de “últimos canales”.
  • Subir el fichero channelselector.py al Subversion antes de publicar.
  • Modificar el fichero channelselector.xml para incrementar el número de la versión.
  • Subirlo al Subversion. En este momento estará disponible para todo el mundo.
  • Cuando un pelisalacarta vea que hay una nueva versión de la lista de canales se la descargará, y cuando el usuario elija el nuevo canal se lo descargará igualmente.

Eliminar un canal

Si bien es algo excepcional, también es posible eliminar canales que ya no van a funcionar de la lista de canales.

  • Modificar el fichero channelselector.py y comentar el canal que no funciona
  • Subir el fichero channelselector.py al Subversion.
  • Modificar el fichero channelselector.xml para incrementar el número de la versión.
  • Subirlo al Subversion.

Guía de desarrollo de pelisalacarta: Análisis de un canal multiplataforma

Miércoles, abril 6th, 2011

Esta entrada forma parte de una serie.

Puedes leer la entrada anterior de la serie en “Cómo hacer canales multiplataforma”

Puedes leer la entrada siguiente de la serie en “Cómo funciona la actualización automática”

Si te interesa no te pierdas la sección guías de desarrollo

Para comprender mejor como funciona por dentro un canal multiplataforma en pelisalacarta, vamos a analizar paso a paso uno de los sencillos. He elegido el canal “seriematic”, una web de series con una estructura muy limpia que se refleja claramente en el código del módulo Python.
El canal seriematic puedes encontrarlo en esta dirección.

Cabecera

# -*- coding: utf-8 -*-
#------------------------------------------------------------
# pelisalacarta - XBMC Plugin
# Canal para seriematic
# http://blog.tvalacarta.info/plugin-xbmc/pelisalacarta/
#------------------------------------------------------------

Lo primero que te vas a encontrar es la cabecera donde se incluyen algunas líneas de comentario con la información sobre el módulo Python que estás viendo.

Es especialmente importante la primera línea, que le especifica al intérprete de Python y a los editores sobre la codificación que emplea el fichero para los caracteres internacionales. Si esta información no está indicada Python dará un error al ejecutar el módulo, y si está informada erróneamente los caracteres internacionales se mostrarán mal.

import urlparse,urllib2,urllib,re
import os, sys

A continuación se incluyen los paquetes estándar de Python más habituales, entre los que suelen encontrarse los de expresiones regulares (paquete re), manipulación de URL (urllib), así como utilidades de bajo nivel (paquetes os y sys).

try:
    from core import logger
    from core import config
    from core import scrapertools
    from core.item import Item
    from servers import servertools
except:
    # En Plex Media server lo anterior no funciona...
    from Code.core import logger
    from Code.core import config
    from Code.core import scrapertools
    from Code.core.item import Item

Los módulos del paquete “core” de pelisalacarta se encargan de las funciones comunes a todos los canales, y es donde se encuentra resuelto el problema de la multiplataforma.

Basta con que uses el módulo “logger” para escribir un log, o el módulo “config” para leer la configuración, y puedes olvidarte de cómo se resuelve cada uno de esos problemas en las diferentes plataformas.

CHANNELNAME = "seriematic"
DEBUG = True

def isGeneric():
    return True

A continuación unas pocas líneas de relleno (boilerplate code) que se ponen siempre. Una constante CHANNELNAME con el nombre del canal para simplificar el copy-paste de código entre canales, otra para usar en depuración, y la función “isGeneric” que permite identificar a este canal como multiplataforma.


Menú principal

def mainlist(item):
    logger.info("[seriematic.py] mainlist")

    itemlist = []
    itemlist.append( Item(channel=CHANNELNAME, title="Series",
      action="series", url="http://www.seriematic.com/series.php"))
    itemlist.append( Item(channel=CHANNELNAME, title="Miniseries",
      action="series", url="http://www.seriematic.com/miniseries.php"))
    itemlist.append( Item(channel=CHANNELNAME, title="Dibujos",
      action="series", url="http://www.seriematic.com/dibujos.php"))
    itemlist.append( Item(channel=CHANNELNAME, title="Anime",
      action="series", url="http://www.seriematic.com/manga.php"))

    return itemlist

El menú principal del canal se tiene que implementar en una función de nombre “mainlist”, que devuelve una lista Python con las entradas que deben mostrarse al usuario.

Cada entrada de esta lista consiste en un objeto “Item”, que es una representación más genérica del elemento “ListItem” de XBMC. En su versión más sencilla permite almacenar los valores básicos:

  • channel: Nombre del canal donde está la acción a realizar
  • action: Nombre de la función dentro del canal que realiza la acción cuando el usuario seleccione esta entrada
  • title: El texto que se debe mostrar en pantalla al usuario cuando se liste esta entrada
  • url: La URL asociada a esta entrada, si es necesario. Si la acción consiste en descargar un listado de categorías, en este atributo vendrá la URL.

    • Navegación

      Supongamos que el usuario elige ahora la entrada titulada “Series”. Como ese item tiene la acción “series”, ese es el nombre de la siguiente función que se ejecuta en el módulo Python.

      def series(item):
          logger.info("[seriematic.py] series")
      
          # Descarga la página
          data = scrapertools.cachePage(item.url)
      
          # Extrae las entradas
          patronvideos  = '<td><a href="([^"]+)">([^<]+)</a></td>'
          matches = re.compile(patronvideos,re.DOTALL).findall(data)
          if DEBUG: scrapertools.printMatches(matches)
      
          itemlist = []
          for match in matches:
              scrapedtitle = match[1]
              scrapedplot = ""
              scrapedurl = urlparse.urljoin(item.url,match[0])
              scrapedthumbnail = ""
      
              # Añade al listado
              itemlist.append( Item(channel=CHANNELNAME,
                action="episodios",
                title=scrapedtitle,
                url=scrapedurl ,
                thumbnail=scrapedthumbnail ,
                plot=scrapedplot ,
                folder=True) )
      
          return itemlist
      

      La función recibe como parámetro el item seleccionado por si quieres utilizar la URL para descargar la página, como ocurre en este caso.

      La página descargada analizada con una expresión regular para obtener los nombres de las series, y para cada coincidencia encontrada se añade un elemento a la lista de items marcando esta vez como acción “episodios”.

      A su vez la acción “episodios” vuelve a hacer un procesamiento similar, pero ahora se buscan episodios de la serie elegida utilizando la URL que ha venido como parámetro.

      def episodios(item):
          logger.info("[seriematic.py] episodios")
      
          # Descarga la página
          data = scrapertools.cachePage(item.url)
      
          # Extrae las entradas
          patronvideos  = '<\!-- Column 1 start -->(.*?)<\!-- Column 1 end -->'
          matches = re.compile(patronvideos,re.DOTALL).findall(data)
          if DEBUG: scrapertools.printMatches(matches)
      
          itemlist = []
          for elemento in matches:
              patronvideos  = '<tr><td>([^<]+)<a href="([^"]+)">([^<]+)'
              patronvideos += '</a></td></tr>'
              matches2 = re.compile(patronvideos,re.DOTALL).findall(elemento)
      
              for match in matches2:
                  scrapedtitle = match[0]+" "+match[2]
                  scrapedplot = ""
                  scrapedurl = urlparse.urljoin(item.url,match[1])
                  scrapedthumbnail = ""
      
                  # Añade al listado
                  itemlist.append( Item(channel=CHANNELNAME,
                    action="videos",
                    title=scrapedtitle ,
                    url=scrapedurl ,
                    thumbnail=scrapedthumbnail ,
                    plot=scrapedplot ,
                    folder=True) )
      
          return itemlist
      

      Observa que en este caso se aplican dos expresiones regulares en dos bucles “for” anidados. Esta técnica permite simplificar expresiones regulares complejas, aislando primero la zona del HTML donde está lo que te interesa para en una segunda pasada extraer los elementos.


      Los vídeos

      Cada entrada que devuelve la función “episodios” es un episodio de la serie, así que sólo falta recuperar las diferentes alternativas (o mirrors) de cada episodio. Esto se hace en la función “vídeos”.

      def videos(item):
      
          logger.info("[seriematic.py] videos")
      
          # Descarga la página
          data = scrapertools.cachePage(item.url)
      
          # Extrae las entradas
          patronvideos  = '<tr><td>([^<]+)<script type="text/javascript">'
          patronvideos += 'p1.\'([^\']+)\'\,\'V\'\)\;</script>'
          patronvideos += '<img src="[^"]+" alt="[^"]+"[^>]+/>([^<]+)<'
          matches = re.compile(patronvideos,re.DOTALL).findall(data)
          if DEBUG: scrapertools.printMatches(matches)
      
          itemlist = []
          for match in matches:
              scrapedtitle = match[0]+" "+match[2]
              scrapedplot = ""
              scrapedurl = match[1]
              scrapedthumbnail = ""
              server="Megavideo"
      
              # Añade al listado
              itemlist.append( Item(channel=CHANNELNAME,
                    action="play",
                    title=scrapedtitle ,
                    url=scrapedurl ,
                    server=server , folder=False) )
      
          return itemlist
      

      Esta función busca las diferentes alternativas de vídeo a reproducir, y las añade a la lista con tres diferencias básicas respecto a lo que hemos visto en el resto de casos:

      • server: Cada entrada es un vídeo alojado en un servidor, aunque en este canal sólo se encuentran vídeos de “Megavideo”. La URL corresponde con la de la página de Megavideo, y el parámetro server sirve para que el plugin sepa obtener el vídeo a partir de ella.
      • action: La acción para que el vídeo se reproduzca es “play”. No hace falta que pongas esta función, ya ha sido definida por tí para que no tengas que repetirla cada vez, aunque si añades una en el canal tendrá prioridad sobre la estándar.
      • folder: Estas entradas ya no tienen más navegación, así que dejan de ser “carpetas” para ser “archivos” siguiendo el símil de un sistema de ficheros.

Guía de desarrollo de pelisalacarta: Cómo hacer canales multiplataforma

Viernes, abril 1st, 2011

Esta entrada es la primera de una serie continuación de la serie original “Cómo añadir canales a pelisalacarta”.

Puedes leer la entrada siguiente de la serie en “Análisis de un canal multiplataforma”

Si te interesa no te pierdas la sección guías de desarrollo

La rama 3.X de pelisalacarta introduce el concepto de “canal genérico”, que a falta de un nombre mejor hace referencia a un canal que puede verse en cualquier plataforma y no sólo XBMC. Los canales genéricos se verán en WiiMC, Plex Media Server, Boxee, y en cualquier futura plataforma que soporte pelisalacarta.

pelisalacarta-wiimc-3

Cinetube en WiiMC

Hay 5 reglas muy sencillas a seguir para desarrollar canales y que sean compatibles con cualquier plataforma. Debes desarrollar un módulo Python (un fichero .py) teniendo en cuenta:

1) Dependencia: No debe haber dependencias con módulos de XBMC o cualquier otra plataforma.
2) IsGeneric: Tienes que añadir una función “isGeneric” que devuelva “True”.
3) Mainlist: La función que muestra el primer nivel de menús debe llamarse “mainlist”, algo que no era realmente obligatorio en versiones anteriores pero ahora es necesario.
4) Item: Cada entrada del plugin se representa con un objeto de clase “Item”, que tendrá:

  • El título, la imagen, la descripción y un fanart opcional.
  • La acción que debe ejecutarse si el usuario la selecciona (el nombre de la función), junto con la URL asociada en caso de que sea necesaria.
  • El atributo server si la entrada corresponde a un vídeo, junto con el código del servidor donde esté alojado. Consulta la lista completa en el directorio “servers” del código.
  • Un atributo folder a “True” si la entrada es navegable, o “False” si es un vídeo.

5) Itemlist: Cada función deberá devolver una lista de entradas de tipo “item” que el plugin debe interpretar.

Además de ser compatible con cualquier plataforma, una ventaja adicional de los canales genéricos es que no necesitas programar tus propias funciones para las operaciones normales del plugin. En particular hay dos funciones ya implementadas, que te simplificarán mucho la vida:

  • findvideos: Descarga la página desde la URL que le pasas, busca todos los vídeos de todos los servidores conocidos, y devuelve un listado de entradas. Estas entradas se muestran al usuario para que elija.
  • play: Partiendo de la URL del vídeo que le pasas, y del servidor que has especificado, se encarga de la reproducción. Básicamente, esta función es la que muestra las opciones de “Ver”, “Descargar”, etc.

Si todo esto te suena a chino creo que con un ejemplo lo verás mejor. Pero lo veremos en la próxima entrega :)