"""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''' Anda Que No Sabes Ná · El programa nocturno 100% granaíno
Anda que no sabes ná
Anda Que No Sabes Ná

Directo desde Granada, de noche

El programa nocturno
100% granaíno

Charlas sin guion sobre lo que sabemos, lo que no, y lo que creemos saber.

Últimos vídeos

Todos los vídeos →
{cards}

De qué va

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.

Síguenos

''' tmp = os.path.join(OUT, 'index.html.tmp') open(tmp, 'w', encoding='utf-8').write(page) shutil.move(tmp, os.path.join(OUT, 'index.html')) print('index.html', len(page), 'bytes;', len(videos), 'videos; destacado:', featured['id'])