kodi-89cd0aba8869.py (Código fuente)

#!/usr/bin/env python3

# (c) 2016 Jesús Cea Avión - jcea@jcea.es
# This code is licensed under AGPLv3.

from collections import namedtuple
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import email.utils
import smtplib
import sqlite3
import os
from hashlib import sha256
import xml.etree.ElementTree as ET
import random
import cbor
import pickle
import paramiko
from datetime import datetime,timedelta
import time


import os.path, configparser
dirname = os.path.dirname(__file__)
config = configparser.ConfigParser()
config.read(os.path.join(dirname, 'config.ini'), encoding='utf-8')

remitente = config['PUBLISH']['email']
servidor_correo = config['PUBLISH']['servidor_correo']
puerto_servidor_correo = config['PUBLISH']['puerto_servidor_correo']

usuario_correo = config['PUBLISH']['usuario_correo']
clave_correo = config['PUBLISH']['clave_correo']

use_tls = config.getboolean('PUBLISH', 'use_tls')

class ya_publicados:
    def __init__(self):
        self.path = os.path.join(dirname, 'kodi.pickle')
        with open(self.path, 'rb') as f:
            #data = f.read().split()
            #index_episodio, index_pelicula = map(int, data)

            self._ya_publicados = pickle.load(f)

        #self._ya_publicados = {
        #        'index_series':index_episodio,
        #        'index_peliculas':index_pelicula,
        #        'series':{},
        #        'peliculas':{}
        #    }

    def responsable(self, url):
        for path, user in [
                ('dav://XXX/XXXX/', 'jcea'),
                ('dav://XXX/XXXX/', 'jcea'),
                ('dav://XXX/XXXX/', 'XXXXXX'),
                ('dav://XXX/XXXX/', 'XXXXXX'),
            ]:

            if url[:len(path)] == path:
                return user

        raise KeyError('No encontramos el responsable de ' + url)

    def _tipo(self, thetvdb_id, imdb_id, temporada, episodio):
        if not (bool(thetvdb_id) ^ bool(imdb_id)):
            raise RuntimeError('Esto no debería pasar nunca')
        if imdb_id:
            return 'peliculas', imdb_id
        elif thetvdb_id:
            if not (temporada and episodio):
                raise RuntimeError('Esto no debería pasar nunca')
            return 'series', (thetvdb_id, int(temporada), int(episodio))

    def ya_publicado(self, responsable=None,
                 thetvdb_id=None, temporada=None, episodio=None,
                 imdb_id=None):
        tipo, id_ = self._tipo(thetvdb_id, imdb_id, temporada, episodio)

        if responsable not in self._ya_publicados[tipo]:
            return False
        return id_ in self._ya_publicados[tipo][responsable]

    def publicado(self, identificador, responsable=None,
                 thetvdb_id=None, temporada=None, episodio=None,
                 imdb_id=None):
        tipo, id_ = self._tipo(thetvdb_id, imdb_id, temporada, episodio)

        self._ya_publicados['index_' + tipo] = identificador
        if responsable not in self._ya_publicados[tipo]:
            self._ya_publicados[tipo][responsable] = set()
        self._ya_publicados[tipo][responsable].add(id_)
        self.save()

    def index_episodio(self):
        return self._ya_publicados['index_series']

    def index_pelicula(self):
        return self._ya_publicados['index_peliculas']

    def save(self):
        with open(self.path + '.NEW', 'wb') as f:
            pickle.dump(self._ya_publicados, f,  pickle.HIGHEST_PROTOCOL)
            f.flush()
            os.fsync(f.fileno())
        os.rename(self.path + '.NEW', self.path)

ya_publicados = ya_publicados()

def envia_email(mensaje):
    mensaje['From'] = remitente
    mensaje['To'] = 'XXXXXX@XXXXXX'
    mensaje['Date'] = email.utils.formatdate(localtime=True)

    m = smtplib.SMTP(servidor_correo, puerto_servidor_correo)
    m.ehlo()
    if use_tls:
        m.starttls()
        m.ehlo()  # Las capabilities pueden cambiar en TLS.
        if sha256(m.sock.getpeercert(True)).hexdigest().lower() != \
            '*********************************************' :
            raise RuntimeError('El certificado digital del servidor de correo no coincide')
    if usuario_correo:
        m.login(usuario_correo, clave_correo)

    m.sendmail(remitente, ['XXXXXX@XXXXXX'], mensaje.as_string())

