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  = '([^<]+)'
          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  = '([^<]+)([^<]+)'
              patronvideos += ''
              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  = '([^<]+)'
          patronvideos += '[^]+/>([^<]+)<'
          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.

Deja un comentario

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