Avanzando despliegue
This commit is contained in:
parent
6e1dd63d5a
commit
095025ce87
11 changed files with 167 additions and 12 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
__pycache__/
|
||||
pics/
|
||||
.DS_Store
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ Web de la acampada del 26S en Madrid. HTML y CSS, sin JavaScript.
|
|||
|
||||
1. `site/` va a `/var/www/acampada26s.net`. `deploy/nginx.conf` y `deploy/hub.conf` a nginx.
|
||||
2. `/c` es el HUB del pub OASIS, en `127.0.0.1:3000` de la misma máquina.
|
||||
3. Directo: `/c`. Comunicados: `/c?type=posts` (los blogposts públicos del HUB).
|
||||
4. Fotos subidas a mano: `tools/limpia-img.sh` antes, siempre.
|
||||
3. Cuadros: Directo `/c/feed/CUENTA_ACAMPADA` y Comunicados `/c/blogs/CUENTA_ACAMPADA`. Botón OASIS del pie: `/c/wiki/WIKI_DIDAC`.
|
||||
4. Cambiar `CUENTA_ACAMPADA` en `site/index.html` por la id de la cuenta de la acampada codificada (`@` → `%40`, `/` → `%2F`, `=` → `%3D`). Esa cuenta tiene que tener activada la visibilidad clearnet en OASIS y el pub tiene que seguirla. Cambiar `WIKI_DIDAC` por el slug de la página wiki de Didac (el de su URL dentro de `/c`); Didac tiene que tener activada la visibilidad clearnet de wikis. En la rama master de OASIS la página por cuenta es `/c/inhabitant/<ID>?type=feed` y `?type=posts`; si el pub corre esa versión, cambiar ahí las dos rutas.
|
||||
5. Banner: fotos en `pics/` (fuera de git), `python3 tools/collage.py` regenera `site/img/banner.webp`, `banner-movil.webp` y `og.jpg` (la imagen de 1200x630 que usan las redes al compartir el enlace). Necesita Pillow.
|
||||
6. Indexado: `site/robots.txt` y `site/sitemap.xml` llevan el dominio escrito a mano; cambiarlo si cambia el dominio. En la cabecera de `site/index.html` están el canonical, las etiquetas Open Graph y el descubrimiento de RSS.
|
||||
7. RSS: los sirve el HUB en `/c/rss/posts` (comunicados) y `/c/rss/feed` (directo), con los nombres de módulo de OASIS. Su sitemap propio es `/c/sitemap.xml`.
|
||||
8. Fotos subidas a mano: `tools/limpia-img.sh` antes, siempre.
|
||||
|
||||
Código AGPL-3.0. Textos CC BY-SA 4.0. Tipografía Anton, OFL.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ img { max-width: 100%; height: auto; display: block; }
|
|||
|
||||
a { color: inherit; }
|
||||
|
||||
.w { max-width: 1100px; margin: 0 auto; padding: 0 16px; }
|
||||
.w { max-width: 1100px; margin: 0 auto; padding-inline: 16px; }
|
||||
|
||||
h1, h2, h3, .cuadro, .bt {
|
||||
font-family: var(--tit);
|
||||
|
|
@ -72,6 +72,8 @@ h3 { font-size: 26px; margin: 0 0 6px; }
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.collage img { width: 100%; }
|
||||
|
||||
.botonera {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
|
|
@ -105,7 +107,8 @@ h3 { font-size: 26px; margin: 0 0 6px; }
|
|||
box-shadow: 0 2px 0 var(--sombra);
|
||||
}
|
||||
|
||||
footer { margin: 64px 0 24px; border-top: var(--bd); padding-top: 12px; font-size: 14px; }
|
||||
footer { margin: 64px 0 24px; border-top: var(--bd); padding-top: 16px; }
|
||||
footer p { margin: 0; font-size: 14px; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.cuadros { grid-template-columns: 1fr; }
|
||||
|
|
|
|||
BIN
site/img/banner-movil.webp
Normal file
BIN
site/img/banner-movil.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
BIN
site/img/banner.webp
Normal file
BIN
site/img/banner.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
BIN
site/img/colectivos/krakenslab.webp
Normal file
BIN
site/img/colectivos/krakenslab.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
BIN
site/img/og.jpg
Normal file
BIN
site/img/og.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
|
|
@ -6,7 +6,23 @@
|
|||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self'; img-src 'self'; font-src 'self'; base-uri 'none'; form-action 'none'">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<title>Acampada 26S</title>
|
||||
<meta name="description" content="Directo y comunicados de la acampada del 26S en Madrid.">
|
||||
<meta name="description" content="Comunicados, directo y colectivos de la acampada del 26S en Madrid.">
|
||||
<link rel="canonical" href="https://acampada26s.net/">
|
||||
<meta name="theme-color" content="#ff5f1f">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="Acampada 26S">
|
||||
<meta property="og:locale" content="es_ES">
|
||||
<meta property="og:url" content="https://acampada26s.net/">
|
||||
<meta property="og:title" content="Acampada 26S">
|
||||
<meta property="og:description" content="Comunicados, directo y colectivos de la acampada del 26S en Madrid.">
|
||||
<meta property="og:image" content="https://acampada26s.net/img/og.jpg">
|
||||
<meta property="og:image:type" content="image/jpeg">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:image:alt" content="Fotos de la acampada en la Puerta del Sol">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<link rel="alternate" type="application/rss+xml" title="Acampada 26S · Comunicados" href="/c/rss/posts">
|
||||
<link rel="alternate" type="application/rss+xml" title="Acampada 26S · Directo" href="/c/rss/feed">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="preload" href="/fonts/anton.woff2" as="font" type="font/woff2" crossorigin>
|
||||
|
|
@ -14,19 +30,21 @@
|
|||
<body>
|
||||
<header>
|
||||
<h1 class="w">Acampada 26S</h1>
|
||||
<picture class="collage">
|
||||
<source media="(max-width: 640px)" srcset="/img/banner-movil.webp">
|
||||
<img src="/img/banner.webp" alt="Fotos de la acampada en la Puerta del Sol" width="2200" height="660">
|
||||
</picture>
|
||||
<nav aria-label="Principal">
|
||||
<ul class="cuadros">
|
||||
<li><a class="cuadro" href="/c">Directo</a></li>
|
||||
<li><a class="cuadro" href="/c?type=posts">Comunicados</a></li>
|
||||
<li><a class="cuadro" href="/c/feed/CUENTA_ACAMPADA">Directo</a></li>
|
||||
<li><a class="cuadro" href="/c/blogs/CUENTA_ACAMPADA">Comunicados</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="w" aria-label="Redes">
|
||||
<ul class="botonera">
|
||||
<li><a class="bt" href="https://play.google.com/store/apps/details?id=com.solarnethub.oasis" rel="noopener noreferrer">OASIS</a></li>
|
||||
<li><a class="bt" href="/c/wiki/WIKI_DIDAC">OASIS</a></li>
|
||||
<li><a class="bt" href="https://mastodon.social/@26s" rel="noopener noreferrer">Mastodon</a></li>
|
||||
<li><a class="bt" href="https://t.me/acampada26s" rel="noopener noreferrer">Telegram</a></li>
|
||||
<!-- <li><a class="bt" href="https://www.instagram.com/acampada26s/" rel="noopener noreferrer">Instagram</a></li> -->
|
||||
<!-- <li><a class="bt" href="https://x.com/acampada26s" rel="noopener noreferrer">X</a></li> -->
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
@ -64,13 +82,14 @@
|
|||
<li><details><summary><img src="/img/colectivos/cjc.webp" alt="" width="48" height="48" loading="lazy"><span class="n">CJC</span><span class="d">Colectivos de Jóvenes Comunistas</span></summary><ul class="redes"><li><a href="https://www.instagram.com/cjcmadrid/" rel="noopener noreferrer">Instagram</a></li><li><a href="https://juventudcomunista.es/" rel="noopener noreferrer">Web</a></li></ul></details></li>
|
||||
<li><details><summary><img src="/img/colectivos/frente-estudiantes.webp" alt="" width="48" height="48" loading="lazy"><span class="n">Frente de Estudiantes</span><span class="d">Sindicato estudiantil</span></summary><ul class="redes"><li><a href="https://www.instagram.com/fde_madrid/" rel="noopener noreferrer">Instagram</a></li><li><a href="https://x.com/fde_madrid" rel="noopener noreferrer">X</a></li><li><a href="https://frentedeestudiantes.es/" rel="noopener noreferrer">Web</a></li></ul></details></li>
|
||||
<li><details><summary><img src="/img/colectivos/palestina.webp" alt="" width="48" height="48" loading="lazy"><span class="n">Acampada x Palestina</span><span class="d">Solidaridad con Palestina</span></summary><ul class="redes"><li><a href="https://www.instagram.com/acampadaxpalestinamadrid/" rel="noopener noreferrer">Instagram</a></li><li><a href="https://x.com/acampadamad" rel="noopener noreferrer">X</a></li></ul></details></li>
|
||||
<li><details><summary><img src="/img/colectivos/krakenslab.webp" alt="" width="48" height="48" loading="lazy"><span class="n">Kräkens.LAb</span><span class="d">Hacktivistas</span></summary><ul class="redes"><li><a href="https://krakenslab.com/" rel="noopener noreferrer">Web</a></li><li><a href="https://solarnethub.com/" rel="noopener noreferrer">SolarNET.HuB</a></li></ul></details></li>
|
||||
</ul>
|
||||
<p>¿Falta tu colectivo? Escribe a la comisión de comunicación en OASIS.</p>
|
||||
<p>¿Falta tu colectivo? Escribe a la comisión de comunicación en OASIS o a <a href="mailto:acampada26s@riseup.net">acampada26s@riseup.net</a>.</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="w">
|
||||
Textos CC BY-SA 4.0 · Código AGPL-3.0 · <a href="https://gitea.laenre.net/hacklab/26s" rel="noopener noreferrer">Fuente</a>
|
||||
<p>Textos CC BY-SA 4.0 · Código AGPL-3.0 · <a href="https://gitea.laenre.net/hacklab/26s" rel="noopener noreferrer">Fuente</a> · <a href="/c/rss/posts">RSS</a></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
5
site/robots.txt
Normal file
5
site/robots.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://acampada26s.net/sitemap.xml
|
||||
Sitemap: https://acampada26s.net/c/sitemap.xml
|
||||
12
site/sitemap.xml
Normal file
12
site/sitemap.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://acampada26s.net/</loc>
|
||||
<lastmod>2026-09-30</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://acampada26s.net/c</loc>
|
||||
<changefreq>hourly</changefreq>
|
||||
</url>
|
||||
</urlset>
|
||||
110
tools/collage.py
Normal file
110
tools/collage.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Collage del banner: pics/ -> site/img/banner.webp, banner-movil.webp y og.jpg.
|
||||
|
||||
Cada celda es (archivo, x, y, ancho, alto[, centro]) en píxeles de la salida. La
|
||||
foto se recorta para cubrir la celda (como object-fit: cover); `centro` es (cx, cy)
|
||||
entre 0 y 1, por defecto (0.5, 0.5). Entre celdas hay una línea negra a juego con
|
||||
el borde de la web. La salida no lleva metadatos.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
RAIZ = Path(__file__).resolve().parent.parent
|
||||
PICS = RAIZ / "pics"
|
||||
IMG = RAIZ / "site" / "img"
|
||||
NEGRO = (0, 0, 0)
|
||||
CALIDAD = 80
|
||||
|
||||
# Las cuatro fotos son apaisadas (ratios 1.02 a 1.53), así que las celdas también.
|
||||
|
||||
# Escritorio: 2200x660 (se muestra a 1100x330). Una fila, línea de 6px (3px a 2x).
|
||||
ESCRITORIO = {
|
||||
"archivo": "banner.webp",
|
||||
"tamano": (2200, 660),
|
||||
"linea": 6,
|
||||
"celdas": [
|
||||
("banner4", 0, 0, 620, 660, (0.45, 0.55)),
|
||||
("banner1", 620, 0, 600, 660),
|
||||
("banner3", 1220, 0, 550, 660),
|
||||
("banner5", 1770, 0, 430, 660, (0.5, 0.4)),
|
||||
],
|
||||
}
|
||||
|
||||
# Móvil: 800x640. Dos filas de 320, línea de 4px.
|
||||
MOVIL = {
|
||||
"archivo": "banner-movil.webp",
|
||||
"tamano": (800, 640),
|
||||
"linea": 4,
|
||||
"celdas": [
|
||||
("banner4", 0, 0, 400, 320, (0.45, 0.55)),
|
||||
("banner1", 400, 0, 400, 320),
|
||||
("banner5", 0, 320, 400, 320, (0.5, 0.35)),
|
||||
("banner3", 400, 320, 400, 320),
|
||||
],
|
||||
}
|
||||
|
||||
# Para compartir en redes (og:image): 1200x630, el tamaño que esperan las plataformas.
|
||||
OG = {
|
||||
"archivo": "og.jpg",
|
||||
"tamano": (1200, 630),
|
||||
"linea": 4,
|
||||
"celdas": [
|
||||
("banner4", 0, 0, 600, 315, (0.45, 0.55)),
|
||||
("banner1", 600, 0, 600, 315),
|
||||
("banner5", 0, 315, 600, 315, (0.5, 0.35)),
|
||||
("banner3", 600, 315, 600, 315),
|
||||
],
|
||||
}
|
||||
|
||||
SALIDAS = (ESCRITORIO, MOVIL, OG)
|
||||
|
||||
def foto(nombre):
|
||||
for ext in ("png", "jpg", "jpeg", "webp"):
|
||||
p = PICS / f"{nombre}.{ext}"
|
||||
if p.exists():
|
||||
im = Image.open(p)
|
||||
im = ImageOps.exif_transpose(im)
|
||||
return im.convert("RGB")
|
||||
sys.exit(f"falta pics/{nombre}.(png|jpg|jpeg|webp)")
|
||||
|
||||
|
||||
def compone(spec):
|
||||
ancho, alto = spec["tamano"]
|
||||
lienzo = Image.new("RGB", (ancho, alto), NEGRO)
|
||||
linea = spec["linea"]
|
||||
for nombre, x, y, w, h, *resto in spec["celdas"]:
|
||||
centro = resto[0] if resto else (0.5, 0.5)
|
||||
# Línea negra: media a cada lado de la celda, y en los bordes exteriores nada.
|
||||
x0 = x + (linea // 2 if x > 0 else 0)
|
||||
y0 = y + (linea // 2 if y > 0 else 0)
|
||||
x1 = x + w - (linea - linea // 2 if x + w < ancho else 0)
|
||||
y1 = y + h - (linea - linea // 2 if y + h < alto else 0)
|
||||
trozo = ImageOps.fit(foto(nombre), (x1 - x0, y1 - y0), Image.LANCZOS, centering=centro)
|
||||
lienzo.paste(trozo, (x0, y0))
|
||||
salida = IMG / spec["archivo"]
|
||||
if salida.suffix == ".jpg":
|
||||
lienzo.save(salida, "JPEG", quality=85, optimize=True, progressive=True)
|
||||
else:
|
||||
lienzo.save(salida, "WEBP", quality=CALIDAD, method=6)
|
||||
return salida
|
||||
|
||||
|
||||
def limpio(ruta):
|
||||
im = Image.open(ruta)
|
||||
if im.getexif():
|
||||
return False
|
||||
return not any(k in im.info for k in ("exif", "xmp", "icc_profile"))
|
||||
|
||||
|
||||
def main():
|
||||
IMG.mkdir(parents=True, exist_ok=True)
|
||||
for spec in SALIDAS:
|
||||
salida = compone(spec)
|
||||
estado = "sin metadatos" if limpio(salida) else "OJO: con metadatos"
|
||||
print(f"{salida.relative_to(RAIZ)} {salida.stat().st_size // 1024} KB {estado}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue