"""Genera index.html a partir del feed RSS del canal (se ejecuta cada 15 min en el contenedor)."""
import html
import os
import shutil
import sys
import urllib.request
import xml.etree.ElementTree as ET
from PIL import Image, ImageStat
OUT = os.environ.get('SITE_DIR', 'public')
FEED = os.environ.get('FEED_URL', 'https://www.youtube.com/feeds/videos.xml?channel_id=UC8gVtzB3Lqm7nnfMhMvXGxQ')
MAX_VIDEOS = 15
NS = {'a': 'http://www.w3.org/2005/Atom', 'y': 'http://www.youtube.com/xml/schemas/2015'}
MESES = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
YT = 'https://www.youtube.com/@andaquenosabesna'
TT = 'https://www.tiktok.com/@andaquenosabesna'
IG = 'https://www.instagram.com/andaquenosabesna'
MAIL = 'andaquenosabesnada@gmail.com'
def fetch(url, timeout=20):
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 (aqnsn-site)'})
return urllib.request.urlopen(req, timeout=timeout).read()
try:
root = ET.fromstring(fetch(FEED))
except Exception as exc: # sin red o feed caído: se conserva la web anterior
print('feed no disponible, se mantiene index.html:', exc, file=sys.stderr)
sys.exit(0)
videos = []
for e in root.findall('a:entry', NS)[:MAX_VIDEOS]:
d = e.find('a:published', NS).text[:10].split('-')
videos.append({
'id': e.find('y:videoId', NS).text,
'title': e.find('a:title', NS).text,
'date': f'{int(d[2])} {MESES[int(d[1]) - 1]} {d[0]}',
})
def sync_thumbs(vids):
img_dir = os.path.join(OUT, 'img')
os.makedirs(img_dir, exist_ok=True)
keep = {'avatar.jpg', 'banner.jpg'}
ok = []
for v in vids:
path = os.path.join(img_dir, v['id'] + '.jpg')
if not os.path.exists(path):
try:
open(path, 'wb').write(fetch(f"https://i.ytimg.com/vi/{v['id']}/hqdefault.jpg"))
except Exception as exc:
print('miniatura no descargada', v['id'], exc, file=sys.stderr)
continue
keep.add(v['id'] + '.jpg')
ok.append(v)
for f in os.listdir(img_dir):
if f not in keep:
os.remove(os.path.join(img_dir, f))
return ok
videos = sync_thumbs(videos)
if not videos:
sys.exit(0)
def is_short(vid):
"""Los Shorts llevan barras laterales desenfocadas: poca textura en los laterales."""
im = Image.open(os.path.join(OUT, 'img', vid + '.jpg')).convert('L')
w, h = im.size
return ImageStat.Stat(im.crop((0, 0, int(w * .18), h))).stddev[0] < 25
esc = html.escape
featured, rest = videos[0], videos[1:] # el feed viene del más nuevo al más antiguo
def url(v):
return f"https://www.youtube.com/watch?v={v['id']}"
def card(v, n):
return f'''
{esc(v['title'])}'''
cards = ''.join(card(v, i) for i, v in enumerate(rest))
page = f'''
Un programa de noche, con tertulia y sin prisa. Cada semana entra gente distinta, con su oficio y su manía, y se habla de lo que sale: de cómo se educa a un adolescente, de la vida en las calles de El Cairo, de si el árabe es tan antiguo como dicen, de qué tienen en común las religiones o de cómo se hace un truco de cartas.