Temas generados a partir de los hashtags de YouTube

This commit is contained in:
Alex
2026-10-20 14:58:01 +02:00
parent de95532a89
commit c8189269e6
+45 -4
View File
@@ -1,6 +1,9 @@
"""Genera index.html a partir del feed RSS del canal (se ejecuta cada 15 min en el contenedor).""" """Genera index.html a partir del feed RSS del canal (se ejecuta cada 15 min en el contenedor)."""
import html import html
import os import os
import re
import unicodedata
from collections import Counter
import shutil import shutil
import sys import sys
import urllib.request import urllib.request
@@ -11,7 +14,8 @@ from PIL import Image, ImageStat
OUT = os.environ.get('SITE_DIR', 'public') OUT = os.environ.get('SITE_DIR', 'public')
FEED = os.environ.get('FEED_URL', 'https://www.youtube.com/feeds/videos.xml?channel_id=UC8gVtzB3Lqm7nnfMhMvXGxQ') FEED = os.environ.get('FEED_URL', 'https://www.youtube.com/feeds/videos.xml?channel_id=UC8gVtzB3Lqm7nnfMhMvXGxQ')
MAX_VIDEOS = 15 MAX_VIDEOS = 15
NS = {'a': 'http://www.w3.org/2005/Atom', 'y': 'http://www.youtube.com/xml/schemas/2015'} NS = {'a': 'http://www.w3.org/2005/Atom', 'y': 'http://www.youtube.com/xml/schemas/2015',
'm': 'http://search.yahoo.com/mrss/'}
MESES = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'] MESES = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
YT = 'https://www.youtube.com/@andaquenosabesna' YT = 'https://www.youtube.com/@andaquenosabesna'
TT = 'https://www.tiktok.com/@andaquenosabesna' TT = 'https://www.tiktok.com/@andaquenosabesna'
@@ -37,6 +41,7 @@ for e in root.findall('a:entry', NS)[:MAX_VIDEOS]:
'id': e.find('y:videoId', NS).text, 'id': e.find('y:videoId', NS).text,
'title': e.find('a:title', NS).text, 'title': e.find('a:title', NS).text,
'date': f'{int(d[2])} {MESES[int(d[1]) - 1]} {d[0]}', 'date': f'{int(d[2])} {MESES[int(d[1]) - 1]} {d[0]}',
'desc': e.find('m:group/m:description', NS).text or '',
}) })
def sync_thumbs(vids): def sync_thumbs(vids):
@@ -72,6 +77,44 @@ def is_short(vid):
return ImageStat.Stat(im.crop((0, 0, int(w * .18), h))).stddev[0] < 25 return ImageStat.Stat(im.crop((0, 0, int(w * .18), h))).stddev[0] < 25
# --- Temas: hashtags de las descripciones de YouTube -------------------------------------------
STOP = {'podcast', 'podcastclips', 'andaquenosabesna', 'andaquenosabesna', 'aqnsn', 'shorts', 'short', 'fyp',
'viral', 'parati', 'galaxoide', 'youtube', 'video', 'clips'}
LABELS = { # clave normalizada -> texto a mostrar (sinónimos incluidos)
'egipto': 'Egipto', 'egiptomania': 'Egipto', 'educacion': 'Educación', 'arabe': 'Lengua árabe',
'lenguas': 'Lenguas', 'religion': 'Religiones', 'religiones': 'Religiones', 'magia': 'Magia',
'burocracia': 'Burocracia', 'poligamia': 'Poligamia', 'economia': 'Economía',
'gastronomia': 'Gastronomía', 'comidaegipcia': 'Comida egipcia', 'robotica': 'Robótica',
'tecnologia': 'Tecnología', 'agricultura': 'Agricultura', 'curiosidades': 'Curiosidades',
}
PINNED = ['Cultura granaína', 'Entrevistas'] # siempre presentes
MAX_TOPICS = 12
def norm(tag):
t = unicodedata.normalize('NFKD', tag.lower())
return ''.join(c for c in t if not unicodedata.combining(c))
def topics(vids):
count, raw = Counter(), {}
for v in vids:
seen = set()
for tag in re.findall(r'#(\w+)', v['desc']):
k = norm(tag)
if k in STOP or k in seen:
continue
seen.add(k)
label = LABELS.get(k) or (tag[0].upper() + tag[1:])
raw[label] = label
count[label] += 1
ranked = [t for t, _ in sorted(count.items(), key=lambda kv: (-kv[1], kv[0]))]
out = [t for t in ranked if t not in PINNED][:max(MAX_TOPICS - len(PINNED), 0)]
return out + PINNED
chips = ''.join(f'<li>{html.escape(t)}</li>' for t in topics(videos))
esc = html.escape esc = html.escape
featured, rest = videos[0], videos[1:] # el feed viene del más nuevo al más antiguo featured, rest = videos[0], videos[1:] # el feed viene del más nuevo al más antiguo
@@ -157,9 +200,7 @@ page = f'''<!doctype html>
<section id="programa" class="about wrap reveal"> <section id="programa" class="about wrap reveal">
<h2>De qué va</h2> <h2>De qué va</h2>
<p>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.</p> <p>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.</p>
<ul class="chips" aria-label="Temas"> <ul class="chips" aria-label="Temas">{chips}</ul>
<li>Educación</li><li>Egipto</li><li>Lengua árabe</li><li>Religiones</li><li>Magia</li><li>Cultura granaína</li><li>Entrevistas</li>
</ul>
</section> </section>
<section id="seguir" class="follow wrap reveal"> <section id="seguir" class="follow wrap reveal">