comando = """
python3 -c 'import cbor,sqlite3,sys;
conn=sqlite3.connect("/home/osmc/.kodi/userdata/Database/MyVideos99.db");
capitulos=conn.execute("select
    episode.idEpisode,episode.c00,episode.c01,episode.c06,episode.c12,episode.c13,episode.c18,tvshow.c00,tvshow.c01,tvshow.c06,tvshow.c10,tvshow.c12
    from episode,tvshow where episode.idShow=tvshow.idShow and
    episode.idEpisode>?",
    ({index_episodio:d},)).fetchall();
peliculas=conn.execute("select
    idMovie,c00,c01,c02,c03,c04,c05,c06,c07,c08,c09,c11,c14,c15,c16,c19,c20,c22
    from movie where idMovie>?",
    ({index_pelicula:d},)).fetchall();
set_peliculas = [str(i[0]) for i in peliculas];
casting=conn.execute("select
        media_id, actor.name
        from actor_link join actor using (actor_id)
        where media_type=\\"movie\\" and media_id in (%s)
        order by media_id,cast_order" % \",".join([\"?\"]*len(set_peliculas)),
        set_peliculas).fetchall();
sys.stdout.buffer.write(cbor.dumps((capitulos,peliculas,casting)));'
""".replace('\n',' ').format(
        index_episodio=ya_publicados.index_episodio(),
        index_pelicula=ya_publicados.index_pelicula())

client = paramiko.client.SSHClient()
try:
    client.load_system_host_keys()
    with open('/home/jcea/.ssh/config') as f:
        config = paramiko.config.SSHConfig()
        config.parse(f)
    osmcpi = config.lookup('osmcpi')
    client.connect(osmcpi['hostname'], username=osmcpi['user'],
                   compress=True, timeout=30)
    stdin, stdout, stderr = client.exec_command(comando, timeout=30)
    err, out = b'', b''
    while True:
        if stdout.channel.recv_ready():
            out += stdout.read()
        if stderr.channel.recv_stderr_ready():
            err += stderr.read()
        if stdout.channel.exit_status_ready():
            break
        time.sleep(0.1)
    if err:
        raise RuntimeError('Error remoto: %s' %err)
    respuesta = cbor.loads(out)
finally:
    client.close()

episodio = namedtuple('episodio',
                      'identificador titulo resumen miniatura temporada episodio '
                      'url serie resumen_serie fanart epguide thetvdb')

pelicula = namedtuple('pelicula',
                      'identificador titulo resumen rresumen tagline '
                      'votos puntuacion guionista year miniaturas imdb '
                      'duracion genero director titulo_original trailer '
                      'fanart url')

capitulos, peliculas, casting = respuesta

for i in capitulos:
    ep = episodio(*i)

    index_episodio = ep.identificador
    responsable = ya_publicados.responsable(ep.url)

    subject = '(auto) STREAMING: %s: %dx%.2d %s [%s]' % (ep.serie,
            int(ep.temporada), int(ep.episodio), ep.titulo, responsable)
    print(ep.identificador, subject)
    if ya_publicados.ya_publicado(
            responsable=responsable,
            thetvdb_id=ep.thetvdb,
            temporada=ep.temporada,
            episodio=ep.episodio):
        print("Ya publicado")
        continue
    if input('¿Enviar? (y/N) ').lower() != 'y':
        continue

    url = 'https://thetvdb.com/?tab=series&lid=16&id=' + ep.thetvdb
    texto = ep.resumen + '\n\n' + ep.serie + '\n'
    texto += url + '\n' + ep.resumen_serie

    plain = MIMEText(texto)

    miniatura = ep.miniatura
    miniatura = miniatura[miniatura.find('http'):miniatura.rfind('<')]

    fanart = ET.fromstring('<?xml version="1.0"?><jcea>' + ep.fanart + '</jcea>')
    fanart = [i.text for i in fanart.findall('./thumb[@aspect="banner"]')
                     if 'season' not in i.attrib]
    if len(fanart):
        fanart = '<p><img src="%s"/></p>' % random.choice(fanart)
    else:
        fanart = ''

    texto = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>%s</title>
        <style type="text/css">
        body {margin: 0; padding: 0; min-width: 100%%!important;}
        .content {width: 100%%; max-width: 600px;}
        a {color: blue; text-decoration: none;}
        a:hover {text-decoration: underline;}
        </style>
    </head>
