diff --git a/build.py b/build.py index c53c4b6..1720a48 100644 --- a/build.py +++ b/build.py @@ -9,11 +9,13 @@ 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 +CHANNEL = 'UC8gVtzB3Lqm7nnfMhMvXGxQ' +# YouTube separa cada tipo en su propia lista: UULF = solo vídeos, UUSH = solo Shorts +FEED_VIDEOS = os.environ.get('FEED_VIDEOS_URL', f'https://www.youtube.com/feeds/videos.xml?playlist_id=UULF{CHANNEL[2:]}') +FEED_SHORTS = os.environ.get('FEED_SHORTS_URL', f'https://www.youtube.com/feeds/videos.xml?playlist_id=UUSH{CHANNEL[2:]}') +VIDEOS_SHOWN = 8 # además del destacado +SHORTS_SHOWN = 12 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'] @@ -28,21 +30,27 @@ def fetch(url, timeout=20): return urllib.request.urlopen(req, timeout=timeout).read() +def read_feed(url): + root = ET.fromstring(fetch(url)) + out = [] + for e in root.findall('a:entry', NS)[:15]: + d = e.find('a:published', NS).text[:10].split('-') + out.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]}', + 'desc': e.find('m:group/m:description', NS).text or '', + }) + return out + + try: - root = ET.fromstring(fetch(FEED)) + videos, shorts = read_feed(FEED_VIDEOS), read_feed(FEED_SHORTS) 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, shorts = videos[:VIDEOS_SHOWN + 1], shorts[:SHORTS_SHOWN] -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]}', - 'desc': e.find('m:group/m:description', NS).text or '', - }) def sync_thumbs(vids): img_dir = os.path.join(OUT, 'img') @@ -65,18 +73,14 @@ def sync_thumbs(vids): return ok -videos = sync_thumbs(videos) +ok = sync_thumbs(videos + shorts) +okids = {v['id'] for v in ok} +videos = [v for v in videos if v['id'] in okids] +shorts = [v for v in shorts if v['id'] in okids] 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 - - # --- Temas: hashtags de las descripciones de YouTube ------------------------------------------- STOP = {'podcast', 'podcastclips', 'andaquenosabesna', 'andaquenosabesna', 'aqnsn', 'shorts', 'short', 'fyp', 'viral', 'parati', 'galaxoide', 'youtube', 'video', 'clips'} @@ -114,10 +118,10 @@ def topics(vids): return out + PINNED -chips = ''.join(f'
  • {html.escape(t)}
  • ' for t in topics(videos)) +chips = ''.join(f'
  • {html.escape(t)}
  • ' for t in topics(videos + shorts)) 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 destacado es el último vídeo (no Short) def url(v): @@ -127,12 +131,21 @@ def url(v): def card(v, n): return f''' - + {esc(v['title'])} ''' +def short_card(v, n): + return f''' + + + {esc(v['title'])} + ''' + + cards = ''.join(card(v, i) for i, v in enumerate(rest)) +scards = ''.join(short_card(v, i) for i, v in enumerate(shorts)) page = f''' @@ -155,7 +168,8 @@ page = f'''
    Anda que no sabes ná