<body>
<table width="100%%" border="0" cellpadding="0" cellspacing="0">
<img style="margin:1em;float:right;" src="%s"/>
%s
<h3 style="clear:both;"><a href="%s">%s</a></h3>
%s
%s
</table>
</body></html>
''' %(subject, miniatura, ep.resumen, url, ep.serie, ep.resumen_serie, fanart)

    html = MIMEText(texto, 'html')

    msg = MIMEMultipart('alternative')
    msg.attach(plain)
    msg.attach(html)

    msg['Subject'] = subject
    envia_email(msg)

    ya_publicados.publicado(index_episodio,
                           responsable=responsable,
                           thetvdb_id=ep.thetvdb,
                           temporada=ep.temporada,
                           episodio=ep.episodio)

for i in peliculas:
    peli = pelicula(*i)

    index_pelicula = peli.identificador
    responsable = ya_publicados.responsable(peli.url)
    titulo = '%s (%s)' %(peli.titulo, peli.year)
    subject = '(auto) STREAMING: %s [%s]' % (titulo, responsable)
    print(peli.identificador, subject)
    if ya_publicados.ya_publicado(
            responsable=responsable,
            imdb_id=peli.imdb):
        print("Ya publicado")
        continue
    if input('¿Enviar? (y/N) ').lower() != 'y':
        continue

    miniaturas = ET.fromstring('<?xml version="1.0"?><jcea>' + peli.miniaturas + '</jcea>')
    miniatura = ''
    if len(miniaturas):
        miniatura = miniaturas[0].get('preview')
        if miniatura.startswith('http://assets.fanart.tv/fanart/') and \
           miniatura.endswith('/preview'):
            print("Reescribiendo URL de la imagen")
            miniatura = \
                miniatura[len('http://assets.fanart.tv/fanart/'):-len('/preview')]
            miniatura = 'http://assets.fanart.tv/preview/' + miniatura
        miniatura = '<img style="margin:1em;float:right;" src="%s"/>' % miniatura

    trailer_prefix = 'plugin://plugin.video.youtube/?action=play_video&videoid='
    trailer = ''
    if peli.trailer.startswith(trailer_prefix):
        trailer = '<a href="https://www.youtube.com/watch?v=%s">' \
                  '<img style="float:right; height:3.4em;' \
                  'margin-left:0.5em;" src="http://findicons.com/files/' \
                  'icons/51/capital_suite/113/film_reel.png"/></a>' % \
                  peli.trailer[len(trailer_prefix):]

    actores = ', '.join((i[1] for i in casting if i[0]==index_pelicula))

    t = datetime(1, 1, 1) + timedelta(seconds=int(peli.duracion))
    duracion = '%d:%0.2d:%0.2d' % (t.hour, t.minute, t.second)
    url = 'http://www.imdb.com/title/%s/' % peli.imdb
    texto = '%s (%s)\n%s\n\n' %(peli.titulo, peli.year, peli.tagline)
    texto += peli.rresumen + '\n\n' + peli.resumen + '\n\n'
    texto += '%s\n%s\n' % (peli.titulo_original, url)
    texto += 'Nota: %.1f/10 (%s votos)' % (float(peli.puntuacion), peli.votos)
    texto += ' - Duración: %s\n' % duracion
    texto += 'Director: %s\n' % peli.director
    texto += 'Guionista: %s\n' % peli.guionista
    texto += 'Género: %s\n' % peli.genero
    texto += 'Actores: %s\n' % actores

    plain = MIMEText(texto)

    texto = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>{titulo}</title>
        <style type="text/css">
        body {{margin: 0; padding: 0; min-width: 100%!important;}}
        .content {{width: 100%; max-width: 600px;}}
        a {{color: blue; text-decoration: none;}}
        a:hover {{text-decoration: underline;}}
        </style>
    </head>
<body>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
{miniatura}
<div style="font-size: 120%"><b><a href="{url}">{titulo}</a></b></div>
<div style="font-size: 80%">{titulo_original}</div>
{trailer}
<p>{rresumen}</p>
<p>{resumen}</p>
<div style="background-color: #e0e0e0; font-style: italic;">
{titulo_original} ({year})
<br/><span style="font-size: 80%">{tagline}</span>
<br/><b>Nota:</b> {puntuacion:.1f}/10 ({votos} votos)
- <b>Duración:</b> {duracion}
<br/><b>Director:</b> {director}
<br/><b>Guionista:</b> {guionista}
<br/><b>Género:</b> {genero}
<br/><b>Actores:</b> {actores}
</div>
</table>
</body></html>
'''.format(titulo=titulo, miniatura=miniatura, url=url, tagline=peli.tagline,
           rresumen=peli.rresumen, resumen=peli.resumen, trailer=trailer,
           titulo_original=peli.titulo_original, year=peli.year,
           director=peli.director, duracion=duracion,
           puntuacion=float(peli.puntuacion), votos=peli.votos,
           guionista=peli.guionista, genero=peli.genero, actores=actores)

    html = MIMEText(texto, 'html')

    msg = MIMEMultipart('alternative')
    msg.attach(plain)
    msg.attach(html)

    msg['Subject'] = subject

    envia_email(msg)

    ya_publicados.publicado(index_pelicula,
                            responsable=responsable,
                            imdb_id=peli.imdb)