diff --git a/.env.example b/.env.example index a00dc26..8793a5e 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,8 @@ DATABASE_URL=postgresql://senza_paura_web:CLAVE@192.168.18.171:5432/senzapaura # --- Audio ------------------------------------------------------------------- # Directorio privado con los archivos completos. NO debe servirse como estático: -# es lo que se entrega solo a quien ha comprado. Los fragmentos de muestra viven -# aparte, en static/muestras/, y esos sí son públicos. +# es lo que se entrega solo a quien ha comprado. Las canciones completas de +# escucha viven a menor calidad en static/escucha/ y son públicas. MEDIA_DIR=./media/audio # --- Pagos (Stripe) ---------------------------------------------------------- diff --git a/README.md b/README.md index 090cf64..2a56773 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ clásica, rock y pop melódico—, se escucha desde un **reproductor persistente que no se corta al navegar, y se puede **comprar en packs de diez canciones** que se entregan como descarga en MP3. -El catálogo vive en Markdown dentro del repositorio: publicar una canción es -añadir un archivo. La base de datos guarda solo lo que generan las personas que -visitan la web —cuentas, carritos, pedidos y compras—. +El catálogo y el contenido editorial viven en PostgreSQL. Los Markdown de +`src/content` se conservan como fuente de importación y respaldo, pero el sitio +público y el panel leen el modelo relacional. ## Estado @@ -81,20 +81,22 @@ el botón de pagar avisa de que no hay pasarela. Sin clave de correo, en desarrollo **el código de acceso se imprime por consola**, así que se puede entrar en una cuenta sin configurar nada. -| Comando | Qué hace | -| --------------------- | --------------------------------------------- | -| `npm run dev` | Servidor de desarrollo (panel en `/admin`) | -| `npm run build` | Compila a `build/` (adapter-node) | -| `npm run preview` | Sirve la compilación de producción en `:4173` | -| `npm run check` | Comprobación de tipos | -| `npm run lint` | Prettier + ESLint | -| `npm run format` | Formatea el proyecto | -| `npm run test:unit` | Tests unitarios y de componente (Vitest) | -| `npm run test:e2e` | Compila y ejecuta los end-to-end (Playwright) | -| `npm test` | Todo lo anterior | -| `npm run db:generate` | Genera una migración a partir del esquema | -| `npm run db:migrate` | Aplica las migraciones pendientes | -| `npm run db:studio` | Abre el visor de la base de datos | +| Comando | Qué hace | +| ---------------------------------------- | ------------------------------------------------- | +| `npm run dev` | Servidor de desarrollo (panel en `/admin`) | +| `npm run build` | Compila a `build/` (adapter-node) | +| `npm run preview` | Sirve la compilación de producción en `:4173` | +| `npm run check` | Comprobación de tipos | +| `npm run lint` | Prettier + ESLint | +| `npm run format` | Formatea el proyecto | +| `npm run test:unit` | Tests unitarios y de componente (Vitest) | +| `npm run test:e2e` | Compila y ejecuta los end-to-end (Playwright) | +| `npm test` | Todo lo anterior | +| `npm run db:generate` | Genera una migración a partir del esquema | +| `npm run db:migrate` | Aplica las migraciones pendientes | +| `npm run db:migrate:check -- ` | Prueba migraciones y revierte la transacción | +| `npm run db:auditar-musica` | Comprueba códigos, créditos y streams tras migrar | +| `npm run db:studio` | Abre el visor de la base de datos | ## El panel @@ -121,9 +123,8 @@ que se desincronizaran nadie se enteraría hasta que el panel guardara en una columna que ya no está. La portada del panel dice qué hay y qué falta —capítulos sin publicar, géneros -sin ficha, marcas del glosario sin confirmar—, que es la pregunta que se hace -quien entra. Desde ahí se editan los capítulos, las fichas de género y los -prompts de IA. +sin ficha, artistas incompletos y canciones sin audio de escucha—. Desde ahí se +editan capítulos, géneros, artistas, créditos, versiones musicales y prompts. ### Los prompts @@ -473,14 +474,17 @@ los principios a las fichas es cambiar de cosa, no seguir leyendo. ## Dónde va el audio -Como el catálogo se vende, **el archivo completo no es público**: +Cada grabación tiene representaciones técnicas distintas. La escucha pública +contiene la canción completa a calidad reducida; el máster y la descarga de +compra siguen siendo privados. Todo va agrupado **por disco**, con el mismo nombre de carpeta en los tres árboles: ``` -media/audio//NN-.mp3 archivo completo — privado, nunca se sirve -static/muestras//NN-.mp3 fragmento — público, es lo que suena en la web +media/audio//NN-.wav máster o descarga — privado +static/escucha//NN-.mp3 canción completa a 96 kbps — pública +static/muestras//NN-.mp3 fragmento antiguo — legado temporal static/images//portada.jpg portada del disco static/images//.jpg portada propia de un tema static/images/sin-portada.svg reserva cuando no hay ninguna @@ -491,7 +495,7 @@ del álbum «Singles» que se genera solo. La `` del audio es lo que declara el campo `audio` de la canción, sin barra inicial: `como-si-nada/01-como-si-nada.mp3`. De ahí salen la ruta privada -y la de la muestra, así que no hay que escribirlas por duplicado. +y la copia de escucha, así que no hay que escribirlas por duplicado. El directorio privado se configura con `MEDIA_DIR`. Los detalles de formato, la convención de nombres y cómo sustituir los archivos de marcador de posición @@ -500,16 +504,17 @@ están en [`media/LEEME.md`](media/LEEME.md). ### Subir un tema sin tocar el disco El campo **Archivo de audio** del panel sube el máster a `media/audio/subidas/`. -Lo que el navegador no puede hacer —recortar la muestra y medir la duración— lo +Lo que el navegador no puede hacer —transcodificar la escucha y medir el audio— lo hace [`scripts/medios.mjs`](scripts/medios.mjs), que corre solo antes de `npm run dev` y de `npm run build`, y también a mano con `npm run medios`: coloca el archivo en la carpeta de su disco con el nombre de la convención, -reescribe la clave en el Markdown, genera el fragmento y rellena `duracion` si +reescribe la clave en el Markdown, genera la canción completa a 96 kbps y rellena `duracion` si estaba vacía. Necesita **ffmpeg** en el equipo de quien edita. Si falta, avisa de lo que no ha podido generar y sigue; no rompe el arranque ni el build. El servidor de -despliegue no lo necesita: las muestras están en el repositorio. +despliegue no lo necesita: los derivados están en el repositorio. El perfil se +define una sola vez en `scripts/config/audio.mjs`. Un archivo que se quede en `subidas/` sin ficha que lo reclame se avisa en cada arranque, para que no se pierda ahí. @@ -807,8 +812,8 @@ música sigue sonando mientras se recorre la web. Su estado es una única instancia de módulo ([`src/lib/player/player.svelte.ts`](src/lib/player/player.svelte.ts)), no un contexto de Svelte, precisamente por eso. -**Lo que suena siempre es el fragmento de muestra**, y la barra lo dice. El -archivo completo solo se obtiene comprando. +**Lo que suena es la canción completa en calidad de escucha.** La compra entrega +la representación privada de mayor calidad e incluye las versiones musicales. Qué hace: cola con orden propio, aleatorio (respetando el tema en curso), repetición de cola o de tema, búsqueda dentro del tema, volumen recordado entre @@ -920,9 +925,9 @@ Quien tiene cuenta puede puntuar un tema de 1 a 5 desde su ficha. La media y el número de votos se ven siempre, también sin entrar: saber cómo está valorado un tema no exige cuenta; lo que la exige es votar, para que un voto sea de alguien. -**No hace falta haber comprado el tema.** Lo que suena en la web es un fragmento, -y opinar sobre lo que se ha escuchado es el uso normal; exigir la compra -convertiría la valoración en una reseña de producto. +**No hace falta haber comprado el tema.** La web permite escucharlo completo en +calidad reducida, y opinar sobre lo que se ha escuchado es el uso normal; exigir +la compra convertiría la valoración en una reseña de producto. Un voto por cuenta y tema, y volver a votar reemplaza el anterior. Eso lo impone la **clave primaria compuesta** de la tabla `valoracion`, no el código de la @@ -1037,16 +1042,17 @@ npm run test:unit -- --run npm run test:e2e ``` -## El contenido se va a PostgreSQL +## El contenido en PostgreSQL Lo que hay hoy en `src/content/**` son registros disfrazados de documento, y eso tiene un coste concreto: cinco tablas apuntan a una canción por una cadena de texto sin clave foránea, así que **renombrar el slug de un tema deja huérfana una compra sin que salte ningún error**. -El modelo completo —41 tablas, con el porqué de cada decisión— está en -[`docs/modelo-de-datos.md`](docs/modelo-de-datos.md). Está entregado y pendiente -de verificación; no hay código escrito todavía. +El modelo completo, con el porqué de cada decisión, está en +[`docs/modelo-de-datos.md`](docs/modelo-de-datos.md). Las migraciones, el panel +y las lecturas públicas ya están implementados; los volcamientos totales quedan +reservados para bases sin actividad comercial. ## Despliegue diff --git a/despliegue/senzapaura.nginx.conf b/despliegue/senzapaura.nginx.conf index 1669673..fd75f55 100644 --- a/despliegue/senzapaura.nginx.conf +++ b/despliegue/senzapaura.nginx.conf @@ -53,7 +53,7 @@ server { # mientras el DNS no haya propagado del todo. server_name senzapaura.es www.senzapaura.es _; - # Los mp3 completos pesan varios megas; los fragmentos, cientos de kilos. + # Los MP3 de escucha completa pesan varios megas. client_max_body_size 20m; gzip on; @@ -79,7 +79,19 @@ server { try_files $uri =404; } - # Imágenes, fuentes, vídeo, los fragmentos de muestra y el panel. No llevan + # El audio de escucha es público y completo, pero de calidad reducida. Nginx + # lo entrega directamente y anuncia rangos para que el reproductor pueda + # buscar dentro de la pista sin descargarla de nuevo desde el principio. + location /escucha/ { + root /var/www/senzapaura/build/client; + access_log off; + expires 1d; + add_header Cache-Control "public"; + add_header Accept-Ranges "bytes" always; + try_files $uri =404; + } + + # Imágenes, fuentes, vídeo, las muestras antiguas y el panel. No llevan # huella, así que se cachean un día: lo justo para no pedirlos en cada # visita sin que un cambio tarde una semana en verse. location ~* ^/(images|background|video|muestras|fuentes|admin)/ { @@ -95,9 +107,9 @@ server { # # Incluidas /descargas/… —que comprueban la compra antes de entregar el # archivo—, la vuelta de Google y Facebook, y el webhook de Stripe. Ojo: - # las descargas NO se pueden servir desde Nginx; el audio completo vive - # fuera de `build/` justamente para que no haya forma de pedirlo por URL - # sin haberlo comprado. + # las descargas NO se pueden servir desde Nginx; el audio original o de alta + # calidad vive fuera de `build/` justamente para que no haya forma de + # pedirlo por URL sin haberlo comprado. # ----------------------------------------------------------------------- location / { proxy_pass http://127.0.0.1:3000; diff --git a/docs/modelo-de-datos.md b/docs/modelo-de-datos.md index cd986c6..4bd3360 100644 --- a/docs/modelo-de-datos.md +++ b/docs/modelo-de-datos.md @@ -1,14 +1,14 @@ # Modelo de datos -Diseño de la migración del contenido a PostgreSQL. **Fase 1 cerrada**: el -modelo está completo y las decisiones tomadas. No hay código escrito todavía. +Diseño y estado de la migración del contenido a PostgreSQL. El modelo base y +el dominio musical están implementados en `src/lib/server/db/schema` y en las +migraciones `0000`–`0014`. Versión presentable, con diagramas entidad-relación: -Este archivo es la especificación; el artefacto es la misma información puesta -para leerse. Cuando se implemente, la verdad pasa a ser -`src/lib/server/db/schema.ts` y esto queda como el porqué. +Este archivo conserva el porqué; la verdad ejecutable es +`src/lib/server/db/schema`. ## Por qué @@ -100,8 +100,14 @@ que hoy leen `$lib/content`. además lo correcto: la maqueta de 2018 no tiene la misma letra, le falta el puente. -- **`persona`** _(nueva)_ — nombre, nombre_legal, ipi. Hoy autores e intérpretes - son cadenas sueltas repetidas en cada archivo. +- **`persona`** — identidad física/legal para autoría, IPI y datos personales. +- **`artista`** — identidad pública con código estable, slug, tipo + (persona/grupo/proyecto), descripción, biografía, imagen, país y estado. +- **`artista_persona`** — vínculo opcional entre una identidad pública y las + personas físicas que la encarnan o integran. +- **`artista_identificador`** — identificadores externos por sistema. +- **`cancion_interprete`** — crédito de una grabación concreta a un artista, + con rol, instrumento, nombre acreditado y orden. - **`cancion_persona`** _(nueva)_ — rol (letra · musica · interprete · arreglos · produccion), orden. - ~~**`version`**~~ — **desaparece como tabla**: cada versión es una fila de @@ -415,8 +421,8 @@ subrayado. ### 6 · Medios -- **`medio`** _(nueva)_ — clase, clave, **`publico`** (los fragmentos sí, el - audio completo nunca; hoy eso lo garantiza la carpeta), mime, bytes, +- **`medio`** — clase, clave, **`publico`** (la copia completa de escucha sí; + másteres y descargas no), mime, bytes, duracion_s, ancho, alto, sha256, alt. Para que la administración pueda subir audio, un archivo tiene que ser una fila. @@ -430,7 +436,7 @@ no hay que tocar ninguna URL —la base del dominio es configuración, y descargas no usan URLs firmadas «porque el servidor ya tiene la sesión delante». En otro dominio la sesión **ya no va delante**: esa es justo la gracia. Así que: -- **Lo público** —portadas, muestras— se sirve directo y cacheado. +- **Lo público** —portadas y copias completas de escucha— se sirve directo y cacheado. - **El audio completo** pasa a necesitar **URL firmada con caducidad corta**: la firma la pone la aplicación después de comprobar la compra, y Nginx la valida con `secure_link`. Sin eso, el dominio de medios sería una puerta abierta al diff --git a/docs/plan-implementacion-dominio-musical.md b/docs/plan-implementacion-dominio-musical.md index dd137dc..b1d9c6a 100644 --- a/docs/plan-implementacion-dominio-musical.md +++ b/docs/plan-implementacion-dominio-musical.md @@ -11,6 +11,12 @@ principal y sus versiones musicales, pero dejará de confundir: - un intérprete y su ficha pública; - una representación técnica de audio para escucha, descarga o archivo. +> Estado a 10 de septiembre de 2026: implementadas las guardas, transacciones, +> migraciones aditivas, read models, fichas públicas, panel, pipeline de audio y +> nueva versión de las condiciones de uso. La activación en una base compartida +> requiere ejecutar `db:migrate` y después `db:medios`; las migraciones se han +> ensayado dentro de una transacción revertida. + El trabajo será incremental. Las migraciones serán aditivas hasta que las lecturas nuevas estén verificadas; ninguna columna ni relación actual se retirará en la misma entrega que introduce su sustituta. diff --git a/drizzle/0014_artistas_y_audio.sql b/drizzle/0014_artistas_y_audio.sql index d44519f..ff2196d 100644 --- a/drizzle/0014_artistas_y_audio.sql +++ b/drizzle/0014_artistas_y_audio.sql @@ -201,3 +201,18 @@ INNER JOIN persona p ON p.id = cp.persona_id INNER JOIN artista a ON a.slug = p.slug WHERE cp.rol = 'interprete' ON CONFLICT DO NOTHING; +--> statement-breakpoint + +-- El legado solo acreditaba la principal. Una versión empieza heredando una +-- copia de esos créditos, que después puede editarse de forma independiente. +INSERT INTO cancion_interprete ( + cancion_id, artista_id, rol, instrumento, acreditado_como, orden +) +SELECT v.id, ci.artista_id, ci.rol, ci.instrumento, ci.acreditado_como, ci.orden +FROM cancion v +INNER JOIN cancion_interprete ci ON ci.cancion_id = v.version_de_id +WHERE v.version_de_id IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM cancion_interprete propia WHERE propia.cancion_id = v.id + ) +ON CONFLICT DO NOTHING; diff --git a/drizzle/0015_condiciones_escucha_completa.sql b/drizzle/0015_condiciones_escucha_completa.sql new file mode 100644 index 0000000..1002742 --- /dev/null +++ b/drizzle/0015_condiciones_escucha_completa.sql @@ -0,0 +1,84 @@ +-- Publica una versión nueva de las condiciones: las versiones legales ya +-- vigentes no se modifican porque documentan lo aceptado en compras anteriores. +INSERT INTO version_legal ( + id, documento_id, cuerpo, vigente_desde, resumen_cambios +) +SELECT + 'legal-condiciones-uso-2026-09-10', + d.id, + $condiciones$ +> **Pendiente de revisión legal.** Es un borrador razonable, no un documento +> validado por un abogado. + +Usar esta web supone aceptar estas condiciones. Si no estás de acuerdo con +alguna, no la uses. + +## La cuenta + +Para comprar hace falta una cuenta, y para crearla basta un correo electrónico +al que llega un código. **No hay contraseña**: cada vez que entras se envía un +código nuevo. Eso significa que quien tenga acceso a tu correo tiene acceso a +tu cuenta, así que protégelo. + +Al crear la cuenta te comprometes a usar una dirección **tuya**. Escribir la de +otra persona para que le lleguen los códigos es un uso indebido, y el sistema +lo limita activamente. + +Puedes pedir que se cierre tu cuenta cuando quieras escribiendo a +hola@senzapaura.es. Las compras y los datos de facturación se conservan el +tiempo que obliga la ley, según se explica en la +[política de privacidad](/legal/privacidad). + +## Lo que suena y lo que se compra + +En la web se puede escuchar **la canción completa en una copia de calidad +reducida**. Esa copia es solo para escucharla desde el sitio: que sea accesible +no concede permiso para descargarla, redistribuirla ni reutilizarla. + +Al comprar recibes el archivo de mayor calidad disponible, con todas sus +versiones alternativas, y queda vinculado a tu cuenta: puedes volver a +descargarlo desde ella siempre que quieras. + +## Lo que no se puede hacer + +Con las canciones compradas: + +- Redistribuirlas, subirlas a ninguna plataforma ni compartirlas públicamente. +- Usarlas en vídeos, anuncios, espectáculos o cualquier producción. +- Incorporarlas a otra obra o hacer versiones para explotarlas. + +Todo eso requiere una licencia aparte. Los identificadores de registro están en +la ficha de cada tema para poder pedirla. + +Con la web: + +- Intentar acceder a partes que no te corresponden, o a los archivos originales + o de descarga sin haberlos comprado. +- Automatizar peticiones de forma que degraden el servicio. +- Usar el formulario de acceso o el de contacto para molestar a terceros. + +## El taller de letras + +Los textos del taller se publican para leerlos y aprender. Puedes usarlos para +escribir tus propias canciones —para eso están— pero no republicarlos como +propios ni venderlos. + +## Cambios + +Estas condiciones pueden cambiar. La fecha de arriba dice cuándo se revisaron +por última vez, y lo que se aplica a una compra son las condiciones vigentes en +el momento de hacerla. +$condiciones$, + TIMESTAMPTZ '2026-09-10 00:00:00+02', + 'La escucha pública pasa de un fragmento a la canción completa en calidad reducida.' +FROM documento_legal d +WHERE d.slug = 'condiciones-de-uso' +ON CONFLICT (id) DO NOTHING; +--> statement-breakpoint + +UPDATE documento_legal d +SET version_vigente_id = v.id +FROM version_legal v +WHERE d.slug = 'condiciones-de-uso' + AND v.id = 'legal-condiciones-uso-2026-09-10' + AND v.documento_id = d.id; diff --git a/drizzle/0016_sincronizar_migraciones_manuales.sql b/drizzle/0016_sincronizar_migraciones_manuales.sql new file mode 100644 index 0000000..060cee1 --- /dev/null +++ b/drizzle/0016_sincronizar_migraciones_manuales.sql @@ -0,0 +1,2 @@ +-- Sin cambios SQL. Punto de enlace creado por Drizzle antes de reconciliar las +-- migraciones manuales 0011–0015 con el esquema declarado. diff --git a/drizzle/0017_verificar_sincronizacion.sql b/drizzle/0017_verificar_sincronizacion.sql new file mode 100644 index 0000000..69b8da4 --- /dev/null +++ b/drizzle/0017_verificar_sincronizacion.sql @@ -0,0 +1,3 @@ +-- Sin cambios SQL. La instantánea asociada representa el esquema resultante de +-- 0011–0015; esas operaciones ya viven en sus migraciones manuales y repetirlas +-- aquí intentaría crear de nuevo las mismas tablas, columnas e índices. diff --git a/drizzle/meta/0016_snapshot.json b/drizzle/meta/0016_snapshot.json new file mode 100644 index 0000000..b7fc9ad --- /dev/null +++ b/drizzle/meta/0016_snapshot.json @@ -0,0 +1,6149 @@ +{ + "id": "89cab2aa-355f-45b2-ace8-7afc914b9b6d", + "prevId": "30625049-98ca-4735-bee2-28f9bd917f7a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.medio": { + "name": "medio", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "clase": { + "name": "clase", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "clave": { + "name": "clave", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "publico": { + "name": "publico", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "mime": { + "name": "mime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "duracion_s": { + "name": "duracion_s", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ancho": { + "name": "ancho", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alto": { + "name": "alto", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "medio_clase": { + "name": "medio_clase", + "columns": [ + { + "expression": "clase", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "medio_sha": { + "name": "medio_sha", + "columns": [ + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "medio_clave_unique": { + "name": "medio_clave_unique", + "columns": [ + "clave" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pais": { + "name": "pais", + "schema": "", + "columns": { + "codigo": { + "name": "codigo", + "type": "char(2)", + "primaryKey": true, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gentilicio": { + "name": "gentilicio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.persona": { + "name": "persona", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre_legal": { + "name": "nombre_legal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipi": { + "name": "ipi", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nacimiento": { + "name": "nacimiento", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "defuncion": { + "name": "defuncion", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "retrato_id": { + "name": "retrato_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "semblanza": { + "name": "semblanza", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "persona_nombre": { + "name": "persona_nombre", + "columns": [ + { + "expression": "nombre", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "persona_pais": { + "name": "persona_pais", + "columns": [ + { + "expression": "pais_codigo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "persona_pais_codigo_pais_codigo_fk": { + "name": "persona_pais_codigo_pais_codigo_fk", + "tableFrom": "persona", + "columnsFrom": [ + "pais_codigo" + ], + "tableTo": "pais", + "columnsTo": [ + "codigo" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "persona_retrato_id_medio_id_fk": { + "name": "persona_retrato_id_medio_id_fk", + "tableFrom": "persona", + "columnsFrom": [ + "retrato_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "persona_slug_unique": { + "name": "persona_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.album": { + "name": "album", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "estilo_id": { + "name": "estilo_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'album'" + }, + "fecha": { + "name": "fecha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portada_id": { + "name": "portada_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sello": { + "name": "sello", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "destacado": { + "name": "destacado", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "numero_catalogo": { + "name": "numero_catalogo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "formatos": { + "name": "formatos", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "pais_edicion_codigo": { + "name": "pais_edicion_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "album_estilo": { + "name": "album_estilo", + "columns": [ + { + "expression": "estilo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "album_fecha": { + "name": "album_fecha", + "columns": [ + { + "expression": "fecha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "album_estilo_id_estilo_id_fk": { + "name": "album_estilo_id_estilo_id_fk", + "tableFrom": "album", + "columnsFrom": [ + "estilo_id" + ], + "tableTo": "estilo", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "album_portada_id_medio_id_fk": { + "name": "album_portada_id_medio_id_fk", + "tableFrom": "album", + "columnsFrom": [ + "portada_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "album_pais_edicion_codigo_pais_codigo_fk": { + "name": "album_pais_edicion_codigo_pais_codigo_fk", + "tableFrom": "album", + "columnsFrom": [ + "pais_edicion_codigo" + ], + "tableTo": "pais", + "columnsTo": [ + "codigo" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "album_slug_unique": { + "name": "album_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.album_enlace": { + "name": "album_enlace", + "schema": "", + "columns": { + "album_id": { + "name": "album_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plataforma": { + "name": "plataforma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "album_enlace_album_id_album_id_fk": { + "name": "album_enlace_album_id_album_id_fk", + "tableFrom": "album_enlace", + "columnsFrom": [ + "album_id" + ], + "tableTo": "album", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "album_enlace_album_id_plataforma_pk": { + "name": "album_enlace_album_id_plataforma_pk", + "columns": [ + "album_id", + "plataforma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.autoria": { + "name": "autoria", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "porcentaje": { + "name": "porcentaje", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "autoria_cancion_id_cancion_id_fk": { + "name": "autoria_cancion_id_cancion_id_fk", + "tableFrom": "autoria", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "autoria_persona_id_persona_id_fk": { + "name": "autoria_persona_id_persona_id_fk", + "tableFrom": "autoria", + "columnsFrom": [ + "persona_id" + ], + "tableTo": "persona", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "autoria_cancion_id_persona_id_rol_pk": { + "name": "autoria_cancion_id_persona_id_rol_pk", + "columns": [ + "cancion_id", + "persona_id", + "rol" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion": { + "name": "cancion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version_de_id": { + "name": "version_de_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_nombre": { + "name": "version_nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_nota": { + "name": "version_nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_orden": { + "name": "version_orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "estilo_id": { + "name": "estilo_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "album_id": { + "name": "album_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "audio_id": { + "name": "audio_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "muestra_id": { + "name": "muestra_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duracion_s": { + "name": "duracion_s", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ano": { + "name": "ano", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "letra": { + "name": "letra", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idioma": { + "name": "idioma", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'es'" + }, + "inspiracion": { + "name": "inspiracion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instrumental": { + "name": "instrumental", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "destacada": { + "name": "destacada", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "portada_id": { + "name": "portada_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ala_venta": { + "name": "ala_venta", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "cancion_estilo": { + "name": "cancion_estilo", + "columns": [ + { + "expression": "estilo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cancion_album": { + "name": "cancion_album", + "columns": [ + { + "expression": "album_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cancion_version_de": { + "name": "cancion_version_de", + "columns": [ + { + "expression": "version_de_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cancion_destacada": { + "name": "cancion_destacada", + "columns": [ + { + "expression": "destacada", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "cancion_estilo_id_estilo_id_fk": { + "name": "cancion_estilo_id_estilo_id_fk", + "tableFrom": "cancion", + "columnsFrom": [ + "estilo_id" + ], + "tableTo": "estilo", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "cancion_album_id_album_id_fk": { + "name": "cancion_album_id_album_id_fk", + "tableFrom": "cancion", + "columnsFrom": [ + "album_id" + ], + "tableTo": "album", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "cancion_audio_id_medio_id_fk": { + "name": "cancion_audio_id_medio_id_fk", + "tableFrom": "cancion", + "columnsFrom": [ + "audio_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "cancion_muestra_id_medio_id_fk": { + "name": "cancion_muestra_id_medio_id_fk", + "tableFrom": "cancion", + "columnsFrom": [ + "muestra_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "cancion_portada_id_medio_id_fk": { + "name": "cancion_portada_id_medio_id_fk", + "tableFrom": "cancion", + "columnsFrom": [ + "portada_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cancion_slug_unique": { + "name": "cancion_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion_enlace": { + "name": "cancion_enlace", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plataforma": { + "name": "plataforma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_enlace_cancion_id_cancion_id_fk": { + "name": "cancion_enlace_cancion_id_cancion_id_fk", + "tableFrom": "cancion_enlace", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "cancion_enlace_cancion_id_plataforma_pk": { + "name": "cancion_enlace_cancion_id_plataforma_pk", + "columns": [ + "cancion_id", + "plataforma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion_etiqueta": { + "name": "cancion_etiqueta", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "etiqueta_id": { + "name": "etiqueta_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_etiqueta_cancion_id_cancion_id_fk": { + "name": "cancion_etiqueta_cancion_id_cancion_id_fk", + "tableFrom": "cancion_etiqueta", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "cancion_etiqueta_etiqueta_id_etiqueta_id_fk": { + "name": "cancion_etiqueta_etiqueta_id_etiqueta_id_fk", + "tableFrom": "cancion_etiqueta", + "columnsFrom": [ + "etiqueta_id" + ], + "tableTo": "etiqueta", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "cancion_etiqueta_cancion_id_etiqueta_id_pk": { + "name": "cancion_etiqueta_cancion_id_etiqueta_id_pk", + "columns": [ + "cancion_id", + "etiqueta_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion_persona": { + "name": "cancion_persona", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_persona_cancion_id_cancion_id_fk": { + "name": "cancion_persona_cancion_id_cancion_id_fk", + "tableFrom": "cancion_persona", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "cancion_persona_persona_id_persona_id_fk": { + "name": "cancion_persona_persona_id_persona_id_fk", + "tableFrom": "cancion_persona", + "columnsFrom": [ + "persona_id" + ], + "tableTo": "persona", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "cancion_persona_cancion_id_persona_id_rol_pk": { + "name": "cancion_persona_cancion_id_persona_id_rol_pk", + "columns": [ + "cancion_id", + "persona_id", + "rol" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credito_album": { + "name": "credito_album", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "album_id": { + "name": "album_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "credito_album_album": { + "name": "credito_album_album", + "columns": [ + { + "expression": "album_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "credito_album_album_id_album_id_fk": { + "name": "credito_album_album_id_album_id_fk", + "tableFrom": "credito_album", + "columnsFrom": [ + "album_id" + ], + "tableTo": "album", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credito_album_persona_id_persona_id_fk": { + "name": "credito_album_persona_id_persona_id_fk", + "tableFrom": "credito_album", + "columnsFrom": [ + "persona_id" + ], + "tableTo": "persona", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.estilo": { + "name": "estilo", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "principal": { + "name": "principal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "imagen_id": { + "name": "imagen_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "estilo_principal_unico": { + "name": "estilo_principal_unico", + "columns": [ + { + "expression": "principal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"estilo\".\"principal\"", + "concurrently": false + }, + "estilo_orden": { + "name": "estilo_orden", + "columns": [ + { + "expression": "orden", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "estilo_imagen_id_medio_id_fk": { + "name": "estilo_imagen_id_medio_id_fk", + "tableFrom": "estilo", + "columnsFrom": [ + "imagen_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "estilo_slug_unique": { + "name": "estilo_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.etiqueta": { + "name": "etiqueta", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "etiqueta_slug_unique": { + "name": "etiqueta_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registro_obra": { + "name": "registro_obra", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "iswc": { + "name": "iswc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isrc": { + "name": "isrc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entidad": { + "name": "entidad", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "numero": { + "name": "numero", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorial": { + "name": "editorial", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "registro_obra_cancion_id_cancion_id_fk": { + "name": "registro_obra_cancion_id_cancion_id_fk", + "tableFrom": "registro_obra", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.traduccion": { + "name": "traduccion", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idioma": { + "name": "idioma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "texto": { + "name": "texto", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "traduccion_cancion_id_cancion_id_fk": { + "name": "traduccion_cancion_id_cancion_id_fk", + "tableFrom": "traduccion", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "traduccion_cancion_id_idioma_pk": { + "name": "traduccion_cancion_id_idioma_pk", + "columns": [ + "cancion_id", + "idioma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.video": { + "name": "video", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "youtube_id": { + "name": "youtube_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "video_cancion": { + "name": "video_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "video_cancion_id_cancion_id_fk": { + "name": "video_cancion_id_cancion_id_fk", + "tableFrom": "video", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.acepcion": { + "name": "acepcion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "termino_id": { + "name": "termino_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "categoria": { + "name": "categoria", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "definicion": { + "name": "definicion", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ampliacion": { + "name": "ampliacion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ejemplo": { + "name": "ejemplo", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "acepcion_numero": { + "name": "acepcion_numero", + "columns": [ + { + "expression": "termino_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "numero", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "acepcion_termino_id_termino_id_fk": { + "name": "acepcion_termino_id_termino_id_fk", + "tableFrom": "acepcion", + "columnsFrom": [ + "termino_id" + ], + "tableTo": "termino", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.animo": { + "name": "animo", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gems": { + "name": "gems", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valencia": { + "name": "valencia", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "energia": { + "name": "energia", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "descripcion": { + "name": "descripcion", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "animo_codigo_unique": { + "name": "animo_codigo_unique", + "columns": [ + "codigo" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion_genero": { + "name": "cancion_genero", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_genero_cancion_id_cancion_id_fk": { + "name": "cancion_genero_cancion_id_cancion_id_fk", + "tableFrom": "cancion_genero", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "cancion_genero_genero_id_genero_id_fk": { + "name": "cancion_genero_genero_id_genero_id_fk", + "tableFrom": "cancion_genero", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "cancion_genero_cancion_id_genero_id_pk": { + "name": "cancion_genero_cancion_id_genero_id_pk", + "columns": [ + "cancion_id", + "genero_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.capitulo": { + "name": "capitulo", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "capitulo_tipo": { + "name": "capitulo_tipo", + "columns": [ + { + "expression": "tipo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "orden", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "capitulo_genero_id_genero_id_fk": { + "name": "capitulo_genero_id_genero_id_fk", + "tableFrom": "capitulo", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "capitulo_slug_unique": { + "name": "capitulo_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.capitulo_acepcion": { + "name": "capitulo_acepcion", + "schema": "", + "columns": { + "capitulo_id": { + "name": "capitulo_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "acepcion_id": { + "name": "acepcion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "automatico": { + "name": "automatico", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "confirmado": { + "name": "confirmado", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "capitulo_acepcion_capitulo_id_capitulo_id_fk": { + "name": "capitulo_acepcion_capitulo_id_capitulo_id_fk", + "tableFrom": "capitulo_acepcion", + "columnsFrom": [ + "capitulo_id" + ], + "tableTo": "capitulo", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "capitulo_acepcion_acepcion_id_acepcion_id_fk": { + "name": "capitulo_acepcion_acepcion_id_acepcion_id_fk", + "tableFrom": "capitulo_acepcion", + "columnsFrom": [ + "acepcion_id" + ], + "tableTo": "acepcion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "capitulo_acepcion_capitulo_id_acepcion_id_pk": { + "name": "capitulo_acepcion_capitulo_id_acepcion_id_pk", + "columns": [ + "capitulo_id", + "acepcion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compas": { + "name": "compas", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "numerador": { + "name": "numerador", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "denominador": { + "name": "denominador", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "compas_codigo_unique": { + "name": "compas_codigo_unique", + "columns": [ + "codigo" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero": { + "name": "genero", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "origen_lugar": { + "name": "origen_lugar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "origen_ano": { + "name": "origen_ano", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tempo": { + "name": "tempo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rima": { + "name": "rima", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metrica_nota": { + "name": "metrica_nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "compas_nota": { + "name": "compas_nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rasgos": { + "name": "rasgos", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "genero_nombre": { + "name": "genero_nombre", + "columns": [ + { + "expression": "nombre", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "genero_origen_ano": { + "name": "genero_origen_ano", + "columns": [ + { + "expression": "origen_ano", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "genero_slug_unique": { + "name": "genero_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_alias": { + "name": "genero_alias", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "genero_alias_nombre": { + "name": "genero_alias_nombre", + "columns": [ + { + "expression": "nombre", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "genero_alias_genero_id_genero_id_fk": { + "name": "genero_alias_genero_id_genero_id_fk", + "tableFrom": "genero_alias", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_alias_pais_codigo_pais_codigo_fk": { + "name": "genero_alias_pais_codigo_pais_codigo_fk", + "tableFrom": "genero_alias", + "columnsFrom": [ + "pais_codigo" + ], + "tableTo": "pais", + "columnsTo": [ + "codigo" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_compas": { + "name": "genero_compas", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "compas_id": { + "name": "compas_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "principal": { + "name": "principal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "genero_compas_genero_id_genero_id_fk": { + "name": "genero_compas_genero_id_genero_id_fk", + "tableFrom": "genero_compas", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_compas_compas_id_compas_id_fk": { + "name": "genero_compas_compas_id_compas_id_fk", + "tableFrom": "genero_compas", + "columnsFrom": [ + "compas_id" + ], + "tableTo": "compas", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "genero_compas_genero_id_compas_id_pk": { + "name": "genero_compas_genero_id_compas_id_pk", + "columns": [ + "genero_id", + "compas_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_instrumento": { + "name": "genero_instrumento", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instrumento_id": { + "name": "instrumento_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "importancia": { + "name": "importancia", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'habitual'" + }, + "papel": { + "name": "papel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "genero_instrumento_genero_id_genero_id_fk": { + "name": "genero_instrumento_genero_id_genero_id_fk", + "tableFrom": "genero_instrumento", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_instrumento_instrumento_id_instrumento_id_fk": { + "name": "genero_instrumento_instrumento_id_instrumento_id_fk", + "tableFrom": "genero_instrumento", + "columnsFrom": [ + "instrumento_id" + ], + "tableTo": "instrumento", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "genero_instrumento_genero_id_instrumento_id_pk": { + "name": "genero_instrumento_genero_id_instrumento_id_pk", + "columns": [ + "genero_id", + "instrumento_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_metro": { + "name": "genero_metro", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metro_id": { + "name": "metro_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "predominante": { + "name": "predominante", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "genero_metro_genero_id_genero_id_fk": { + "name": "genero_metro_genero_id_genero_id_fk", + "tableFrom": "genero_metro", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_metro_metro_id_metro_id_fk": { + "name": "genero_metro_metro_id_metro_id_fk", + "tableFrom": "genero_metro", + "columnsFrom": [ + "metro_id" + ], + "tableTo": "metro", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "genero_metro_genero_id_metro_id_pk": { + "name": "genero_metro_genero_id_metro_id_pk", + "columns": [ + "genero_id", + "metro_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_origen": { + "name": "genero_origen", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origen_id": { + "name": "origen_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "principal": { + "name": "principal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "genero_origen_origen": { + "name": "genero_origen_origen", + "columns": [ + { + "expression": "origen_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "genero_origen_genero_id_genero_id_fk": { + "name": "genero_origen_genero_id_genero_id_fk", + "tableFrom": "genero_origen", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_origen_origen_id_genero_id_fk": { + "name": "genero_origen_origen_id_genero_id_fk", + "tableFrom": "genero_origen", + "columnsFrom": [ + "origen_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "genero_origen_genero_id_origen_id_tipo_pk": { + "name": "genero_origen_genero_id_origen_id_tipo_pk", + "columns": [ + "genero_id", + "origen_id", + "tipo" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_pais": { + "name": "genero_pais", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": true + }, + "relacion": { + "name": "relacion", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'adoptado'" + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "genero_pais_genero_id_genero_id_fk": { + "name": "genero_pais_genero_id_genero_id_fk", + "tableFrom": "genero_pais", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_pais_pais_codigo_pais_codigo_fk": { + "name": "genero_pais_pais_codigo_pais_codigo_fk", + "tableFrom": "genero_pais", + "columnsFrom": [ + "pais_codigo" + ], + "tableTo": "pais", + "columnsTo": [ + "codigo" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "genero_pais_genero_id_pais_codigo_pk": { + "name": "genero_pais_genero_id_pais_codigo_pk", + "columns": [ + "genero_id", + "pais_codigo" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_persona": { + "name": "genero_persona", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "papel": { + "name": "papel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "genero_persona_genero_id_genero_id_fk": { + "name": "genero_persona_genero_id_genero_id_fk", + "tableFrom": "genero_persona", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_persona_persona_id_persona_id_fk": { + "name": "genero_persona_persona_id_persona_id_fk", + "tableFrom": "genero_persona", + "columnsFrom": [ + "persona_id" + ], + "tableTo": "persona", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "genero_persona_genero_id_persona_id_papel_pk": { + "name": "genero_persona_genero_id_persona_id_papel_pk", + "columns": [ + "genero_id", + "persona_id", + "papel" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_seccion": { + "name": "genero_seccion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "clave": { + "name": "clave", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "genero_seccion_clave": { + "name": "genero_seccion_clave", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "clave", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "genero_seccion_genero_id_genero_id_fk": { + "name": "genero_seccion_genero_id_genero_id_fk", + "tableFrom": "genero_seccion", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_tema": { + "name": "genero_tema", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "etiqueta_id": { + "name": "etiqueta_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "genero_tema_genero_id_genero_id_fk": { + "name": "genero_tema_genero_id_genero_id_fk", + "tableFrom": "genero_tema", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "genero_tema_etiqueta_id_etiqueta_id_fk": { + "name": "genero_tema_etiqueta_id_etiqueta_id_fk", + "tableFrom": "genero_tema", + "columnsFrom": [ + "etiqueta_id" + ], + "tableTo": "etiqueta", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "genero_tema_genero_id_etiqueta_id_pk": { + "name": "genero_tema_genero_id_etiqueta_id_pk", + "columns": [ + "genero_id", + "etiqueta_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.instrumento": { + "name": "instrumento", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "familia": { + "name": "familia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subfamilia": { + "name": "subfamilia", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "descripcion": { + "name": "descripcion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "afinacion": { + "name": "afinacion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "imagen_id": { + "name": "imagen_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "muestra_id": { + "name": "muestra_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "instrumento_familia": { + "name": "instrumento_familia", + "columns": [ + { + "expression": "familia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "instrumento_pais_codigo_pais_codigo_fk": { + "name": "instrumento_pais_codigo_pais_codigo_fk", + "tableFrom": "instrumento", + "columnsFrom": [ + "pais_codigo" + ], + "tableTo": "pais", + "columnsTo": [ + "codigo" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "instrumento_imagen_id_medio_id_fk": { + "name": "instrumento_imagen_id_medio_id_fk", + "tableFrom": "instrumento", + "columnsFrom": [ + "imagen_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "instrumento_muestra_id_medio_id_fk": { + "name": "instrumento_muestra_id_medio_id_fk", + "tableFrom": "instrumento", + "columnsFrom": [ + "muestra_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "instrumento_slug_unique": { + "name": "instrumento_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metro": { + "name": "metro", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "silabas": { + "name": "silabas", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "termino_id": { + "name": "termino_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "metro_codigo_unique": { + "name": "metro_codigo_unique", + "columns": [ + "codigo" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.modelo_ia": { + "name": "modelo_ia", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "familia": { + "name": "familia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vigente": { + "name": "vigente", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "nota_sintaxis": { + "name": "nota_sintaxis", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "esquema": { + "name": "esquema", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "modelo_ia_familia": { + "name": "modelo_ia_familia", + "columns": [ + { + "expression": "familia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "modelo_ia_codigo_unique": { + "name": "modelo_ia_codigo_unique", + "columns": [ + "codigo" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obra_referencia": { + "name": "obra_referencia", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ano": { + "name": "ano", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cita": { + "name": "cita", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "obra_referencia_genero": { + "name": "obra_referencia_genero", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "obra_referencia_genero_id_genero_id_fk": { + "name": "obra_referencia_genero_id_genero_id_fk", + "tableFrom": "obra_referencia", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "obra_referencia_slug_unique": { + "name": "obra_referencia_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obra_referencia_enlace": { + "name": "obra_referencia_enlace", + "schema": "", + "columns": { + "obra_id": { + "name": "obra_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plataforma": { + "name": "plataforma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "obra_referencia_enlace_obra_id_obra_referencia_id_fk": { + "name": "obra_referencia_enlace_obra_id_obra_referencia_id_fk", + "tableFrom": "obra_referencia_enlace", + "columnsFrom": [ + "obra_id" + ], + "tableTo": "obra_referencia", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "obra_referencia_enlace_obra_id_plataforma_pk": { + "name": "obra_referencia_enlace_obra_id_plataforma_pk", + "columns": [ + "obra_id", + "plataforma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obra_referencia_persona": { + "name": "obra_referencia_persona", + "schema": "", + "columns": { + "obra_id": { + "name": "obra_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "papel": { + "name": "papel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "obra_referencia_persona_obra_id_obra_referencia_id_fk": { + "name": "obra_referencia_persona_obra_id_obra_referencia_id_fk", + "tableFrom": "obra_referencia_persona", + "columnsFrom": [ + "obra_id" + ], + "tableTo": "obra_referencia", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "obra_referencia_persona_persona_id_persona_id_fk": { + "name": "obra_referencia_persona_persona_id_persona_id_fk", + "tableFrom": "obra_referencia_persona", + "columnsFrom": [ + "persona_id" + ], + "tableTo": "persona", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "obra_referencia_persona_obra_id_persona_id_papel_pk": { + "name": "obra_referencia_persona_obra_id_persona_id_papel_pk", + "columns": [ + "obra_id", + "persona_id", + "papel" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prompt": { + "name": "prompt", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "modelo_ia_id": { + "name": "modelo_ia_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "animo_id": { + "name": "animo_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idioma": { + "name": "idioma", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'en'" + }, + "contenido": { + "name": "contenido", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "notas": { + "name": "notas", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valoracion": { + "name": "valoracion", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ejemplo_id": { + "name": "ejemplo_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preferido": { + "name": "preferido", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "visibilidad": { + "name": "visibilidad", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'privado'" + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "prompt_genero": { + "name": "prompt_genero", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "prompt_modelo": { + "name": "prompt_modelo", + "columns": [ + { + "expression": "modelo_ia_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "prompt_animo": { + "name": "prompt_animo", + "columns": [ + { + "expression": "animo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "prompt_modelo_ia_id_modelo_ia_id_fk": { + "name": "prompt_modelo_ia_id_modelo_ia_id_fk", + "tableFrom": "prompt", + "columnsFrom": [ + "modelo_ia_id" + ], + "tableTo": "modelo_ia", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "prompt_genero_id_genero_id_fk": { + "name": "prompt_genero_id_genero_id_fk", + "tableFrom": "prompt", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "prompt_animo_id_animo_id_fk": { + "name": "prompt_animo_id_animo_id_fk", + "tableFrom": "prompt", + "columnsFrom": [ + "animo_id" + ], + "tableTo": "animo", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "prompt_ejemplo_id_medio_id_fk": { + "name": "prompt_ejemplo_id_medio_id_fk", + "tableFrom": "prompt", + "columnsFrom": [ + "ejemplo_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.termino": { + "name": "termino", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "termino": { + "name": "termino", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "termino_slug_unique": { + "name": "termino_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + }, + "termino_termino_unique": { + "name": "termino_termino_unique", + "columns": [ + "termino" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.termino_variante": { + "name": "termino_variante", + "schema": "", + "columns": { + "termino_id": { + "name": "termino_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "forma": { + "name": "forma", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "termino_variante_termino_id_termino_id_fk": { + "name": "termino_variante_termino_id_termino_id_fk", + "tableFrom": "termino_variante", + "columnsFrom": [ + "termino_id" + ], + "tableTo": "termino", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "termino_variante_termino_id_forma_pk": { + "name": "termino_variante_termino_id_forma_pk", + "columns": [ + "termino_id", + "forma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verso": { + "name": "verso", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ilustra": { + "name": "ilustra", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fuente": { + "name": "fuente", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ejemplo'" + }, + "analisis": { + "name": "analisis", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "medida": { + "name": "medida", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rima": { + "name": "rima", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "obra_id": { + "name": "obra_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "verso_genero": { + "name": "verso_genero", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "orden", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "verso_genero_id_genero_id_fk": { + "name": "verso_genero_id_genero_id_fk", + "tableFrom": "verso", + "columnsFrom": [ + "genero_id" + ], + "tableTo": "genero", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "verso_obra_id_obra_referencia_id_fk": { + "name": "verso_obra_id_obra_referencia_id_fk", + "tableFrom": "verso", + "columnsFrom": [ + "obra_id" + ], + "tableTo": "obra_referencia", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "verso_cancion_id_cancion_id_fk": { + "name": "verso_cancion_id_cancion_id_fk", + "tableFrom": "verso", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verso_linea": { + "name": "verso_linea", + "schema": "", + "columns": { + "verso_id": { + "name": "verso_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "texto": { + "name": "texto", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "silabas": { + "name": "silabas", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tonicas": { + "name": "tonicas", + "type": "integer[]", + "primaryKey": false, + "notNull": false + }, + "medida": { + "name": "medida", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rima": { + "name": "rima", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "verso_linea_verso_id_verso_id_fk": { + "name": "verso_linea_verso_id_verso_id_fk", + "tableFrom": "verso_linea", + "columnsFrom": [ + "verso_id" + ], + "tableTo": "verso", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "verso_linea_verso_id_numero_pk": { + "name": "verso_linea_verso_id_numero_pk", + "columns": [ + "verso_id", + "numero" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documento_legal": { + "name": "documento_legal", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "version_vigente_id": { + "name": "version_vigente_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "documento_legal_slug_unique": { + "name": "documento_legal_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entrada": { + "name": "entrada", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fecha": { + "name": "fecha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "imagen_id": { + "name": "imagen_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "entrada_fecha": { + "name": "entrada_fecha", + "columns": [ + { + "expression": "fecha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "entrada_imagen_id_medio_id_fk": { + "name": "entrada_imagen_id_medio_id_fk", + "tableFrom": "entrada", + "columnsFrom": [ + "imagen_id" + ], + "tableTo": "medio", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entrada_slug_unique": { + "name": "entrada_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entrada_cancion": { + "name": "entrada_cancion", + "schema": "", + "columns": { + "entrada_id": { + "name": "entrada_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "entrada_cancion_entrada_id_entrada_id_fk": { + "name": "entrada_cancion_entrada_id_entrada_id_fk", + "tableFrom": "entrada_cancion", + "columnsFrom": [ + "entrada_id" + ], + "tableTo": "entrada", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "entrada_cancion_cancion_id_cancion_id_fk": { + "name": "entrada_cancion_cancion_id_cancion_id_fk", + "tableFrom": "entrada_cancion", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "entrada_cancion_entrada_id_cancion_id_pk": { + "name": "entrada_cancion_entrada_id_cancion_id_pk", + "columns": [ + "entrada_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entrada_etiqueta": { + "name": "entrada_etiqueta", + "schema": "", + "columns": { + "entrada_id": { + "name": "entrada_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "etiqueta_id": { + "name": "etiqueta_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "entrada_etiqueta_entrada_id_entrada_id_fk": { + "name": "entrada_etiqueta_entrada_id_entrada_id_fk", + "tableFrom": "entrada_etiqueta", + "columnsFrom": [ + "entrada_id" + ], + "tableTo": "entrada", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "entrada_etiqueta_etiqueta_id_etiqueta_id_fk": { + "name": "entrada_etiqueta_etiqueta_id_etiqueta_id_fk", + "tableFrom": "entrada_etiqueta", + "columnsFrom": [ + "etiqueta_id" + ], + "tableTo": "etiqueta", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "entrada_etiqueta_entrada_id_etiqueta_id_pk": { + "name": "entrada_etiqueta_entrada_id_etiqueta_id_pk", + "columns": [ + "entrada_id", + "etiqueta_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pagina": { + "name": "pagina", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "descripcion": { + "name": "descripcion", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "pagina_slug_unique": { + "name": "pagina_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redireccion": { + "name": "redireccion", + "schema": "", + "columns": { + "desde": { + "name": "desde", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "hacia": { + "name": "hacia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanente": { + "name": "permanente", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "motivo": { + "name": "motivo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "redireccion_hacia": { + "name": "redireccion_hacia", + "columns": [ + { + "expression": "hacia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.version_legal": { + "name": "version_legal", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "documento_id": { + "name": "documento_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vigente_desde": { + "name": "vigente_desde", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resumen_cambios": { + "name": "resumen_cambios", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "version_legal_documento": { + "name": "version_legal_documento", + "columns": [ + { + "expression": "documento_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "vigente_desde", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "version_legal_documento_id_documento_legal_id_fk": { + "name": "version_legal_documento_id_documento_legal_id_fk", + "tableFrom": "version_legal", + "columnsFrom": [ + "documento_id" + ], + "tableTo": "documento_legal", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contador_factura": { + "name": "contador_factura", + "schema": "", + "columns": { + "serie": { + "name": "serie", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ultimo_numero": { + "name": "ultimo_numero", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.datos_facturacion": { + "name": "datos_facturacion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'particular'" + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nif": { + "name": "nif", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "direccion": { + "name": "direccion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cp": { + "name": "cp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ciudad": { + "name": "ciudad", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provincia": { + "name": "provincia", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": true + }, + "predeterminada": { + "name": "predeterminada", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "datos_facturacion_usuario": { + "name": "datos_facturacion_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "datos_facturacion_usuario_id_usuario_id_fk": { + "name": "datos_facturacion_usuario_id_usuario_id_fk", + "tableFrom": "datos_facturacion", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "datos_facturacion_pais_codigo_pais_codigo_fk": { + "name": "datos_facturacion_pais_codigo_pais_codigo_fk", + "tableFrom": "datos_facturacion", + "columnsFrom": [ + "pais_codigo" + ], + "tableTo": "pais", + "columnsTo": [ + "codigo" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.descarga": { + "name": "descarga", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bajada_en": { + "name": "bajada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agente": { + "name": "agente", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "descarga_usuario": { + "name": "descarga_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "descarga_fecha": { + "name": "descarga_fecha", + "columns": [ + { + "expression": "bajada_en", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "descarga_usuario_id_usuario_id_fk": { + "name": "descarga_usuario_id_usuario_id_fk", + "tableFrom": "descarga", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "descarga_cancion_id_cancion_id_fk": { + "name": "descarga_cancion_id_cancion_id_fk", + "tableFrom": "descarga", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.factura": { + "name": "factura", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pedido_id": { + "name": "pedido_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serie": { + "name": "serie", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "rectifica_a_id": { + "name": "rectifica_a_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fecha": { + "name": "fecha", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "base_centimos": { + "name": "base_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "iva_tipo": { + "name": "iva_tipo", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "iva_cuota_centimos": { + "name": "iva_cuota_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_centimos": { + "name": "total_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "moneda": { + "name": "moneda", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'eur'" + }, + "datos_congelados": { + "name": "datos_congelados", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "factura_serie_numero": { + "name": "factura_serie_numero", + "columns": [ + { + "expression": "serie", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "numero", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "factura_pedido": { + "name": "factura_pedido", + "columns": [ + { + "expression": "pedido_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "factura_fecha": { + "name": "factura_fecha", + "columns": [ + { + "expression": "fecha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "factura_pedido_id_pedido_id_fk": { + "name": "factura_pedido_id_pedido_id_fk", + "tableFrom": "factura", + "columnsFrom": [ + "pedido_id" + ], + "tableTo": "pedido", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "factura_pais_codigo_pais_codigo_fk": { + "name": "factura_pais_codigo_pais_codigo_fk", + "tableFrom": "factura", + "columnsFrom": [ + "pais_codigo" + ], + "tableTo": "pais", + "columnsTo": [ + "codigo" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.codigo_acceso": { + "name": "codigo_acceso", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expira_en": { + "name": "expira_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "intentos": { + "name": "intentos", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "usado_en": { + "name": "usado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "codigo_acceso_email": { + "name": "codigo_acceso_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.identidad": { + "name": "identidad", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "proveedor": { + "name": "proveedor", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sujeto": { + "name": "sujeto", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ultimo_acceso_en": { + "name": "ultimo_acceso_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "identidad_proveedor_sujeto": { + "name": "identidad_proveedor_sujeto", + "columns": [ + { + "expression": "proveedor", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sujeto", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "identidad_usuario": { + "name": "identidad_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "identidad_usuario_id_usuario_id_fk": { + "name": "identidad_usuario_id_usuario_id_fk", + "tableFrom": "identidad", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.limite": { + "name": "limite", + "schema": "", + "columns": { + "clave": { + "name": "clave", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "intentos": { + "name": "intentos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expira_en": { + "name": "expira_en", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "limite_expira": { + "name": "limite_expira", + "columns": [ + { + "expression": "expira_en", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sesion": { + "name": "sesion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expira_en": { + "name": "expira_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sesion_usuario": { + "name": "sesion_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "sesion_usuario_id_usuario_id_fk": { + "name": "sesion_usuario_id_usuario_id_fk", + "tableFrom": "sesion", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usuario": { + "name": "usuario", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ultimo_acceso_en": { + "name": "ultimo_acceso_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "usuario_email_unique": { + "name": "usuario_email_unique", + "columns": [ + "email" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.carrito": { + "name": "carrito", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "carrito_usuario": { + "name": "carrito_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "carrito_usuario_id_usuario_id_fk": { + "name": "carrito_usuario_id_usuario_id_fk", + "tableFrom": "carrito", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.carrito_item": { + "name": "carrito_item", + "schema": "", + "columns": { + "carrito_id": { + "name": "carrito_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anadido_en": { + "name": "anadido_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "carrito_item_carrito_id_carrito_id_fk": { + "name": "carrito_item_carrito_id_carrito_id_fk", + "tableFrom": "carrito_item", + "columnsFrom": [ + "carrito_id" + ], + "tableTo": "carrito", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "carrito_item_cancion_id_cancion_id_fk": { + "name": "carrito_item_cancion_id_cancion_id_fk", + "tableFrom": "carrito_item", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "carrito_item_carrito_id_cancion_id_pk": { + "name": "carrito_item_carrito_id_cancion_id_pk", + "columns": [ + "carrito_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compra": { + "name": "compra", + "schema": "", + "columns": { + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pedido_id": { + "name": "pedido_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "comprado_en": { + "name": "comprado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "compra_usuario_id_usuario_id_fk": { + "name": "compra_usuario_id_usuario_id_fk", + "tableFrom": "compra", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "compra_cancion_id_cancion_id_fk": { + "name": "compra_cancion_id_cancion_id_fk", + "tableFrom": "compra", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "compra_pedido_id_pedido_id_fk": { + "name": "compra_pedido_id_pedido_id_fk", + "tableFrom": "compra", + "columnsFrom": [ + "pedido_id" + ], + "tableTo": "pedido", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "compra_usuario_id_cancion_id_pk": { + "name": "compra_usuario_id_cancion_id_pk", + "columns": [ + "usuario_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pedido": { + "name": "pedido", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sesion_pago_id": { + "name": "sesion_pago_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pendiente'" + }, + "packs": { + "name": "packs", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_centimos": { + "name": "total_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "moneda": { + "name": "moneda", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'eur'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "pagado_en": { + "name": "pagado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pedido_usuario": { + "name": "pedido_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "pedido_usuario_id_usuario_id_fk": { + "name": "pedido_usuario_id_usuario_id_fk", + "tableFrom": "pedido", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "pedido_sesion_pago_id_unique": { + "name": "pedido_sesion_pago_id_unique", + "columns": [ + "sesion_pago_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pedido_item": { + "name": "pedido_item", + "schema": "", + "columns": { + "pedido_id": { + "name": "pedido_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "pedido_item_pedido_id_pedido_id_fk": { + "name": "pedido_item_pedido_id_pedido_id_fk", + "tableFrom": "pedido_item", + "columnsFrom": [ + "pedido_id" + ], + "tableTo": "pedido", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "pedido_item_cancion_id_cancion_id_fk": { + "name": "pedido_item_cancion_id_cancion_id_fk", + "tableFrom": "pedido_item", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": { + "pedido_item_pedido_id_cancion_id_pk": { + "name": "pedido_item_pedido_id_cancion_id_pk", + "columns": [ + "pedido_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.favorito": { + "name": "favorito", + "schema": "", + "columns": { + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "marcado_en": { + "name": "marcado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_favorito_usuario": { + "name": "idx_favorito_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "favorito_usuario_id_usuario_id_fk": { + "name": "favorito_usuario_id_usuario_id_fk", + "tableFrom": "favorito", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "favorito_cancion_id_cancion_id_fk": { + "name": "favorito_cancion_id_cancion_id_fk", + "tableFrom": "favorito", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "favorito_usuario_id_cancion_id_pk": { + "name": "favorito_usuario_id_cancion_id_pk", + "columns": [ + "usuario_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reproduccion": { + "name": "reproduccion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "segundos": { + "name": "segundos", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "dia": { + "name": "dia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reproduccion_dia": { + "name": "reproduccion_dia", + "columns": [ + { + "expression": "dia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reproduccion_cancion": { + "name": "reproduccion_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reproduccion_usuario": { + "name": "reproduccion_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "reproduccion_cancion_id_cancion_id_fk": { + "name": "reproduccion_cancion_id_cancion_id_fk", + "tableFrom": "reproduccion", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "reproduccion_usuario_id_usuario_id_fk": { + "name": "reproduccion_usuario_id_usuario_id_fk", + "tableFrom": "reproduccion", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.valoracion": { + "name": "valoracion", + "schema": "", + "columns": { + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "puntuacion": { + "name": "puntuacion", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizada_en": { + "name": "actualizada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_valoracion_cancion": { + "name": "idx_valoracion_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "valoracion_usuario_id_usuario_id_fk": { + "name": "valoracion_usuario_id_usuario_id_fk", + "tableFrom": "valoracion", + "columnsFrom": [ + "usuario_id" + ], + "tableTo": "usuario", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "valoracion_cancion_id_cancion_id_fk": { + "name": "valoracion_cancion_id_cancion_id_fk", + "tableFrom": "valoracion", + "columnsFrom": [ + "cancion_id" + ], + "tableTo": "cancion", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "valoracion_usuario_id_cancion_id_pk": { + "name": "valoracion_usuario_id_cancion_id_pk", + "columns": [ + "usuario_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0017_snapshot.json b/drizzle/meta/0017_snapshot.json new file mode 100644 index 0000000..3e38d1d --- /dev/null +++ b/drizzle/meta/0017_snapshot.json @@ -0,0 +1,6968 @@ +{ + "id": "84f697d0-cb96-441e-9013-361469bbeb3e", + "prevId": "89cab2aa-355f-45b2-ace8-7afc914b9b6d", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.medio": { + "name": "medio", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "clase": { + "name": "clase", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "clave": { + "name": "clave", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "publico": { + "name": "publico", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "mime": { + "name": "mime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "duracion_s": { + "name": "duracion_s", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ancho": { + "name": "ancho", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alto": { + "name": "alto", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "medio_clase": { + "name": "medio_clase", + "columns": [ + { + "expression": "clase", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "medio_sha": { + "name": "medio_sha", + "columns": [ + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "medio_clave_unique": { + "name": "medio_clave_unique", + "nullsNotDistinct": false, + "columns": [ + "clave" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pais": { + "name": "pais", + "schema": "", + "columns": { + "codigo": { + "name": "codigo", + "type": "char(2)", + "primaryKey": true, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gentilicio": { + "name": "gentilicio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.persona": { + "name": "persona", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre_legal": { + "name": "nombre_legal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipi": { + "name": "ipi", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nacimiento": { + "name": "nacimiento", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "defuncion": { + "name": "defuncion", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "retrato_id": { + "name": "retrato_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "semblanza": { + "name": "semblanza", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "persona_nombre": { + "name": "persona_nombre", + "columns": [ + { + "expression": "nombre", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "persona_pais": { + "name": "persona_pais", + "columns": [ + { + "expression": "pais_codigo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "persona_pais_codigo_pais_codigo_fk": { + "name": "persona_pais_codigo_pais_codigo_fk", + "tableFrom": "persona", + "tableTo": "pais", + "columnsFrom": [ + "pais_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "persona_retrato_id_medio_id_fk": { + "name": "persona_retrato_id_medio_id_fk", + "tableFrom": "persona", + "tableTo": "medio", + "columnsFrom": [ + "retrato_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "persona_slug_unique": { + "name": "persona_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.artista": { + "name": "artista", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ART-' || lpad(nextval('artista_codigo_seq')::text, 6, '0')" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'persona'" + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "descripcion_corta": { + "name": "descripcion_corta", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "biografia": { + "name": "biografia", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "imagen_id": { + "name": "imagen_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "lugar_origen": { + "name": "lugar_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inicio": { + "name": "inicio", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "fin": { + "name": "fin", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sitio_web": { + "name": "sitio_web", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "artista_codigo_unico": { + "name": "artista_codigo_unico", + "columns": [ + { + "expression": "codigo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "artista_slug_unico": { + "name": "artista_slug_unico", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "artista_nombre": { + "name": "artista_nombre", + "columns": [ + { + "expression": "nombre", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "artista_estado": { + "name": "artista_estado", + "columns": [ + { + "expression": "estado", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "artista_imagen_id_medio_id_fk": { + "name": "artista_imagen_id_medio_id_fk", + "tableFrom": "artista", + "tableTo": "medio", + "columnsFrom": [ + "imagen_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "artista_pais_codigo_pais_codigo_fk": { + "name": "artista_pais_codigo_pais_codigo_fk", + "tableFrom": "artista", + "tableTo": "pais", + "columnsFrom": [ + "pais_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "artista_tipo_conocido": { + "name": "artista_tipo_conocido", + "value": "\"artista\".\"tipo\" in ('persona', 'grupo', 'proyecto')" + }, + "artista_estado_conocido": { + "name": "artista_estado_conocido", + "value": "\"artista\".\"estado\" in ('borrador', 'publicado', 'retirado')" + }, + "artista_periodo_valido": { + "name": "artista_periodo_valido", + "value": "\"artista\".\"fin\" is null or \"artista\".\"inicio\" is null or \"artista\".\"fin\" >= \"artista\".\"inicio\"" + } + }, + "isRLSEnabled": false + }, + "public.artista_identificador": { + "name": "artista_identificador", + "schema": "", + "columns": { + "artista_id": { + "name": "artista_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sistema": { + "name": "sistema", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "valor": { + "name": "valor", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "artista_identificador_externo_unico": { + "name": "artista_identificador_externo_unico", + "columns": [ + { + "expression": "sistema", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "valor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "artista_identificador_artista_id_artista_id_fk": { + "name": "artista_identificador_artista_id_artista_id_fk", + "tableFrom": "artista_identificador", + "tableTo": "artista", + "columnsFrom": [ + "artista_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "artista_identificador_artista_id_sistema_pk": { + "name": "artista_identificador_artista_id_sistema_pk", + "columns": [ + "artista_id", + "sistema" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.artista_persona": { + "name": "artista_persona", + "schema": "", + "columns": { + "artista_id": { + "name": "artista_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "relacion": { + "name": "relacion", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "desde": { + "name": "desde", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hasta": { + "name": "hasta", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "artista_persona_persona": { + "name": "artista_persona_persona", + "columns": [ + { + "expression": "persona_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "artista_persona_artista_id_artista_id_fk": { + "name": "artista_persona_artista_id_artista_id_fk", + "tableFrom": "artista_persona", + "tableTo": "artista", + "columnsFrom": [ + "artista_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "artista_persona_persona_id_persona_id_fk": { + "name": "artista_persona_persona_id_persona_id_fk", + "tableFrom": "artista_persona", + "tableTo": "persona", + "columnsFrom": [ + "persona_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "artista_persona_artista_id_persona_id_relacion_pk": { + "name": "artista_persona_artista_id_persona_id_relacion_pk", + "columns": [ + "artista_id", + "persona_id", + "relacion" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "artista_persona_periodo_valido": { + "name": "artista_persona_periodo_valido", + "value": "\"artista_persona\".\"hasta\" is null or \"artista_persona\".\"desde\" is null or \"artista_persona\".\"hasta\" >= \"artista_persona\".\"desde\"" + } + }, + "isRLSEnabled": false + }, + "public.album": { + "name": "album", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "estilo_id": { + "name": "estilo_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'album'" + }, + "fecha": { + "name": "fecha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portada_id": { + "name": "portada_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sello": { + "name": "sello", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "destacado": { + "name": "destacado", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "numero_catalogo": { + "name": "numero_catalogo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "formatos": { + "name": "formatos", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "pais_edicion_codigo": { + "name": "pais_edicion_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "album_estilo": { + "name": "album_estilo", + "columns": [ + { + "expression": "estilo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "album_fecha": { + "name": "album_fecha", + "columns": [ + { + "expression": "fecha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "album_estilo_id_estilo_id_fk": { + "name": "album_estilo_id_estilo_id_fk", + "tableFrom": "album", + "tableTo": "estilo", + "columnsFrom": [ + "estilo_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "album_portada_id_medio_id_fk": { + "name": "album_portada_id_medio_id_fk", + "tableFrom": "album", + "tableTo": "medio", + "columnsFrom": [ + "portada_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "album_pais_edicion_codigo_pais_codigo_fk": { + "name": "album_pais_edicion_codigo_pais_codigo_fk", + "tableFrom": "album", + "tableTo": "pais", + "columnsFrom": [ + "pais_edicion_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "album_slug_unique": { + "name": "album_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.album_enlace": { + "name": "album_enlace", + "schema": "", + "columns": { + "album_id": { + "name": "album_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plataforma": { + "name": "plataforma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "album_enlace_album_id_album_id_fk": { + "name": "album_enlace_album_id_album_id_fk", + "tableFrom": "album_enlace", + "tableTo": "album", + "columnsFrom": [ + "album_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "album_enlace_album_id_plataforma_pk": { + "name": "album_enlace_album_id_plataforma_pk", + "columns": [ + "album_id", + "plataforma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.autoria": { + "name": "autoria", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "porcentaje": { + "name": "porcentaje", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "autoria_cancion_id_cancion_id_fk": { + "name": "autoria_cancion_id_cancion_id_fk", + "tableFrom": "autoria", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "autoria_persona_id_persona_id_fk": { + "name": "autoria_persona_id_persona_id_fk", + "tableFrom": "autoria", + "tableTo": "persona", + "columnsFrom": [ + "persona_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "autoria_cancion_id_persona_id_rol_pk": { + "name": "autoria_cancion_id_persona_id_rol_pk", + "columns": [ + "cancion_id", + "persona_id", + "rol" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion": { + "name": "cancion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'CAN-' || lpad(nextval('cancion_codigo_seq')::text, 6, '0')" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version_de_id": { + "name": "version_de_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_nombre": { + "name": "version_nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_nota": { + "name": "version_nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_codigo": { + "name": "version_codigo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version_orden": { + "name": "version_orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "estilo_id": { + "name": "estilo_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "album_id": { + "name": "album_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "audio_id": { + "name": "audio_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "muestra_id": { + "name": "muestra_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duracion_s": { + "name": "duracion_s", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ano": { + "name": "ano", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "letra": { + "name": "letra", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idioma": { + "name": "idioma", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'es'" + }, + "inspiracion": { + "name": "inspiracion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instrumental": { + "name": "instrumental", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "destacada": { + "name": "destacada", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "portada_id": { + "name": "portada_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ala_venta": { + "name": "ala_venta", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "cancion_codigo_unico": { + "name": "cancion_codigo_unico", + "columns": [ + { + "expression": "codigo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cancion_estilo": { + "name": "cancion_estilo", + "columns": [ + { + "expression": "estilo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cancion_album": { + "name": "cancion_album", + "columns": [ + { + "expression": "album_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cancion_version_de": { + "name": "cancion_version_de", + "columns": [ + { + "expression": "version_de_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cancion_destacada": { + "name": "cancion_destacada", + "columns": [ + { + "expression": "destacada", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cancion_version_codigo_unico": { + "name": "cancion_version_codigo_unico", + "columns": [ + { + "expression": "version_de_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version_codigo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cancion\".\"version_de_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cancion_estilo_id_estilo_id_fk": { + "name": "cancion_estilo_id_estilo_id_fk", + "tableFrom": "cancion", + "tableTo": "estilo", + "columnsFrom": [ + "estilo_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cancion_album_id_album_id_fk": { + "name": "cancion_album_id_album_id_fk", + "tableFrom": "cancion", + "tableTo": "album", + "columnsFrom": [ + "album_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cancion_audio_id_medio_id_fk": { + "name": "cancion_audio_id_medio_id_fk", + "tableFrom": "cancion", + "tableTo": "medio", + "columnsFrom": [ + "audio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cancion_muestra_id_medio_id_fk": { + "name": "cancion_muestra_id_medio_id_fk", + "tableFrom": "cancion", + "tableTo": "medio", + "columnsFrom": [ + "muestra_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cancion_portada_id_medio_id_fk": { + "name": "cancion_portada_id_medio_id_fk", + "tableFrom": "cancion", + "tableTo": "medio", + "columnsFrom": [ + "portada_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cancion_slug_unique": { + "name": "cancion_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": { + "cancion_version_codigo_presente": { + "name": "cancion_version_codigo_presente", + "value": "(\"cancion\".\"version_de_id\" is null and \"cancion\".\"version_codigo\" is null) or (\"cancion\".\"version_de_id\" is not null and nullif(btrim(\"cancion\".\"version_codigo\"), '') is not null)" + }, + "cancion_version_no_se_vende": { + "name": "cancion_version_no_se_vende", + "value": "\"cancion\".\"version_de_id\" is null or not \"cancion\".\"ala_venta\"" + }, + "cancion_estado_conocido": { + "name": "cancion_estado_conocido", + "value": "\"cancion\".\"estado\" in ('borrador', 'publicado', 'retirado')" + } + }, + "isRLSEnabled": false + }, + "public.cancion_audio": { + "name": "cancion_audio", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "medio_id": { + "name": "medio_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uso": { + "name": "uso", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "formato": { + "name": "formato", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bitrate_kbps": { + "name": "bitrate_kbps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "frecuencia_hz": { + "name": "frecuencia_hz", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "bits_muestra": { + "name": "bits_muestra", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "canales": { + "name": "canales", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "cancion_audio_medio_unico": { + "name": "cancion_audio_medio_unico", + "columns": [ + { + "expression": "medio_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cancion_audio_cancion": { + "name": "cancion_audio_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cancion_audio_cancion_id_cancion_id_fk": { + "name": "cancion_audio_cancion_id_cancion_id_fk", + "tableFrom": "cancion_audio", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cancion_audio_medio_id_medio_id_fk": { + "name": "cancion_audio_medio_id_medio_id_fk", + "tableFrom": "cancion_audio", + "tableTo": "medio", + "columnsFrom": [ + "medio_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cancion_audio_cancion_id_uso_formato_pk": { + "name": "cancion_audio_cancion_id_uso_formato_pk", + "columns": [ + "cancion_id", + "uso", + "formato" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cancion_audio_uso_conocido": { + "name": "cancion_audio_uso_conocido", + "value": "\"cancion_audio\".\"uso\" in ('stream', 'descarga', 'master')" + }, + "cancion_audio_bitrate_valido": { + "name": "cancion_audio_bitrate_valido", + "value": "\"cancion_audio\".\"bitrate_kbps\" is null or \"cancion_audio\".\"bitrate_kbps\" > 0" + }, + "cancion_audio_frecuencia_valida": { + "name": "cancion_audio_frecuencia_valida", + "value": "\"cancion_audio\".\"frecuencia_hz\" is null or \"cancion_audio\".\"frecuencia_hz\" > 0" + }, + "cancion_audio_bits_validos": { + "name": "cancion_audio_bits_validos", + "value": "\"cancion_audio\".\"bits_muestra\" is null or \"cancion_audio\".\"bits_muestra\" > 0" + }, + "cancion_audio_canales_validos": { + "name": "cancion_audio_canales_validos", + "value": "\"cancion_audio\".\"canales\" is null or \"cancion_audio\".\"canales\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.cancion_enlace": { + "name": "cancion_enlace", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plataforma": { + "name": "plataforma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_enlace_cancion_id_cancion_id_fk": { + "name": "cancion_enlace_cancion_id_cancion_id_fk", + "tableFrom": "cancion_enlace", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cancion_enlace_cancion_id_plataforma_pk": { + "name": "cancion_enlace_cancion_id_plataforma_pk", + "columns": [ + "cancion_id", + "plataforma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion_etiqueta": { + "name": "cancion_etiqueta", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "etiqueta_id": { + "name": "etiqueta_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_etiqueta_cancion_id_cancion_id_fk": { + "name": "cancion_etiqueta_cancion_id_cancion_id_fk", + "tableFrom": "cancion_etiqueta", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cancion_etiqueta_etiqueta_id_etiqueta_id_fk": { + "name": "cancion_etiqueta_etiqueta_id_etiqueta_id_fk", + "tableFrom": "cancion_etiqueta", + "tableTo": "etiqueta", + "columnsFrom": [ + "etiqueta_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cancion_etiqueta_cancion_id_etiqueta_id_pk": { + "name": "cancion_etiqueta_cancion_id_etiqueta_id_pk", + "columns": [ + "cancion_id", + "etiqueta_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion_interprete": { + "name": "cancion_interprete", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "artista_id": { + "name": "artista_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'principal'" + }, + "instrumento": { + "name": "instrumento", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acreditado_como": { + "name": "acreditado_como", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "cancion_interprete_artista": { + "name": "cancion_interprete_artista", + "columns": [ + { + "expression": "artista_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cancion_interprete_cancion_id_cancion_id_fk": { + "name": "cancion_interprete_cancion_id_cancion_id_fk", + "tableFrom": "cancion_interprete", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cancion_interprete_artista_id_artista_id_fk": { + "name": "cancion_interprete_artista_id_artista_id_fk", + "tableFrom": "cancion_interprete", + "tableTo": "artista", + "columnsFrom": [ + "artista_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cancion_interprete_cancion_id_artista_id_rol_pk": { + "name": "cancion_interprete_cancion_id_artista_id_rol_pk", + "columns": [ + "cancion_id", + "artista_id", + "rol" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cancion_interprete_rol_conocido": { + "name": "cancion_interprete_rol_conocido", + "value": "\"cancion_interprete\".\"rol\" in ('principal', 'invitado', 'coros', 'instrumentista')" + } + }, + "isRLSEnabled": false + }, + "public.cancion_persona": { + "name": "cancion_persona", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_persona_cancion_id_cancion_id_fk": { + "name": "cancion_persona_cancion_id_cancion_id_fk", + "tableFrom": "cancion_persona", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cancion_persona_persona_id_persona_id_fk": { + "name": "cancion_persona_persona_id_persona_id_fk", + "tableFrom": "cancion_persona", + "tableTo": "persona", + "columnsFrom": [ + "persona_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cancion_persona_cancion_id_persona_id_rol_pk": { + "name": "cancion_persona_cancion_id_persona_id_rol_pk", + "columns": [ + "cancion_id", + "persona_id", + "rol" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credito_album": { + "name": "credito_album", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "album_id": { + "name": "album_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "credito_album_album": { + "name": "credito_album_album", + "columns": [ + { + "expression": "album_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credito_album_album_id_album_id_fk": { + "name": "credito_album_album_id_album_id_fk", + "tableFrom": "credito_album", + "tableTo": "album", + "columnsFrom": [ + "album_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credito_album_persona_id_persona_id_fk": { + "name": "credito_album_persona_id_persona_id_fk", + "tableFrom": "credito_album", + "tableTo": "persona", + "columnsFrom": [ + "persona_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.estilo": { + "name": "estilo", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "principal": { + "name": "principal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "imagen_id": { + "name": "imagen_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "estilo_principal_unico": { + "name": "estilo_principal_unico", + "columns": [ + { + "expression": "principal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"estilo\".\"principal\"", + "concurrently": false, + "method": "btree", + "with": {} + }, + "estilo_orden": { + "name": "estilo_orden", + "columns": [ + { + "expression": "orden", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "estilo_imagen_id_medio_id_fk": { + "name": "estilo_imagen_id_medio_id_fk", + "tableFrom": "estilo", + "tableTo": "medio", + "columnsFrom": [ + "imagen_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "estilo_slug_unique": { + "name": "estilo_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.etiqueta": { + "name": "etiqueta", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "etiqueta_slug_unique": { + "name": "etiqueta_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registro_obra": { + "name": "registro_obra", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "iswc": { + "name": "iswc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isrc": { + "name": "isrc", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entidad": { + "name": "entidad", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "numero": { + "name": "numero", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorial": { + "name": "editorial", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "registro_obra_cancion_id_cancion_id_fk": { + "name": "registro_obra_cancion_id_cancion_id_fk", + "tableFrom": "registro_obra", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.traduccion": { + "name": "traduccion", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idioma": { + "name": "idioma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "texto": { + "name": "texto", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "traduccion_cancion_id_cancion_id_fk": { + "name": "traduccion_cancion_id_cancion_id_fk", + "tableFrom": "traduccion", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "traduccion_cancion_id_idioma_pk": { + "name": "traduccion_cancion_id_idioma_pk", + "columns": [ + "cancion_id", + "idioma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.video": { + "name": "video", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "youtube_id": { + "name": "youtube_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "video_cancion": { + "name": "video_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "video_cancion_id_cancion_id_fk": { + "name": "video_cancion_id_cancion_id_fk", + "tableFrom": "video", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.acepcion": { + "name": "acepcion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "termino_id": { + "name": "termino_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "categoria": { + "name": "categoria", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "definicion": { + "name": "definicion", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ampliacion": { + "name": "ampliacion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ejemplo": { + "name": "ejemplo", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "acepcion_numero": { + "name": "acepcion_numero", + "columns": [ + { + "expression": "termino_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "numero", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "acepcion_termino_id_termino_id_fk": { + "name": "acepcion_termino_id_termino_id_fk", + "tableFrom": "acepcion", + "tableTo": "termino", + "columnsFrom": [ + "termino_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.animo": { + "name": "animo", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gems": { + "name": "gems", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valencia": { + "name": "valencia", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "energia": { + "name": "energia", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "descripcion": { + "name": "descripcion", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "animo_codigo_unique": { + "name": "animo_codigo_unique", + "nullsNotDistinct": false, + "columns": [ + "codigo" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cancion_genero": { + "name": "cancion_genero", + "schema": "", + "columns": { + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cancion_genero_cancion_id_cancion_id_fk": { + "name": "cancion_genero_cancion_id_cancion_id_fk", + "tableFrom": "cancion_genero", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cancion_genero_genero_id_genero_id_fk": { + "name": "cancion_genero_genero_id_genero_id_fk", + "tableFrom": "cancion_genero", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cancion_genero_cancion_id_genero_id_pk": { + "name": "cancion_genero_cancion_id_genero_id_pk", + "columns": [ + "cancion_id", + "genero_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.capitulo": { + "name": "capitulo", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "capitulo_tipo": { + "name": "capitulo_tipo", + "columns": [ + { + "expression": "tipo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "orden", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "capitulo_genero_id_genero_id_fk": { + "name": "capitulo_genero_id_genero_id_fk", + "tableFrom": "capitulo", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "capitulo_slug_unique": { + "name": "capitulo_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.capitulo_acepcion": { + "name": "capitulo_acepcion", + "schema": "", + "columns": { + "capitulo_id": { + "name": "capitulo_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "acepcion_id": { + "name": "acepcion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "automatico": { + "name": "automatico", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "confirmado": { + "name": "confirmado", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "capitulo_acepcion_capitulo_id_capitulo_id_fk": { + "name": "capitulo_acepcion_capitulo_id_capitulo_id_fk", + "tableFrom": "capitulo_acepcion", + "tableTo": "capitulo", + "columnsFrom": [ + "capitulo_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "capitulo_acepcion_acepcion_id_acepcion_id_fk": { + "name": "capitulo_acepcion_acepcion_id_acepcion_id_fk", + "tableFrom": "capitulo_acepcion", + "tableTo": "acepcion", + "columnsFrom": [ + "acepcion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "capitulo_acepcion_capitulo_id_acepcion_id_pk": { + "name": "capitulo_acepcion_capitulo_id_acepcion_id_pk", + "columns": [ + "capitulo_id", + "acepcion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compas": { + "name": "compas", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "numerador": { + "name": "numerador", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "denominador": { + "name": "denominador", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "compas_codigo_unique": { + "name": "compas_codigo_unique", + "nullsNotDistinct": false, + "columns": [ + "codigo" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero": { + "name": "genero", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "origen_lugar": { + "name": "origen_lugar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "origen_ano": { + "name": "origen_ano", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tempo": { + "name": "tempo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rima": { + "name": "rima", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metrica_nota": { + "name": "metrica_nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "compas_nota": { + "name": "compas_nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rasgos": { + "name": "rasgos", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "genero_nombre": { + "name": "genero_nombre", + "columns": [ + { + "expression": "nombre", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "genero_origen_ano": { + "name": "genero_origen_ano", + "columns": [ + { + "expression": "origen_ano", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "genero_slug_unique": { + "name": "genero_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_alias": { + "name": "genero_alias", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "genero_alias_nombre": { + "name": "genero_alias_nombre", + "columns": [ + { + "expression": "nombre", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "genero_alias_genero_id_genero_id_fk": { + "name": "genero_alias_genero_id_genero_id_fk", + "tableFrom": "genero_alias", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_alias_pais_codigo_pais_codigo_fk": { + "name": "genero_alias_pais_codigo_pais_codigo_fk", + "tableFrom": "genero_alias", + "tableTo": "pais", + "columnsFrom": [ + "pais_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_compas": { + "name": "genero_compas", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "compas_id": { + "name": "compas_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "principal": { + "name": "principal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "genero_compas_genero_id_genero_id_fk": { + "name": "genero_compas_genero_id_genero_id_fk", + "tableFrom": "genero_compas", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_compas_compas_id_compas_id_fk": { + "name": "genero_compas_compas_id_compas_id_fk", + "tableFrom": "genero_compas", + "tableTo": "compas", + "columnsFrom": [ + "compas_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "genero_compas_genero_id_compas_id_pk": { + "name": "genero_compas_genero_id_compas_id_pk", + "columns": [ + "genero_id", + "compas_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_instrumento": { + "name": "genero_instrumento", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instrumento_id": { + "name": "instrumento_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "importancia": { + "name": "importancia", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'habitual'" + }, + "papel": { + "name": "papel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "genero_instrumento_genero_id_genero_id_fk": { + "name": "genero_instrumento_genero_id_genero_id_fk", + "tableFrom": "genero_instrumento", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_instrumento_instrumento_id_instrumento_id_fk": { + "name": "genero_instrumento_instrumento_id_instrumento_id_fk", + "tableFrom": "genero_instrumento", + "tableTo": "instrumento", + "columnsFrom": [ + "instrumento_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "genero_instrumento_genero_id_instrumento_id_pk": { + "name": "genero_instrumento_genero_id_instrumento_id_pk", + "columns": [ + "genero_id", + "instrumento_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_metro": { + "name": "genero_metro", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metro_id": { + "name": "metro_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "predominante": { + "name": "predominante", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "genero_metro_genero_id_genero_id_fk": { + "name": "genero_metro_genero_id_genero_id_fk", + "tableFrom": "genero_metro", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_metro_metro_id_metro_id_fk": { + "name": "genero_metro_metro_id_metro_id_fk", + "tableFrom": "genero_metro", + "tableTo": "metro", + "columnsFrom": [ + "metro_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "genero_metro_genero_id_metro_id_pk": { + "name": "genero_metro_genero_id_metro_id_pk", + "columns": [ + "genero_id", + "metro_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_origen": { + "name": "genero_origen", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origen_id": { + "name": "origen_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "principal": { + "name": "principal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "genero_origen_origen": { + "name": "genero_origen_origen", + "columns": [ + { + "expression": "origen_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "genero_origen_genero_id_genero_id_fk": { + "name": "genero_origen_genero_id_genero_id_fk", + "tableFrom": "genero_origen", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_origen_origen_id_genero_id_fk": { + "name": "genero_origen_origen_id_genero_id_fk", + "tableFrom": "genero_origen", + "tableTo": "genero", + "columnsFrom": [ + "origen_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "genero_origen_genero_id_origen_id_tipo_pk": { + "name": "genero_origen_genero_id_origen_id_tipo_pk", + "columns": [ + "genero_id", + "origen_id", + "tipo" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_pais": { + "name": "genero_pais", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": true + }, + "relacion": { + "name": "relacion", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'adoptado'" + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "genero_pais_genero_id_genero_id_fk": { + "name": "genero_pais_genero_id_genero_id_fk", + "tableFrom": "genero_pais", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_pais_pais_codigo_pais_codigo_fk": { + "name": "genero_pais_pais_codigo_pais_codigo_fk", + "tableFrom": "genero_pais", + "tableTo": "pais", + "columnsFrom": [ + "pais_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "genero_pais_genero_id_pais_codigo_pk": { + "name": "genero_pais_genero_id_pais_codigo_pk", + "columns": [ + "genero_id", + "pais_codigo" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_persona": { + "name": "genero_persona", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "papel": { + "name": "papel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "genero_persona_genero_id_genero_id_fk": { + "name": "genero_persona_genero_id_genero_id_fk", + "tableFrom": "genero_persona", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_persona_persona_id_persona_id_fk": { + "name": "genero_persona_persona_id_persona_id_fk", + "tableFrom": "genero_persona", + "tableTo": "persona", + "columnsFrom": [ + "persona_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "genero_persona_genero_id_persona_id_papel_pk": { + "name": "genero_persona_genero_id_persona_id_papel_pk", + "columns": [ + "genero_id", + "persona_id", + "papel" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_seccion": { + "name": "genero_seccion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "clave": { + "name": "clave", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "genero_seccion_clave": { + "name": "genero_seccion_clave", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "clave", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "genero_seccion_genero_id_genero_id_fk": { + "name": "genero_seccion_genero_id_genero_id_fk", + "tableFrom": "genero_seccion", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.genero_tema": { + "name": "genero_tema", + "schema": "", + "columns": { + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "etiqueta_id": { + "name": "etiqueta_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "genero_tema_genero_id_genero_id_fk": { + "name": "genero_tema_genero_id_genero_id_fk", + "tableFrom": "genero_tema", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "genero_tema_etiqueta_id_etiqueta_id_fk": { + "name": "genero_tema_etiqueta_id_etiqueta_id_fk", + "tableFrom": "genero_tema", + "tableTo": "etiqueta", + "columnsFrom": [ + "etiqueta_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "genero_tema_genero_id_etiqueta_id_pk": { + "name": "genero_tema_genero_id_etiqueta_id_pk", + "columns": [ + "genero_id", + "etiqueta_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.instrumento": { + "name": "instrumento", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "familia": { + "name": "familia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subfamilia": { + "name": "subfamilia", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": false + }, + "descripcion": { + "name": "descripcion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "afinacion": { + "name": "afinacion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "imagen_id": { + "name": "imagen_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "muestra_id": { + "name": "muestra_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "instrumento_familia": { + "name": "instrumento_familia", + "columns": [ + { + "expression": "familia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "instrumento_pais_codigo_pais_codigo_fk": { + "name": "instrumento_pais_codigo_pais_codigo_fk", + "tableFrom": "instrumento", + "tableTo": "pais", + "columnsFrom": [ + "pais_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "instrumento_imagen_id_medio_id_fk": { + "name": "instrumento_imagen_id_medio_id_fk", + "tableFrom": "instrumento", + "tableTo": "medio", + "columnsFrom": [ + "imagen_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "instrumento_muestra_id_medio_id_fk": { + "name": "instrumento_muestra_id_medio_id_fk", + "tableFrom": "instrumento", + "tableTo": "medio", + "columnsFrom": [ + "muestra_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "instrumento_slug_unique": { + "name": "instrumento_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metro": { + "name": "metro", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "silabas": { + "name": "silabas", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "termino_id": { + "name": "termino_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "metro_codigo_unique": { + "name": "metro_codigo_unique", + "nullsNotDistinct": false, + "columns": [ + "codigo" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.modelo_ia": { + "name": "modelo_ia", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "codigo": { + "name": "codigo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "familia": { + "name": "familia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vigente": { + "name": "vigente", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "nota_sintaxis": { + "name": "nota_sintaxis", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "esquema": { + "name": "esquema", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "modelo_ia_familia": { + "name": "modelo_ia_familia", + "columns": [ + { + "expression": "familia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "modelo_ia_codigo_unique": { + "name": "modelo_ia_codigo_unique", + "nullsNotDistinct": false, + "columns": [ + "codigo" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obra_referencia": { + "name": "obra_referencia", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ano": { + "name": "ano", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nota": { + "name": "nota", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cita": { + "name": "cita", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "obra_referencia_genero": { + "name": "obra_referencia_genero", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "obra_referencia_genero_id_genero_id_fk": { + "name": "obra_referencia_genero_id_genero_id_fk", + "tableFrom": "obra_referencia", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "obra_referencia_slug_unique": { + "name": "obra_referencia_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obra_referencia_enlace": { + "name": "obra_referencia_enlace", + "schema": "", + "columns": { + "obra_id": { + "name": "obra_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plataforma": { + "name": "plataforma", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "obra_referencia_enlace_obra_id_obra_referencia_id_fk": { + "name": "obra_referencia_enlace_obra_id_obra_referencia_id_fk", + "tableFrom": "obra_referencia_enlace", + "tableTo": "obra_referencia", + "columnsFrom": [ + "obra_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "obra_referencia_enlace_obra_id_plataforma_pk": { + "name": "obra_referencia_enlace_obra_id_plataforma_pk", + "columns": [ + "obra_id", + "plataforma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.obra_referencia_persona": { + "name": "obra_referencia_persona", + "schema": "", + "columns": { + "obra_id": { + "name": "obra_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "persona_id": { + "name": "persona_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "papel": { + "name": "papel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "obra_referencia_persona_obra_id_obra_referencia_id_fk": { + "name": "obra_referencia_persona_obra_id_obra_referencia_id_fk", + "tableFrom": "obra_referencia_persona", + "tableTo": "obra_referencia", + "columnsFrom": [ + "obra_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "obra_referencia_persona_persona_id_persona_id_fk": { + "name": "obra_referencia_persona_persona_id_persona_id_fk", + "tableFrom": "obra_referencia_persona", + "tableTo": "persona", + "columnsFrom": [ + "persona_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "obra_referencia_persona_obra_id_persona_id_papel_pk": { + "name": "obra_referencia_persona_obra_id_persona_id_papel_pk", + "columns": [ + "obra_id", + "persona_id", + "papel" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prompt": { + "name": "prompt", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "modelo_ia_id": { + "name": "modelo_ia_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "animo_id": { + "name": "animo_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idioma": { + "name": "idioma", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'en'" + }, + "contenido": { + "name": "contenido", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "notas": { + "name": "notas", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valoracion": { + "name": "valoracion", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ejemplo_id": { + "name": "ejemplo_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preferido": { + "name": "preferido", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "visibilidad": { + "name": "visibilidad", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'privado'" + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "prompt_genero": { + "name": "prompt_genero", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prompt_cancion": { + "name": "prompt_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prompt_modelo": { + "name": "prompt_modelo", + "columns": [ + { + "expression": "modelo_ia_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prompt_animo": { + "name": "prompt_animo", + "columns": [ + { + "expression": "animo_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "prompt_modelo_ia_id_modelo_ia_id_fk": { + "name": "prompt_modelo_ia_id_modelo_ia_id_fk", + "tableFrom": "prompt", + "tableTo": "modelo_ia", + "columnsFrom": [ + "modelo_ia_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "prompt_genero_id_genero_id_fk": { + "name": "prompt_genero_id_genero_id_fk", + "tableFrom": "prompt", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "prompt_cancion_id_cancion_id_fk": { + "name": "prompt_cancion_id_cancion_id_fk", + "tableFrom": "prompt", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "prompt_animo_id_animo_id_fk": { + "name": "prompt_animo_id_animo_id_fk", + "tableFrom": "prompt", + "tableTo": "animo", + "columnsFrom": [ + "animo_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "prompt_ejemplo_id_medio_id_fk": { + "name": "prompt_ejemplo_id_medio_id_fk", + "tableFrom": "prompt", + "tableTo": "medio", + "columnsFrom": [ + "ejemplo_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.termino": { + "name": "termino", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "termino": { + "name": "termino", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "termino_slug_unique": { + "name": "termino_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + }, + "termino_termino_unique": { + "name": "termino_termino_unique", + "nullsNotDistinct": false, + "columns": [ + "termino" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.termino_variante": { + "name": "termino_variante", + "schema": "", + "columns": { + "termino_id": { + "name": "termino_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "forma": { + "name": "forma", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "termino_variante_termino_id_termino_id_fk": { + "name": "termino_variante_termino_id_termino_id_fk", + "tableFrom": "termino_variante", + "tableTo": "termino", + "columnsFrom": [ + "termino_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "termino_variante_termino_id_forma_pk": { + "name": "termino_variante_termino_id_forma_pk", + "columns": [ + "termino_id", + "forma" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verso": { + "name": "verso", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "genero_id": { + "name": "genero_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ilustra": { + "name": "ilustra", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fuente": { + "name": "fuente", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ejemplo'" + }, + "analisis": { + "name": "analisis", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "medida": { + "name": "medida", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rima": { + "name": "rima", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "obra_id": { + "name": "obra_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "origen": { + "name": "origen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "modelo_origen": { + "name": "modelo_origen", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "importado_en": { + "name": "importado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revisado_por": { + "name": "revisado_por", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revisado_en": { + "name": "revisado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "verso_genero": { + "name": "verso_genero", + "columns": [ + { + "expression": "genero_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "orden", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "verso_genero_id_genero_id_fk": { + "name": "verso_genero_id_genero_id_fk", + "tableFrom": "verso", + "tableTo": "genero", + "columnsFrom": [ + "genero_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "verso_obra_id_obra_referencia_id_fk": { + "name": "verso_obra_id_obra_referencia_id_fk", + "tableFrom": "verso", + "tableTo": "obra_referencia", + "columnsFrom": [ + "obra_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "verso_cancion_id_cancion_id_fk": { + "name": "verso_cancion_id_cancion_id_fk", + "tableFrom": "verso", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verso_linea": { + "name": "verso_linea", + "schema": "", + "columns": { + "verso_id": { + "name": "verso_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "texto": { + "name": "texto", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "silabas": { + "name": "silabas", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tonicas": { + "name": "tonicas", + "type": "integer[]", + "primaryKey": false, + "notNull": false + }, + "medida": { + "name": "medida", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rima": { + "name": "rima", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "verso_linea_verso_id_verso_id_fk": { + "name": "verso_linea_verso_id_verso_id_fk", + "tableFrom": "verso_linea", + "tableTo": "verso", + "columnsFrom": [ + "verso_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "verso_linea_verso_id_numero_pk": { + "name": "verso_linea_verso_id_numero_pk", + "columns": [ + "verso_id", + "numero" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documento_legal": { + "name": "documento_legal", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "orden": { + "name": "orden", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "version_vigente_id": { + "name": "version_vigente_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "documento_legal_slug_unique": { + "name": "documento_legal_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entrada": { + "name": "entrada", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fecha": { + "name": "fecha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resumen": { + "name": "resumen", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "imagen_id": { + "name": "imagen_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "entrada_fecha": { + "name": "entrada_fecha", + "columns": [ + { + "expression": "fecha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "entrada_imagen_id_medio_id_fk": { + "name": "entrada_imagen_id_medio_id_fk", + "tableFrom": "entrada", + "tableTo": "medio", + "columnsFrom": [ + "imagen_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entrada_slug_unique": { + "name": "entrada_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entrada_cancion": { + "name": "entrada_cancion", + "schema": "", + "columns": { + "entrada_id": { + "name": "entrada_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "entrada_cancion_entrada_id_entrada_id_fk": { + "name": "entrada_cancion_entrada_id_entrada_id_fk", + "tableFrom": "entrada_cancion", + "tableTo": "entrada", + "columnsFrom": [ + "entrada_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entrada_cancion_cancion_id_cancion_id_fk": { + "name": "entrada_cancion_cancion_id_cancion_id_fk", + "tableFrom": "entrada_cancion", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "entrada_cancion_entrada_id_cancion_id_pk": { + "name": "entrada_cancion_entrada_id_cancion_id_pk", + "columns": [ + "entrada_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entrada_etiqueta": { + "name": "entrada_etiqueta", + "schema": "", + "columns": { + "entrada_id": { + "name": "entrada_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "etiqueta_id": { + "name": "etiqueta_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "entrada_etiqueta_entrada_id_entrada_id_fk": { + "name": "entrada_etiqueta_entrada_id_entrada_id_fk", + "tableFrom": "entrada_etiqueta", + "tableTo": "entrada", + "columnsFrom": [ + "entrada_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entrada_etiqueta_etiqueta_id_etiqueta_id_fk": { + "name": "entrada_etiqueta_etiqueta_id_etiqueta_id_fk", + "tableFrom": "entrada_etiqueta", + "tableTo": "etiqueta", + "columnsFrom": [ + "etiqueta_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "entrada_etiqueta_entrada_id_etiqueta_id_pk": { + "name": "entrada_etiqueta_entrada_id_etiqueta_id_pk", + "columns": [ + "entrada_id", + "etiqueta_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pagina": { + "name": "pagina", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "descripcion": { + "name": "descripcion", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'borrador'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "pagina_slug_unique": { + "name": "pagina_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redireccion": { + "name": "redireccion", + "schema": "", + "columns": { + "desde": { + "name": "desde", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "hacia": { + "name": "hacia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanente": { + "name": "permanente", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "motivo": { + "name": "motivo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "redireccion_hacia": { + "name": "redireccion_hacia", + "columns": [ + { + "expression": "hacia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.version_legal": { + "name": "version_legal", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "documento_id": { + "name": "documento_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cuerpo": { + "name": "cuerpo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vigente_desde": { + "name": "vigente_desde", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resumen_cambios": { + "name": "resumen_cambios", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "version_legal_documento": { + "name": "version_legal_documento", + "columns": [ + { + "expression": "documento_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "vigente_desde", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "version_legal_documento_id_documento_legal_id_fk": { + "name": "version_legal_documento_id_documento_legal_id_fk", + "tableFrom": "version_legal", + "tableTo": "documento_legal", + "columnsFrom": [ + "documento_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contador_factura": { + "name": "contador_factura", + "schema": "", + "columns": { + "serie": { + "name": "serie", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ultimo_numero": { + "name": "ultimo_numero", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.datos_facturacion": { + "name": "datos_facturacion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'particular'" + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nif": { + "name": "nif", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "direccion": { + "name": "direccion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cp": { + "name": "cp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ciudad": { + "name": "ciudad", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provincia": { + "name": "provincia", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": true + }, + "predeterminada": { + "name": "predeterminada", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "datos_facturacion_usuario": { + "name": "datos_facturacion_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "datos_facturacion_usuario_id_usuario_id_fk": { + "name": "datos_facturacion_usuario_id_usuario_id_fk", + "tableFrom": "datos_facturacion", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "datos_facturacion_pais_codigo_pais_codigo_fk": { + "name": "datos_facturacion_pais_codigo_pais_codigo_fk", + "tableFrom": "datos_facturacion", + "tableTo": "pais", + "columnsFrom": [ + "pais_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.descarga": { + "name": "descarga", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bajada_en": { + "name": "bajada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agente": { + "name": "agente", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "descarga_usuario": { + "name": "descarga_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "descarga_fecha": { + "name": "descarga_fecha", + "columns": [ + { + "expression": "bajada_en", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "descarga_usuario_id_usuario_id_fk": { + "name": "descarga_usuario_id_usuario_id_fk", + "tableFrom": "descarga", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "descarga_cancion_id_cancion_id_fk": { + "name": "descarga_cancion_id_cancion_id_fk", + "tableFrom": "descarga", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.factura": { + "name": "factura", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pedido_id": { + "name": "pedido_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serie": { + "name": "serie", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "numero": { + "name": "numero", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "rectifica_a_id": { + "name": "rectifica_a_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fecha": { + "name": "fecha", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "base_centimos": { + "name": "base_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "iva_tipo": { + "name": "iva_tipo", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "iva_cuota_centimos": { + "name": "iva_cuota_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_centimos": { + "name": "total_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "moneda": { + "name": "moneda", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'eur'" + }, + "datos_congelados": { + "name": "datos_congelados", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "pais_codigo": { + "name": "pais_codigo", + "type": "char(2)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "factura_serie_numero": { + "name": "factura_serie_numero", + "columns": [ + { + "expression": "serie", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "numero", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "factura_pedido": { + "name": "factura_pedido", + "columns": [ + { + "expression": "pedido_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "factura_fecha": { + "name": "factura_fecha", + "columns": [ + { + "expression": "fecha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "factura_pedido_id_pedido_id_fk": { + "name": "factura_pedido_id_pedido_id_fk", + "tableFrom": "factura", + "tableTo": "pedido", + "columnsFrom": [ + "pedido_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "factura_pais_codigo_pais_codigo_fk": { + "name": "factura_pais_codigo_pais_codigo_fk", + "tableFrom": "factura", + "tableTo": "pais", + "columnsFrom": [ + "pais_codigo" + ], + "columnsTo": [ + "codigo" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.codigo_acceso": { + "name": "codigo_acceso", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expira_en": { + "name": "expira_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "intentos": { + "name": "intentos", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "usado_en": { + "name": "usado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "codigo_acceso_email": { + "name": "codigo_acceso_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.identidad": { + "name": "identidad", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "proveedor": { + "name": "proveedor", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sujeto": { + "name": "sujeto", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ultimo_acceso_en": { + "name": "ultimo_acceso_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "identidad_proveedor_sujeto": { + "name": "identidad_proveedor_sujeto", + "columns": [ + { + "expression": "proveedor", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sujeto", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "identidad_usuario": { + "name": "identidad_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "identidad_usuario_id_usuario_id_fk": { + "name": "identidad_usuario_id_usuario_id_fk", + "tableFrom": "identidad", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.limite": { + "name": "limite", + "schema": "", + "columns": { + "clave": { + "name": "clave", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "intentos": { + "name": "intentos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expira_en": { + "name": "expira_en", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "limite_expira": { + "name": "limite_expira", + "columns": [ + { + "expression": "expira_en", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sesion": { + "name": "sesion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expira_en": { + "name": "expira_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sesion_usuario": { + "name": "sesion_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sesion_usuario_id_usuario_id_fk": { + "name": "sesion_usuario_id_usuario_id_fk", + "tableFrom": "sesion", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usuario": { + "name": "usuario", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nombre": { + "name": "nombre", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rol": { + "name": "rol", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'persona'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ultimo_acceso_en": { + "name": "ultimo_acceso_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "usuario_email_unique": { + "name": "usuario_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.carrito": { + "name": "carrito", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizado_en": { + "name": "actualizado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "carrito_usuario_unico": { + "name": "carrito_usuario_unico", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"carrito\".\"usuario_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "carrito_usuario_id_usuario_id_fk": { + "name": "carrito_usuario_id_usuario_id_fk", + "tableFrom": "carrito", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.carrito_item": { + "name": "carrito_item", + "schema": "", + "columns": { + "carrito_id": { + "name": "carrito_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anadido_en": { + "name": "anadido_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "carrito_item_carrito_id_carrito_id_fk": { + "name": "carrito_item_carrito_id_carrito_id_fk", + "tableFrom": "carrito_item", + "tableTo": "carrito", + "columnsFrom": [ + "carrito_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "carrito_item_cancion_id_cancion_id_fk": { + "name": "carrito_item_cancion_id_cancion_id_fk", + "tableFrom": "carrito_item", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "carrito_item_carrito_id_cancion_id_pk": { + "name": "carrito_item_carrito_id_cancion_id_pk", + "columns": [ + "carrito_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compra": { + "name": "compra", + "schema": "", + "columns": { + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pedido_id": { + "name": "pedido_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "comprado_en": { + "name": "comprado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "compra_usuario_id_usuario_id_fk": { + "name": "compra_usuario_id_usuario_id_fk", + "tableFrom": "compra", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compra_cancion_id_cancion_id_fk": { + "name": "compra_cancion_id_cancion_id_fk", + "tableFrom": "compra", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "compra_pedido_id_pedido_id_fk": { + "name": "compra_pedido_id_pedido_id_fk", + "tableFrom": "compra", + "tableTo": "pedido", + "columnsFrom": [ + "pedido_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "compra_usuario_id_cancion_id_pk": { + "name": "compra_usuario_id_cancion_id_pk", + "columns": [ + "usuario_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pedido": { + "name": "pedido", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sesion_pago_id": { + "name": "sesion_pago_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "estado": { + "name": "estado", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pendiente'" + }, + "packs": { + "name": "packs", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_centimos": { + "name": "total_centimos", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "moneda": { + "name": "moneda", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'eur'" + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "pagado_en": { + "name": "pagado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pedido_usuario": { + "name": "pedido_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pedido_usuario_id_usuario_id_fk": { + "name": "pedido_usuario_id_usuario_id_fk", + "tableFrom": "pedido", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "pedido_sesion_pago_id_unique": { + "name": "pedido_sesion_pago_id_unique", + "nullsNotDistinct": false, + "columns": [ + "sesion_pago_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pedido_item": { + "name": "pedido_item", + "schema": "", + "columns": { + "pedido_id": { + "name": "pedido_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "pedido_item_pedido_id_pedido_id_fk": { + "name": "pedido_item_pedido_id_pedido_id_fk", + "tableFrom": "pedido_item", + "tableTo": "pedido", + "columnsFrom": [ + "pedido_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pedido_item_cancion_id_cancion_id_fk": { + "name": "pedido_item_cancion_id_cancion_id_fk", + "tableFrom": "pedido_item", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "pedido_item_pedido_id_cancion_id_pk": { + "name": "pedido_item_pedido_id_cancion_id_pk", + "columns": [ + "pedido_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.favorito": { + "name": "favorito", + "schema": "", + "columns": { + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "marcado_en": { + "name": "marcado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_favorito_usuario": { + "name": "idx_favorito_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "favorito_usuario_id_usuario_id_fk": { + "name": "favorito_usuario_id_usuario_id_fk", + "tableFrom": "favorito", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "favorito_cancion_id_cancion_id_fk": { + "name": "favorito_cancion_id_cancion_id_fk", + "tableFrom": "favorito", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "favorito_usuario_id_cancion_id_pk": { + "name": "favorito_usuario_id_cancion_id_pk", + "columns": [ + "usuario_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reproduccion": { + "name": "reproduccion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "segundos": { + "name": "segundos", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "dia": { + "name": "dia", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "creado_en": { + "name": "creado_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reproduccion_dia": { + "name": "reproduccion_dia", + "columns": [ + { + "expression": "dia", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reproduccion_cancion": { + "name": "reproduccion_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reproduccion_usuario": { + "name": "reproduccion_usuario", + "columns": [ + { + "expression": "usuario_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reproduccion_cancion_id_cancion_id_fk": { + "name": "reproduccion_cancion_id_cancion_id_fk", + "tableFrom": "reproduccion", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reproduccion_usuario_id_usuario_id_fk": { + "name": "reproduccion_usuario_id_usuario_id_fk", + "tableFrom": "reproduccion", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.valoracion": { + "name": "valoracion", + "schema": "", + "columns": { + "usuario_id": { + "name": "usuario_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancion_id": { + "name": "cancion_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "puntuacion": { + "name": "puntuacion", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "creada_en": { + "name": "creada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actualizada_en": { + "name": "actualizada_en", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_valoracion_cancion": { + "name": "idx_valoracion_cancion", + "columns": [ + { + "expression": "cancion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "valoracion_usuario_id_usuario_id_fk": { + "name": "valoracion_usuario_id_usuario_id_fk", + "tableFrom": "valoracion", + "tableTo": "usuario", + "columnsFrom": [ + "usuario_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "valoracion_cancion_id_cancion_id_fk": { + "name": "valoracion_cancion_id_cancion_id_fk", + "tableFrom": "valoracion", + "tableTo": "cancion", + "columnsFrom": [ + "cancion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "valoracion_usuario_id_cancion_id_pk": { + "name": "valoracion_usuario_id_cancion_id_pk", + "columns": [ + "usuario_id", + "cancion_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": { + "public.artista_codigo_seq": { + "name": "artista_codigo_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + }, + "public.cancion_codigo_seq": { + "name": "cancion_codigo_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index baaf1cd..f9f52e7 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -78,6 +78,55 @@ "when": 1788898884661, "tag": "0010_texto_de_estilos_y_discos", "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1788949140000, + "tag": "0011_administradores", + "breakpoints": true + }, + { + "idx": 12, + "version": "7", + "when": 1788965520000, + "tag": "0012_prompts_de_una_cancion", + "breakpoints": true + }, + { + "idx": 13, + "version": "7", + "when": 1789041720000, + "tag": "0013_integridad_concurrente", + "breakpoints": true + }, + { + "idx": 14, + "version": "7", + "when": 1789042140000, + "tag": "0014_artistas_y_audio", + "breakpoints": true + }, + { + "idx": 15, + "version": "7", + "when": 1789042620000, + "tag": "0015_condiciones_escucha_completa", + "breakpoints": true + }, + { + "idx": 16, + "version": "7", + "when": 1789053311680, + "tag": "0016_sincronizar_migraciones_manuales", + "breakpoints": true + }, + { + "idx": 17, + "version": "7", + "when": 1789053373570, + "tag": "0017_verificar_sincronizacion", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index e3bc6ab..f670612 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "db:push": "drizzle-kit push", "db:generate": "drizzle-kit generate", "db:migrate": "node --env-file=.env scripts/db/migrar.mjs", + "db:migrate:check": "node --env-file=.env scripts/db/comprobar-migraciones.mjs", + "db:auditar-musica": "node --env-file=.env scripts/db/auditar-dominio-musical.mjs", "db:studio": "drizzle-kit studio", "medios": "node scripts/medios.mjs", "predev": "node scripts/medios.mjs", diff --git a/scripts/config/audio.mjs b/scripts/config/audio.mjs new file mode 100644 index 0000000..a9edd55 --- /dev/null +++ b/scripts/config/audio.mjs @@ -0,0 +1,7 @@ +/** Perfil único de derivados públicos; cambiarlo obliga a regenerarlos. */ +export const PERFIL_ESCUCHA = Object.freeze({ + id: 'mp3-96-v1', + formato: 'mp3', + codecFfmpeg: 'libmp3lame', + bitrateKbps: 96 +}); diff --git a/scripts/db/auditar-dominio-musical.mjs b/scripts/db/auditar-dominio-musical.mjs new file mode 100644 index 0000000..593b0e0 --- /dev/null +++ b/scripts/db/auditar-dominio-musical.mjs @@ -0,0 +1,50 @@ +/** Informe de paridad tras migrar artistas, versiones y representaciones. */ + +import pg from 'pg'; + +const cliente = new pg.Client({ + connectionString: process.env.DATABASE_URL, + query_timeout: 30_000 +}); +await cliente.connect(); + +const { + rows: [n] +} = await cliente.query(` + select + (select count(*) from cancion where version_de_id is null)::int as canciones, + (select count(*) from cancion where version_de_id is not null)::int as versiones, + (select count(*) from cancion where codigo is null)::int as canciones_sin_codigo, + (select count(*) from artista)::int as artistas, + (select count(*) from cancion_persona where rol = 'interprete')::int as creditos_anteriores, + (select count(*) from cancion_interprete)::int as creditos_nuevos, + (select count(*) from cancion_audio where uso = 'master')::int as masters, + (select count(*) from cancion_audio where uso = 'stream')::int as streams, + (select count(*) from cancion c where c.estado = 'publicado' + and not exists (select 1 from cancion_audio ca where ca.cancion_id = c.id and ca.uso = 'stream'))::int + as publicadas_sin_stream +`); + +const { rows: problemas } = await cliente.query(` + select 'intérprete sin migrar' as tipo, c.slug as referencia + from cancion_persona cp + join cancion c on c.id = cp.cancion_id + where cp.rol = 'interprete' + and not exists (select 1 from cancion_interprete ci where ci.cancion_id = cp.cancion_id) + union all + select 'artista sin código', slug from artista where codigo is null or btrim(codigo) = '' + union all + select 'versión sin código', slug from cancion + where version_de_id is not null and (version_codigo is null or btrim(version_codigo) = '') + order by tipo, referencia +`); + +console.table(n); +if (problemas.length) console.table(problemas); +await cliente.end(); + +if (problemas.length || n.canciones_sin_codigo || n.publicadas_sin_stream) { + console.error('La migración todavía no cumple la paridad requerida.'); + process.exit(1); +} +console.log('Dominio musical íntegro: códigos, créditos y streams completos.'); diff --git a/scripts/db/comprobar-migraciones.mjs b/scripts/db/comprobar-migraciones.mjs new file mode 100644 index 0000000..e965489 --- /dev/null +++ b/scripts/db/comprobar-migraciones.mjs @@ -0,0 +1,42 @@ +/** Ejecuta migraciones dentro de una transacción que siempre se revierte. */ + +import fs from 'node:fs'; +import path from 'node:path'; +import pg from 'pg'; + +const nombres = process.argv.slice(2); +if (nombres.length === 0) + throw new Error('Indica al menos una migración, por ejemplo 0014_artistas_y_audio.sql.'); + +const sentenciasDe = (texto) => + texto + .split('--> statement-breakpoint') + .map((s) => s.trim()) + .filter((s) => s && s.split('\n').some((l) => l.trim() && !l.trim().startsWith('--'))); + +const cliente = new pg.Client({ + connectionString: process.env.DATABASE_URL, + query_timeout: 120_000 +}); +await cliente.connect(); +await cliente.query('BEGIN'); + +try { + for (const nombre of nombres) { + const ruta = path.join('drizzle', nombre); + const sentencias = sentenciasDe(fs.readFileSync(ruta, 'utf8')); + for (const [indice, sentencia] of sentencias.entries()) { + try { + await cliente.query(sentencia); + } catch (error) { + throw new Error(`${nombre}, sentencia ${indice + 1}: ${error.message}`, { cause: error }); + } + } + console.log(` ${nombre}: ${sentencias.length} sentencias válidas`); + } +} finally { + await cliente.query('ROLLBACK').catch(() => {}); + await cliente.end(); +} + +console.log('Comprobadas y revertidas: la base no ha cambiado.'); diff --git a/scripts/db/volcar-medios.mjs b/scripts/db/volcar-medios.mjs index 4df2fca..67f3fdb 100644 --- a/scripts/db/volcar-medios.mjs +++ b/scripts/db/volcar-medios.mjs @@ -1,6 +1,6 @@ /** - * Vuelca los archivos —audio, muestras y portadas— a la tabla `medio` y los - * enlaza con quien los usa. + * Vuelca másteres, copias completas de escucha, muestras antiguas y portadas a + * `medio`, y reconstruye sus relaciones. * * npm run db:medios * npm run db:medios -- --seco @@ -20,20 +20,45 @@ import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import pg from 'pg'; +import { load as leerYaml } from 'js-yaml'; +import { PERFIL_ESCUCHA } from '../config/audio.mjs'; const SECO = process.argv.includes('--seco'); /** Dónde vive cada clase de archivo y si se puede pedir por URL. */ const ALMACENES = [ - { clase: 'audio', raiz: 'media/audio', publico: false, carpeta: 'audio', prefijo: '' }, { + nombre: 'máster', + clase: 'audio', + raiz: 'media/audio', + publico: false, + carpeta: 'audio', + prefijo: '' + }, + { + nombre: 'escucha', + clase: 'audio', + raiz: 'static/escucha', + publico: true, + carpeta: 'escucha', + prefijo: '/escucha/' + }, + { + nombre: 'muestra', clase: 'muestra', raiz: 'static/muestras', publico: true, carpeta: 'muestras', prefijo: '/muestras/' }, - { clase: 'imagen', raiz: 'static/images', publico: true, carpeta: 'images', prefijo: '/images/' } + { + nombre: 'imagen', + clase: 'imagen', + raiz: 'static/images', + publico: true, + carpeta: 'images', + prefijo: '/images/' + } ]; const MIMES = { @@ -57,23 +82,50 @@ function recorrer(dir) { }); } +const aSlug = (texto) => + String(texto ?? '') + .normalize('NFD') + .replace(/\p{Diacritic}/gu, '') + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + /** * Duración en segundos, medida con ffprobe. * * Medida y no escrita: es el dato que más fácil se queda viejo. Si ffprobe no * está, se deja en nulo y se avisa; no se inventa. */ -function duracionDe(ruta) { +function datosDeAudio(ruta) { try { const salida = execFileSync( 'ffprobe', - ['-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', ruta], + [ + '-v', + 'error', + '-select_streams', + 'a:0', + '-show_entries', + 'format=duration,bit_rate:stream=sample_rate,channels', + '-of', + 'json', + ruta + ], { encoding: 'utf8', timeout: 20_000 } ); - const segundos = Number.parseFloat(salida.trim()); - return Number.isFinite(segundos) ? Math.round(segundos) : null; + const datos = JSON.parse(salida); + const segundos = Number.parseFloat(datos.format?.duration); + const bitrate = Number.parseInt(datos.format?.bit_rate, 10); + const frecuencia = Number.parseInt(datos.streams?.[0]?.sample_rate, 10); + const canales = Number.parseInt(datos.streams?.[0]?.channels, 10); + return { + duracionS: Number.isFinite(segundos) ? Math.round(segundos) : null, + bitrateKbps: Number.isFinite(bitrate) ? Math.round(bitrate / 1000) : null, + frecuenciaHz: Number.isFinite(frecuencia) ? frecuencia : null, + canales: Number.isFinite(canales) ? canales : null + }; } catch { - return null; + return { duracionS: null, bitrateKbps: null, frecuenciaHz: null, canales: null }; } } @@ -114,8 +166,12 @@ for (const almacen of ALMACENES) { const contenido = fs.readFileSync(ruta); const relativa = path.relative(almacen.raiz, ruta).split(path.sep).join('/'); const esImagen = mime.startsWith('image/'); + const audio = esImagen + ? { duracionS: null, bitrateKbps: null, frecuenciaHz: null, canales: null } + : datosDeAudio(ruta); encontrados.push({ + almacen: almacen.nombre, clase: almacen.clase, // La clave lleva el almacen delante: `singles/dorina.mp3` existe en audio // y en muestras, y sin el prefijo serian la misma fila. Ademas es la ruta @@ -126,7 +182,7 @@ for (const almacen of ALMACENES) { mime, bytes: contenido.length, sha256: createHash('sha256').update(contenido).digest('hex'), - duracionS: esImagen ? null : duracionDe(ruta), + ...audio, ...(esImagen ? medidasDe(ruta) : { ancho: null, alto: null }) }); } @@ -134,10 +190,10 @@ for (const almacen of ALMACENES) { console.log(`Encontrados ${encontrados.length} archivos\n`); for (const almacen of ALMACENES) { - const suyos = encontrados.filter((m) => m.clase === almacen.clase); + const suyos = encontrados.filter((m) => m.almacen === almacen.nombre); const pesan = suyos.reduce((n, m) => n + m.bytes, 0); console.log( - ` ${almacen.clase.padEnd(9)} ${String(suyos.length).padStart(3)} archivos · ${(pesan / 1048576).toFixed(1)} MB` + ` ${almacen.nombre.padEnd(9)} ${String(suyos.length).padStart(3)} archivos · ${(pesan / 1048576).toFixed(1)} MB` ); } @@ -170,28 +226,30 @@ await c.connect(); await c.query('BEGIN'); try { - /* - * Se sueltan los enlaces antes de vaciar la tabla. Las columnas son - * `ON DELETE set null`, así que Postgres lo haría solo, pero decirlo aquí - * deja claro que el volcado no pierde nada: lo que se rehace es la fila del - * archivo, no la canción. - */ - await c.query('UPDATE cancion SET audio_id = NULL, muestra_id = NULL, portada_id = NULL'); - await c.query('UPDATE album SET portada_id = NULL'); - await c.query('DELETE FROM medio'); - const idPorClave = new Map(); + const medioPorClave = new Map(); for (const m of encontrados) { const id = crypto.randomUUID(); - await c.query( + const { rows } = await c.query( `INSERT INTO medio (id, clase, clave, publico, mime, bytes, duracion_s, ancho, alto, sha256) - VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`, + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) + ON CONFLICT (clave) DO UPDATE SET + clase = excluded.clase, + publico = excluded.publico, + mime = excluded.mime, + bytes = excluded.bytes, + duracion_s = excluded.duracion_s, + ancho = excluded.ancho, + alto = excluded.alto, + sha256 = excluded.sha256 + RETURNING id`, [id, m.clase, m.clave, m.publico, m.mime, m.bytes, m.duracionS, m.ancho, m.alto, m.sha256] ); - idPorClave.set(`${m.clase}:${m.clave}`, id); - if (m.clase === 'imagen') idPorClave.set(`url:${m.url}`, id); + idPorClave.set(`${m.clase}:${m.clave}`, rows[0].id); + medioPorClave.set(`${m.clase}:${m.clave}`, m); + if (m.clase === 'imagen') idPorClave.set(`url:${m.url}`, rows[0].id); } - console.log(`\n ${encontrados.length} filas en medio`); + console.log(`\n ${encontrados.length} medios insertados o actualizados`); /* --- Enlazar con lo que los usa --------------------------------------- * @@ -199,7 +257,7 @@ try { * volcado del catálogo no guardó esas rutas: guardarlas como texto habría * sido repetir el problema que esta tabla viene a resolver. */ - const enlazados = { audio: 0, muestra: 0, portadaCancion: 0, portadaAlbum: 0 }; + const enlazados = { master: 0, stream: 0, muestra: 0, portadaCancion: 0, portadaAlbum: 0 }; const huerfanos = []; const canciones = fs @@ -211,22 +269,81 @@ try { })); for (const { slug, texto } of canciones) { - const audio = /^audio:\s*(.+)$/m - .exec(texto)?.[1] - .trim() - .replace(/^['"]|['"]$/g, ''); + const cabecera = /^---\r?\n([\s\S]*?)\r?\n---/.exec(texto)?.[1]; + const datos = cabecera ? (leerYaml(cabecera) ?? {}) : {}; const portada = /^portada:\s*(.+)$/m .exec(texto)?.[1] .trim() .replace(/^['"]|['"]$/g, ''); - if (audio) { + const audios = [ + { slug, audio: datos.audio }, + ...(Array.isArray(datos.versiones) + ? datos.versiones.map((version) => ({ + slug: `${slug}-${version.id ?? aSlug(version.nombre)}`, + audio: version.audio + })) + : []) + ]; + + for (const entidad of audios) { + const { rows: destinos } = await c.query('SELECT id FROM cancion WHERE slug = $1', [ + entidad.slug + ]); + if (destinos.length === 0) { + huerfanos.push(`${entidad.slug}: la canción no existe en la base`); + continue; + } + await c.query('DELETE FROM cancion_audio WHERE cancion_id = $1', [destinos[0].id]); + await c.query('UPDATE cancion SET audio_id = NULL, muestra_id = NULL WHERE id = $1', [ + destinos[0].id + ]); + const audio = + typeof entidad.audio === 'string' ? entidad.audio.replace(/^\//, '') : undefined; + if (!audio) continue; const id = idPorClave.get(`audio:audio/${audio}`); if (id) { - await c.query('UPDATE cancion SET audio_id = $1 WHERE slug = $2', [id, slug]); - enlazados.audio++; + const master = medioPorClave.get(`audio:audio/${audio}`); + await c.query('UPDATE cancion SET audio_id = $1 WHERE slug = $2', [id, entidad.slug]); + await c.query( + `INSERT INTO cancion_audio + (cancion_id, medio_id, uso, formato, bitrate_kbps, frecuencia_hz, canales) + SELECT id, $1, 'master', $2, $3, $4, $5 FROM cancion WHERE slug = $6 + ON CONFLICT DO NOTHING`, + [ + id, + path.extname(audio).slice(1).toLowerCase() || 'audio', + master?.bitrateKbps ?? null, + master?.frecuenciaHz ?? null, + master?.canales ?? null, + entidad.slug + ] + ); + enlazados.master++; + } else { + huerfanos.push(`${entidad.slug}: el audio ${audio} no existe`); + } + + const escucha = audio.replace(/\.[^.]+$/, '.mp3'); + const idEscucha = idPorClave.get(`audio:escucha/${escucha}`); + if (idEscucha) { + const stream = medioPorClave.get(`audio:escucha/${escucha}`); + await c.query( + `INSERT INTO cancion_audio + (cancion_id, medio_id, uso, formato, bitrate_kbps, frecuencia_hz, canales) + SELECT id, $1, 'stream', 'mp3', $2, $3, $4 FROM cancion WHERE slug = $5 + ON CONFLICT DO NOTHING`, + [ + idEscucha, + stream?.bitrateKbps ?? PERFIL_ESCUCHA.bitrateKbps, + stream?.frecuenciaHz ?? null, + stream?.canales ?? null, + entidad.slug + ] + ); + enlazados.stream++; } else { - huerfanos.push(`${slug}: el audio ${audio} no existe`); + huerfanos.push(`${entidad.slug}: falta la escucha completa ${escucha}`); } /* @@ -237,11 +354,15 @@ try { const muestra = audio.replace(/\.[^.]+$/, '.mp3'); const idMuestra = idPorClave.get(`muestra:muestras/${muestra}`); if (idMuestra) { - await c.query('UPDATE cancion SET muestra_id = $1 WHERE slug = $2', [idMuestra, slug]); + await c.query('UPDATE cancion SET muestra_id = $1 WHERE slug = $2', [ + idMuestra, + entidad.slug + ]); enlazados.muestra++; } } + await c.query('UPDATE cancion SET portada_id = NULL WHERE slug = $1', [slug]); if (portada) { const id = idPorClave.get(`url:${portada}`); if (id) { @@ -260,6 +381,7 @@ try { .exec(texto)?.[1] .trim() .replace(/^['"]|['"]$/g, ''); + await c.query('UPDATE album SET portada_id = NULL WHERE slug = $1', [slug]); if (!portada) continue; const id = idPorClave.get(`url:${portada}`); @@ -272,7 +394,7 @@ try { } console.log( - ` enlazados: ${enlazados.audio} audios, ${enlazados.muestra} muestras, ` + + ` enlazados: ${enlazados.master} másteres, ${enlazados.stream} escuchas, ${enlazados.muestra} muestras antiguas, ` + `${enlazados.portadaCancion} portadas de tema, ${enlazados.portadaAlbum} de disco` ); diff --git a/scripts/db/volcar.mjs b/scripts/db/volcar.mjs index d7a6240..d510c0f 100644 --- a/scripts/db/volcar.mjs +++ b/scripts/db/volcar.mjs @@ -102,6 +102,16 @@ const slugificar = (nombre) => async function volcar(c, contenido, informe) { const { estilos, albumes, canciones, capitulos, entradas, legales, paginas } = contenido; + const { rows: actividad } = await c.query(` + select + (select count(*) from compra)::int as compras, + (select count(*) from pedido_item)::int as lineas_pedido + `); + if (actividad[0].compras > 0 || actividad[0].lineas_pedido > 0) { + throw new Error( + 'El volcado total es solo para una base vacía: ya hay compras o pedidos. Usa el panel y migraciones aditivas.' + ); + } /* --- Se borra lo volcado antes de reescribirlo ----------------------- * @@ -132,6 +142,8 @@ async function volcar(c, contenido, informe) { 'pagina', 'autoria', 'registro_obra', + 'cancion_audio', + 'cancion_interprete', 'cancion_persona', 'cancion_enlace', 'cancion_etiqueta', @@ -143,6 +155,9 @@ async function volcar(c, contenido, informe) { 'album', 'etiqueta', 'estilo', + 'artista_identificador', + 'artista_persona', + 'artista', 'persona' ]; for (const tabla of aVaciar) await c.query(`DELETE FROM "${tabla}"`); @@ -168,6 +183,30 @@ async function volcar(c, contenido, informe) { return id; }; + /* Identidad pública: separada de la persona física que firma la obra. */ + const artistas = new Map(); + const artista = async (nombre, personaId) => { + const limpio = nombre.trim(); + if (artistas.has(limpio)) return artistas.get(limpio); + const id = nuevoId(); + const tipo = + limpio === 'Los del Puerto' ? 'grupo' : limpio === 'Senza Paura' ? 'proyecto' : 'persona'; + await c.query( + `INSERT INTO artista (id, slug, tipo, nombre, estado) + VALUES ($1,$2,$3,$4,'publicado')`, + [id, slugificar(limpio), tipo, limpio] + ); + if (tipo === 'persona') { + await c.query( + `INSERT INTO artista_persona (artista_id, persona_id, relacion) + VALUES ($1,$2,'identidad')`, + [id, personaId] + ); + } + artistas.set(limpio, id); + return id; + }; + /* --- Etiquetas ------------------------------------------------------- */ const etiquetas = new Map(); const etiqueta = async (nombre) => { @@ -267,7 +306,7 @@ async function volcar(c, contenido, informe) { for (const [rol, nombres] of [ ['letra', datos.letra], ['musica', datos.musica], - ['interprete', datos.interpretes] + ['interprete', datos.interpretes.map((interprete) => interprete.nombre)] ]) { for (const [i, nombre] of (nombres ?? []).entries()) { await c.query( @@ -277,6 +316,15 @@ async function volcar(c, contenido, informe) { ); } } + for (const [i, interprete] of datos.interpretes.entries()) { + const personaId = await persona(interprete.nombre); + const artistaId = await artista(interprete.nombre, personaId); + await c.query( + `INSERT INTO cancion_interprete (cancion_id, artista_id, rol, acreditado_como, orden) + VALUES ($1,$2,$3,$4,$5)`, + [id, artistaId, interprete.rol, interprete.nombre, i] + ); + } for (const nombre of datos.etiquetas ?? []) { await c.query( @@ -338,17 +386,19 @@ async function volcar(c, contenido, informe) { * «no lo sé» o «lo mismo de arriba». */ for (const [i, v] of (datos.versiones ?? []).entries()) { + const versionId = nuevoId(); await c.query( `INSERT INTO cancion (id, slug, titulo, estilo_id, version_de_id, version_nombre, - version_orden, duracion_s, ano, letra, idioma, instrumental, estado) - VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,'publicado')`, + version_codigo, version_orden, duracion_s, ano, letra, idioma, instrumental, estado) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,'publicado')`, [ - nuevoId(), + versionId, `${datos.slug}-${v.id}`, datos.titulo, idEstilo.get(datos.estilo), id, v.nombre, + v.id, i, v.duracion ?? null, datos.ano ?? null, @@ -357,6 +407,15 @@ async function volcar(c, contenido, informe) { datos.instrumental ] ); + for (const [orden, interprete] of datos.interpretes.entries()) { + const personaId = await persona(interprete.nombre); + const artistaId = await artista(interprete.nombre, personaId); + await c.query( + `INSERT INTO cancion_interprete (cancion_id, artista_id, rol, acreditado_como, orden) + VALUES ($1,$2,$3,$4,$5)`, + [versionId, artistaId, interprete.rol, interprete.nombre, orden] + ); + } versiones++; } } diff --git a/scripts/medios.mjs b/scripts/medios.mjs index 142119f..ba70217 100644 --- a/scripts/medios.mjs +++ b/scripts/medios.mjs @@ -3,17 +3,16 @@ * puede preparar. * * El panel de contenido escribe archivos en el repositorio, así que subir el - * máster desde ahí es fácil. Lo que no puede hacer es recortar el fragmento de - * muestra ni medir la duración: eso es ffmpeg, y ffmpeg no corre dentro de una - * pestaña. Este script cierra ese hueco. + * máster desde ahí es fácil. Lo que no puede hacer es generar la copia pública + * completa de menor calidad ni medir la duración: eso es ffmpeg. * * Qué hace, por cada canción de `src/content/canciones/`: * * 1. Si su clave de audio apunta a la bandeja (`subidas/…`), mueve el archivo * a la carpeta que le toca —la del disco, o `singles/`— con el nombre de * la convención, y reescribe la clave en el Markdown. - * 2. Si falta el fragmento de muestra, o es más viejo que el máster, lo - * genera. + * 2. Si falta la copia completa de escucha, o es más vieja que el máster, la + * genera a 96 kbps. * 3. Si no tiene duración escrita, la mide y la escribe. * * Corre solo antes de `npm run dev` y de `npm run build`, así que subir un tema @@ -21,7 +20,8 @@ * `npm run medios`. * * Sin ffmpeg no falla: avisa de lo que no ha podido hacer y sigue. Un entorno - * de despliegue no lo necesita, porque las muestras van en el repositorio. + * de despliegue no lo necesita, porque las copias de escucha van en el + * repositorio. */ import { spawnSync } from 'node:child_process'; @@ -29,17 +29,17 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { load as leerYaml } from 'js-yaml'; +import { PERFIL_ESCUCHA } from './config/audio.mjs'; const RAIZ = path.resolve(import.meta.dirname, '..'); const CANCIONES = path.join(RAIZ, 'src/content/canciones'); const MASTERS = path.join(RAIZ, 'media/audio'); -const MUESTRAS = path.join(RAIZ, 'static/muestras'); +const ESCUCHAS = path.join(RAIZ, 'static/escucha'); /** Carpeta donde deja el panel lo que se sube; nada se queda aquí. */ const BANDEJA = 'subidas'; -/** Fragmento de muestra: 40 s desde el segundo 30, con 4 s de fundido final. */ -const MUESTRA = { desde: 30, dura: 40, fundido: 4 }; +/** Perfil audible pero claramente inferior al archivo entregado al comprar. */ const avisos = []; const hechos = []; @@ -121,64 +121,57 @@ function colocar(claveVieja, claveNueva) { return true; } -/** - * Los argumentos del recorte. - * - * `-ss` va **antes** de `-i`, y eso no es cosmético. Detrás de `-i` el corte se - * hace a la salida, pero el filtro sigue viendo los tiempos del original: un - * `afade` en el segundo 36 se aplicaría en el 36 de la canción, es decir a los - * seis de empezar el fragmento, y el resto saldría mudo. Delante de `-i` los - * tiempos se reinician en cero y el fundido cae donde debe. - */ -export function argumentosDeMuestra(master, muestra, desde, dura) { - const fundido = Math.max(0, dura - MUESTRA.fundido); +/** Nombre equivalente de la copia pública: siempre MP3. */ +export function claveDeEscucha(clave) { + return clave.replace(/\.[^.]+$/, '') + '.mp3'; +} + +/** Argumentos deterministas de la transcodificación completa de escucha. */ +export function argumentosDeEscucha(master, escucha) { return [ '-v', 'error', '-y', - '-ss', - String(desde), - '-t', - String(dura), '-i', master, - '-af', - `afade=t=out:st=${fundido}:d=${MUESTRA.fundido}`, - muestra + '-vn', + '-map_metadata', + '-1', + '-codec:a', + PERFIL_ESCUCHA.codecFfmpeg, + '-b:a', + `${PERFIL_ESCUCHA.bitrateKbps}k`, + escucha ]; } -/** Recorta el fragmento público si falta o si el máster es más nuevo. */ -function generarMuestra(clave) { +/** Genera la canción pública completa si falta o si el máster es más nuevo. */ +function generarEscucha(clave) { const master = path.join(MASTERS, clave); - const muestra = path.join(MUESTRAS, clave); + const escucha = path.join(ESCUCHAS, claveDeEscucha(clave)); if (!fs.existsSync(master)) { - avisos.push(`${clave}: no hay máster en media/audio/, no se puede generar la muestra.`); + avisos.push(`${clave}: no hay máster en media/audio/, no se puede generar la escucha.`); return; } - if (fs.existsSync(muestra) && fs.statSync(muestra).mtimeMs >= fs.statSync(master).mtimeMs) { + if (fs.existsSync(escucha) && fs.statSync(escucha).mtimeMs >= fs.statSync(master).mtimeMs) { return; } if (!hayFfmpeg) { - avisos.push(`${clave}: falta el fragmento de muestra y no hay ffmpeg para generarlo.`); + avisos.push(`${clave}: falta la copia de escucha y no hay ffmpeg para generarla.`); return; } - // Un tema corto no da para empezar en el segundo 30: se recorta desde el - // principio y se toma lo que haya, para que la muestra no salga vacía. - const total = medir(master) ?? 0; - const desde = total > MUESTRA.desde + MUESTRA.dura ? MUESTRA.desde : 0; - const dura = Math.min(MUESTRA.dura, Math.max(1, total - desde)); - - fs.mkdirSync(path.dirname(muestra), { recursive: true }); - const hecho = ejecutar('ffmpeg', argumentosDeMuestra(master, muestra, desde, dura)); + fs.mkdirSync(path.dirname(escucha), { recursive: true }); + const hecho = ejecutar('ffmpeg', argumentosDeEscucha(master, escucha)); if (hecho === undefined) { - avisos.push(`${clave}: ffmpeg no pudo generar la muestra.`); + avisos.push(`${clave}: ffmpeg no pudo generar la escucha completa.`); return; } - hechos.push(`muestra ${clave} (${dura} s desde ${desde})`); + hechos.push( + `escucha ${claveDeEscucha(clave)} (${PERFIL_ESCUCHA.bitrateKbps} kbps, ${PERFIL_ESCUCHA.id})` + ); } /** @@ -228,7 +221,7 @@ function procesar(archivo) { } } - generarMuestra(clave); + generarEscucha(clave); if (!datos.duracion && hayFfmpeg) { const segundos = medir(path.join(MASTERS, clave)); @@ -257,7 +250,7 @@ function procesar(archivo) { } } - generarMuestra(clave); + generarEscucha(clave); } if (tocado) { diff --git a/scripts/medios.spec.mjs b/scripts/medios.spec.mjs index 425e0b6..259b599 100644 --- a/scripts/medios.spec.mjs +++ b/scripts/medios.spec.mjs @@ -1,5 +1,12 @@ import { describe, expect, it } from 'vitest'; -import { argumentosDeMuestra, aSlug, claveCanonica, comoDuracion, enLaBandeja } from './medios.mjs'; +import { + argumentosDeEscucha, + aSlug, + claveCanonica, + claveDeEscucha, + comoDuracion, + enLaBandeja +} from './medios.mjs'; describe('claveCanonica', () => { it('agrupa por disco y numera como la pista', () => { @@ -68,34 +75,20 @@ describe('aSlug', () => { }); }); -describe('argumentosDeMuestra', () => { - const args = argumentosDeMuestra( - 'media/audio/singles/dorina.mp3', - 'static/muestras/singles/dorina.mp3', - 30, - 40 - ); - - it('corta a la entrada, no a la salida', () => { - // Con `-ss` detrás de `-i` el filtro sigue viendo los tiempos del - // original: el fundido cae seis segundos después de empezar y el resto - // del fragmento sale mudo. Pasó de verdad. - expect(args.indexOf('-ss')).toBeLessThan(args.indexOf('-i')); - }); - - it('funde los últimos cuatro segundos, no otros', () => { - expect(args).toContain('afade=t=out:st=36:d=4'); +describe('escucha pública', () => { + it('conserva la jerarquía y normaliza el formato', () => { + expect(claveDeEscucha('disco/01-tema.wav')).toBe('disco/01-tema.mp3'); }); - it('no funde antes de empezar en un fragmento corto', () => { - // Un tema de tres segundos no da para un fundido de cuatro: el inicio del - // fundido no puede ser negativo. - const cortos = argumentosDeMuestra('a.mp3', 'b.mp3', 0, 3); - expect(cortos).toContain('afade=t=out:st=0:d=4'); - }); - - it('recorta lo que se le pide', () => { - expect(args[args.indexOf('-ss') + 1]).toBe('30'); - expect(args[args.indexOf('-t') + 1]).toBe('40'); + it('codifica la canción completa a menor calidad', () => { + const args = argumentosDeEscucha( + 'media/audio/singles/dorina.wav', + 'static/escucha/singles/dorina.mp3' + ); + expect(args).not.toContain('-ss'); + expect(args).not.toContain('-t'); + expect(args).toContain('libmp3lame'); + expect(args).toContain('96k'); + expect(args.at(-1)).toBe('static/escucha/singles/dorina.mp3'); }); }); diff --git a/src/content/legal/condiciones-de-uso.md b/src/content/legal/condiciones-de-uso.md index 3d8ddba..853ec8e 100644 --- a/src/content/legal/condiciones-de-uso.md +++ b/src/content/legal/condiciones-de-uso.md @@ -1,7 +1,7 @@ --- titulo: 'Condiciones de uso' resumen: 'Qué puedes hacer en esta web y qué no.' -actualizada: 2026-09-07 +actualizada: 2026-09-10 orden: 2 --- @@ -29,12 +29,13 @@ tiempo que obliga la ley, según se explica en la ## Lo que suena y lo que se compra -En la web **suena un fragmento** de cada tema, no la canción entera. Es una -muestra para que decidas. +En la web se puede escuchar **la canción completa en una copia de calidad +reducida**. Esa copia es solo para escucharla desde el sitio: que sea accesible +no concede permiso para descargarla, redistribuirla ni reutilizarla. -Al comprar recibes el archivo completo en MP3, con todas sus versiones -alternativas, y queda vinculado a tu cuenta: puedes volver a descargarlo desde -ella siempre que quieras. +Al comprar recibes el archivo de mayor calidad disponible, con todas sus +versiones alternativas, y queda vinculado a tu cuenta: puedes volver a +descargarlo desde ella siempre que quieras. ## Lo que no se puede hacer @@ -49,8 +50,8 @@ la ficha de cada tema para poder pedirla. Con la web: -- Intentar acceder a partes que no te corresponden, o a los archivos completos - sin haberlos comprado. +- Intentar acceder a partes que no te corresponden, o a los archivos originales + o de descarga sin haberlos comprado. - Automatizar peticiones de forma que degraden el servicio. - Usar el formulario de acceso o el de contacto para molestar a terceros. diff --git a/src/lib/audio.ts b/src/lib/audio.ts index f723137..410897a 100644 --- a/src/lib/audio.ts +++ b/src/lib/audio.ts @@ -1,33 +1,33 @@ /** * Rutas de audio. * - * El catalogo se vende, asi que el archivo completo no vive en `static/`: se - * guarda en un directorio privado y solo lo entrega el servidor a quien lo ha - * comprado. Lo unico publico es un fragmento de muestra. + * La escucha pública contiene la canción completa con una codificación de + * menor calidad. El máster y la descarga siguen en un almacén privado. * * En el contenido, cada tema declara una *clave* relativa * (`como-si-nada/01-como-si-nada.mp3`). De esa clave salen las dos rutas: * - * muestra publica static/muestras/ -> /muestras/ + * escucha publica static/escucha/ -> /escucha/ * archivo completo / -> /descargas/ */ import { resolve } from '$app/paths'; import type { ResolvedPathname } from '$app/types'; -/** Prefijo publico de los fragmentos de muestra. */ -export const RUTA_MUESTRAS = '/muestras'; +/** Prefijo público de las copias completas de escucha. */ +export const RUTA_ESCUCHA = '/escucha'; /** - * URL publica del fragmento de muestra de una clave de audio. + * URL pública de escucha de una clave de audio. * * No pasa por `asset()` porque las claves salen del contenido y son dinamicas, * mientras que `asset()` exige un archivo conocido en tiempo de compilacion. Si * algun dia el sitio se sirviera bajo un subdirectorio, esta funcion es el * unico sitio donde habria que anteponer la base. */ -export function urlMuestra(clave: string): string { - return `${RUTA_MUESTRAS}/${clave}`; +export function urlEscucha(clave: string): string { + if (clave.startsWith('/')) return clave; + return `${RUTA_ESCUCHA}/${clave}`; } /** diff --git a/src/lib/components/FichaDeCancion.svelte b/src/lib/components/FichaDeCancion.svelte index 81ca38b..6c8bbfb 100644 --- a/src/lib/components/FichaDeCancion.svelte +++ b/src/lib/components/FichaDeCancion.svelte @@ -125,7 +125,19 @@
{listaDeNombres(cancion.musica)}
{#if cancion.interpretes.length > 0}
Interpretación
-
{listaDeNombres(cancion.interpretes)}
+
+ {#each cancion.interpretes as interprete, i (interprete.id + interprete.rol)} + {#if i > 0}, + {/if} + {#if interprete.slug} + {interprete.nombre} + {:else} + {interprete.nombre} + {/if} + {/each} +
{/if} diff --git a/src/lib/components/TablaCanciones.svelte b/src/lib/components/TablaCanciones.svelte index da4f3bb..cdf36a9 100644 --- a/src/lib/components/TablaCanciones.svelte +++ b/src/lib/components/TablaCanciones.svelte @@ -348,7 +348,8 @@ +{c.etiquetas.length - CUANTAS_ETIQUETAS} {/if} {#if c.generos.length === 0 && c.etiquetas.length === 0} - {listaDeNombres(c.interpretes)} + {listaDeNombres(c.interpretes.map((i) => i.nombre))} {/if} diff --git a/src/lib/components/TarjetaCancion.svelte.spec.ts b/src/lib/components/TarjetaCancion.svelte.spec.ts index 9e1a986..e73c5b4 100644 --- a/src/lib/components/TarjetaCancion.svelte.spec.ts +++ b/src/lib/components/TarjetaCancion.svelte.spec.ts @@ -1,4 +1,4 @@ -import { page } from '@vitest/browser/context'; +import { page } from 'vitest/browser'; import { describe, expect, it, vi } from 'vitest'; import { render } from 'vitest-browser-svelte'; import TarjetaCancion from './TarjetaCancion.svelte'; @@ -54,6 +54,7 @@ vi.mock('$app/state', () => ({ function cancion(parcial: Partial = {}): Cancion { return { + codigo: 'CAN-000001', slug: 'como-si-nada', titulo: 'Como si nada', estilo: 'baladas-romanticas', @@ -64,7 +65,16 @@ function cancion(parcial: Partial = {}): Cancion { ano: 2024, letra: ['Senza Paura'], musica: ['Senza Paura'], - interpretes: ['Marta Cifuentes'], + interpretes: [ + { + id: 'marta-cifuentes', + codigo: 'ART-000001', + slug: 'marta-cifuentes', + nombre: 'Marta Cifuentes', + tipo: 'persona', + rol: 'principal' + } + ], destacada: true, etiquetas: ['Desamor', 'Dueto'], generos: [], @@ -86,7 +96,7 @@ function pista(id = 'como-si-nada'): PistaReproductor { id, cancionSlug: id, titulo: 'Como si nada', - audio: '/muestras/como-si-nada/01-como-si-nada.mp3', + audio: '/escucha/como-si-nada/01-como-si-nada.mp3', albumTitulo: 'Como si nada', albumSlug: 'como-si-nada', portada: '/images/albumes/como-si-nada.svg', diff --git a/src/lib/components/player/BarraReproductor.svelte b/src/lib/components/player/BarraReproductor.svelte index 551e118..e92c500 100644 --- a/src/lib/components/player/BarraReproductor.svelte +++ b/src/lib/components/player/BarraReproductor.svelte @@ -114,12 +114,7 @@ {pista.titulo} - - Muestra + Calidad de escucha

{pista.albumTitulo} @@ -259,7 +254,7 @@ min-width: 0; } - .muestra { + .calidad { flex: none; padding: 0 0.4em; border: 1px solid var(--c-border); @@ -350,7 +345,7 @@ /* * El mismo metal que los botones del sitio: la clase `.oro` de `app.css` * pone el degradado, el filo de luz y el barrido al pasar por encima. Antes - * era `--c-accent` plano y, al lado de un «Escuchar la muestra» dorado, + * era `--c-accent` plano y, al lado de un botón de escucha dorado, * parecía otro color. */ .control.principal { diff --git a/src/lib/content/schema.ts b/src/lib/content/schema.ts index 594ac1c..6a8dae3 100644 --- a/src/lib/content/schema.ts +++ b/src/lib/content/schema.ts @@ -208,10 +208,13 @@ function parsearVersion(datos: Datos, indice: number, archivo: string): VersionM // Sin `id` explicito se deriva del nombre: basta para distinguirlas // dentro del tema, que es el unico ambito en el que viven. id: textoOpcional(datos, 'id', `${archivo} (${campo})`) ?? aSlug(nombre), + codigo: `LEG-V-${indice + 1}`, + slug: aSlug(nombre), nombre, audio, duracion: duracion(datos, 'duracion', `${archivo} (${campo})`), - nota: textoOpcional(datos, 'nota', `${archivo} (${campo})`) + nota: textoOpcional(datos, 'nota', `${archivo} (${campo})`), + interpretes: [] }; } @@ -237,6 +240,7 @@ export function parsearCancion(datos: Datos, slug: string, archivo: string): Can } return { + codigo: `LEG-${slug.toUpperCase()}`, slug, titulo: texto(datos, 'titulo', archivo), estilo: texto(datos, 'estilo', archivo), @@ -250,7 +254,13 @@ export function parsearCancion(datos: Datos, slug: string, archivo: string): Can // La autoria por defecto es del artista: es su propia web. letra: listaDeTextos(datos, 'letra', archivo, autoria), musica: listaDeTextos(datos, 'musica', archivo, autoria), - interpretes: listaDeTextos(datos, 'interpretes', archivo), + interpretes: listaDeTextos(datos, 'interpretes', archivo).map((nombre, indice) => ({ + id: `legacy-${aSlug(nombre)}`, + codigo: `LEG-ART-${indice + 1}`, + nombre, + tipo: 'persona', + rol: 'principal' + })), destacada: booleano(datos, 'destacada', archivo), etiquetas: listaDeTextos(datos, 'etiquetas', archivo), // Los generos viven en la base, no en el frontmatter: este lector es del diff --git a/src/lib/navegacion.ts b/src/lib/navegacion.ts index 3e16ec6..a251340 100644 --- a/src/lib/navegacion.ts +++ b/src/lib/navegacion.ts @@ -14,6 +14,7 @@ import type { ResolvedPathname } from '$app/types'; */ export const navegacion: { href: ResolvedPathname; texto: string }[] = [ { href: resolve('/musica'), texto: 'Música' }, + { href: resolve('/interpretes'), texto: 'Intérpretes' }, { href: resolve('/taller'), texto: 'Taller de letras' }, { href: resolve('/generos'), texto: 'Géneros' }, { href: resolve('/blog'), texto: 'Blog' }, diff --git a/src/lib/player/pista.ts b/src/lib/player/pista.ts index be22749..6822cb6 100644 --- a/src/lib/player/pista.ts +++ b/src/lib/player/pista.ts @@ -5,11 +5,10 @@ * desnormalizadas, con su portada y sus etiquetas resueltas. Esta es la unica * conversion entre los dos mundos. * - * Lo que suena en la web es siempre el fragmento de muestra, nunca el archivo - * completo: ese solo se entrega por descarga a quien lo ha comprado. + * Lo que suena en la web es la canción completa en calidad de escucha. El + * archivo de compra permanece privado y se entrega por una ruta autorizada. */ -import { urlMuestra } from '$lib/audio'; /* * Del catálogo que baja por `page.data`, no de los archivos. `aPistas` se * llama siempre dentro de un `$derived` de un componente, así que la lectura @@ -43,7 +42,7 @@ export function aPista(cancion: Cancion): PistaReproductor { id: cancion.slug, cancionSlug: cancion.slug, titulo: cancion.titulo, - audio: cancion.audio ? urlMuestra(cancion.audio) : '', + audio: cancion.audio ?? '', duracion: cancion.duracion, ...contexto(cancion) }; @@ -60,7 +59,7 @@ export function aPistaDeVersion(cancion: Cancion, version: VersionMusical): Pist id: `${cancion.slug}#${version.id}`, cancionSlug: cancion.slug, titulo: `${cancion.titulo} · ${version.nombre}`, - audio: urlMuestra(version.audio), + audio: version.audio ?? '', duracion: version.duracion, ...contexto(cancion) }; diff --git a/src/lib/player/player.svelte.ts b/src/lib/player/player.svelte.ts index 6a82bc2..f867141 100644 --- a/src/lib/player/player.svelte.ts +++ b/src/lib/player/player.svelte.ts @@ -31,7 +31,7 @@ export interface PistaReproductor { /** Slug del tema al que pertenece, para enlazar a su ficha. */ cancionSlug: string; titulo: string; - /** URL publica del fragmento de muestra. */ + /** URL pública de la canción completa en calidad de escucha. */ audio: string; albumTitulo: string; albumSlug: string; diff --git a/src/lib/server/artistas.ts b/src/lib/server/artistas.ts new file mode 100644 index 0000000..90689bc --- /dev/null +++ b/src/lib/server/artistas.ts @@ -0,0 +1,138 @@ +import { and, asc, eq } from 'drizzle-orm'; +import { alias } from 'drizzle-orm/pg-core'; +import { db } from './db'; +import { artista, artistaIdentificador, medio, pais } from './db/schema'; +import { leerCatalogo } from './catalogo'; +import { componer } from './markdown'; +import type { Cancion, TipoArtista } from '$lib/types'; + +export interface ResumenDeArtista { + id: string; + codigo: string; + slug: string; + nombre: string; + tipo: TipoArtista; + descripcion: string; + imagen?: string; + pais?: string; + lugarOrigen?: string; + inicio?: number; + fin?: number; + totalCanciones: number; +} + +export interface FichaDeArtista extends ResumenDeArtista { + biografia: string; + sitioWeb?: string; + identificadores: { sistema: string; valor: string }[]; + canciones: Cancion[]; +} + +function rutaPublica(clave: string | null): string | undefined { + return clave ? `/${clave}` : undefined; +} + +function participa(cancion: Cancion, artistaId: string): boolean { + return ( + cancion.interpretes.some((i) => i.id === artistaId) || + cancion.versiones.some((v) => v.interpretes.some((i) => i.id === artistaId)) + ); +} + +/** Directorio público de identidades artísticas, nunca personas en borrador. */ +export async function artistasPublicos(): Promise { + const imagen = alias(medio, 'imagen_artista_publica'); + const [filas, catalogo] = await Promise.all([ + db + .select({ + id: artista.id, + codigo: artista.codigo, + slug: artista.slug, + nombre: artista.nombre, + tipo: artista.tipo, + descripcion: artista.descripcionCorta, + imagenClave: imagen.clave, + pais: pais.nombre, + lugarOrigen: artista.lugarOrigen, + inicio: artista.inicio, + fin: artista.fin + }) + .from(artista) + .leftJoin(imagen, eq(imagen.id, artista.imagenId)) + .leftJoin(pais, eq(pais.codigo, artista.paisCodigo)) + .where(eq(artista.estado, 'publicado')) + .orderBy(asc(artista.nombre)), + leerCatalogo() + ]); + + return filas.map((fila) => ({ + id: fila.id, + codigo: fila.codigo, + slug: fila.slug, + nombre: fila.nombre, + tipo: fila.tipo, + descripcion: fila.descripcion, + imagen: rutaPublica(fila.imagenClave), + pais: fila.pais ?? undefined, + lugarOrigen: fila.lugarOrigen ?? undefined, + inicio: fila.inicio ?? undefined, + fin: fila.fin ?? undefined, + totalCanciones: catalogo.canciones.filter((c) => participa(c, fila.id)).length + })); +} + +/** Ficha pública y discografía de una identidad artística. */ +export async function obtenerArtistaPublico(slug: string): Promise { + const imagen = alias(medio, 'imagen_artista_ficha'); + const [fila] = await db + .select({ + id: artista.id, + codigo: artista.codigo, + slug: artista.slug, + nombre: artista.nombre, + tipo: artista.tipo, + descripcion: artista.descripcionCorta, + biografia: artista.biografia, + imagenClave: imagen.clave, + pais: pais.nombre, + lugarOrigen: artista.lugarOrigen, + inicio: artista.inicio, + fin: artista.fin, + sitioWeb: artista.sitioWeb + }) + .from(artista) + .leftJoin(imagen, eq(imagen.id, artista.imagenId)) + .leftJoin(pais, eq(pais.codigo, artista.paisCodigo)) + .where(and(eq(artista.slug, slug), eq(artista.estado, 'publicado'))) + .limit(1); + + if (!fila) return undefined; + const [catalogo, identificadores] = await Promise.all([ + leerCatalogo(), + db + .select({ sistema: artistaIdentificador.sistema, valor: artistaIdentificador.valor }) + .from(artistaIdentificador) + .where(eq(artistaIdentificador.artistaId, fila.id)) + .orderBy(asc(artistaIdentificador.sistema)) + ]); + const canciones = catalogo.canciones.filter((c) => participa(c, fila.id)); + + return { + id: fila.id, + codigo: fila.codigo, + slug: fila.slug, + nombre: fila.nombre, + tipo: fila.tipo, + descripcion: fila.descripcion, + biografia: componer(fila.biografia), + imagen: rutaPublica(fila.imagenClave), + pais: fila.pais ?? undefined, + lugarOrigen: fila.lugarOrigen ?? undefined, + inicio: fila.inicio ?? undefined, + fin: fila.fin ?? undefined, + sitioWeb: fila.sitioWeb ?? undefined, + identificadores, + canciones, + totalCanciones: canciones.length + }; +} diff --git a/src/lib/server/audios.ts b/src/lib/server/audios.ts new file mode 100644 index 0000000..1e064a0 --- /dev/null +++ b/src/lib/server/audios.ts @@ -0,0 +1,76 @@ +/** Selección servidor-a-servidor del archivo privado que incluye una compra. */ + +import { and, asc, eq, inArray, isNull, ne, sql } from 'drizzle-orm'; +import { db } from './db'; +import { cancion, cancionAudio, medio } from './db/schema'; + +export interface ArchivoComprado { + cancionId: string; + cancionSlug: string; + principalSlug: string; + titulo: string; + versionNombre?: string; + clave: string; +} + +/** + * Resuelve primero la entidad musical y después una representación privada. + * La descarga explícita gana al máster; ninguno de los dos cruza el contrato + * público del catálogo. + */ +export async function archivoComprado( + principalSlug: string, + versionCodigo?: string +): Promise { + const [principal] = await db + .select({ id: cancion.id, slug: cancion.slug, titulo: cancion.titulo }) + .from(cancion) + .where(and(eq(cancion.slug, principalSlug), isNull(cancion.versionDeId))) + .limit(1); + if (!principal) return undefined; + + const destino = versionCodigo + ? ( + await db + .select({ + id: cancion.id, + slug: cancion.slug, + titulo: cancion.titulo, + versionNombre: cancion.versionNombre + }) + .from(cancion) + .where( + and( + eq(cancion.versionDeId, principal.id), + eq(cancion.versionCodigo, versionCodigo), + ne(cancion.estado, 'borrador') + ) + ) + .limit(1) + )[0] + : { ...principal, versionNombre: null }; + if (!destino) return undefined; + + const [audio] = await db + .select({ clave: medio.clave }) + .from(cancionAudio) + .innerJoin(medio, eq(medio.id, cancionAudio.medioId)) + .where( + and(eq(cancionAudio.cancionId, destino.id), inArray(cancionAudio.uso, ['descarga', 'master'])) + ) + .orderBy( + sql`case when ${cancionAudio.uso} = 'descarga' then 0 else 1 end`, + asc(cancionAudio.orden) + ) + .limit(1); + if (!audio) return undefined; + + return { + cancionId: destino.id, + cancionSlug: destino.slug, + principalSlug: principal.slug, + titulo: principal.titulo, + versionNombre: destino.versionNombre ?? undefined, + clave: audio.clave.replace(/^audio\//, '') + }; +} diff --git a/src/lib/server/catalogo.ts b/src/lib/server/catalogo.ts index 294fea6..8a20887 100644 --- a/src/lib/server/catalogo.ts +++ b/src/lib/server/catalogo.ts @@ -30,11 +30,14 @@ import { db } from './db'; import { album, albumEnlace, + artista, autoria, cancion, + cancionAudio, cancionEnlace, cancionEtiqueta, cancionGenero, + cancionInterprete, cancionPersona, creditoAlbum, estilo, @@ -118,15 +121,17 @@ async function cargar(): Promise { * `medio` por su audio y otra vez por su portada, y sin alias Postgres ve * un solo `medio` y devuelve la misma fila para los dos. */ - const audio = alias(medio, 'audio'); const portadaCancion = alias(medio, 'portada_cancion'); const portadaAlbum = alias(medio, 'portada_album'); + const imagenArtista = alias(medio, 'imagen_artista'); const [ filasEstilo, filasAlbum, filasCancion, personas, + interpretes, + audiosDeEscucha, etiquetas, enlacesCancion, enlacesAlbum, @@ -163,14 +168,11 @@ async function cargar(): Promise { fila: cancion, estiloSlug: estilo.slug, albumSlug: album.slug, - audioClave: audio.clave, - audioDuracion: audio.duracionS, portadaClave: portadaCancion.clave }) .from(cancion) .innerJoin(estilo, eq(estilo.id, cancion.estiloId)) .leftJoin(album, eq(album.id, cancion.albumId)) - .leftJoin(audio, eq(audio.id, cancion.audioId)) .leftJoin(portadaCancion, eq(portadaCancion.id, cancion.portadaId)) .where( and( @@ -192,6 +194,36 @@ async function cargar(): Promise { .innerJoin(persona, eq(persona.id, cancionPersona.personaId)) .orderBy(asc(cancionPersona.orden)), + db + .select({ + cancionId: cancionInterprete.cancionId, + id: artista.id, + codigo: artista.codigo, + slug: artista.slug, + nombre: artista.nombre, + tipo: artista.tipo, + estado: artista.estado, + rol: cancionInterprete.rol, + instrumento: cancionInterprete.instrumento, + acreditadoComo: cancionInterprete.acreditadoComo, + imagenClave: imagenArtista.clave + }) + .from(cancionInterprete) + .innerJoin(artista, eq(artista.id, cancionInterprete.artistaId)) + .leftJoin(imagenArtista, eq(imagenArtista.id, artista.imagenId)) + .orderBy(asc(cancionInterprete.orden), asc(artista.nombre)), + + db + .select({ + cancionId: cancionAudio.cancionId, + clave: medio.clave, + duracion: medio.duracionS + }) + .from(cancionAudio) + .innerJoin(medio, eq(medio.id, cancionAudio.medioId)) + .where(and(eq(cancionAudio.uso, 'stream'), eq(medio.publico, true))) + .orderBy(asc(cancionAudio.orden)), + db .select({ cancionId: cancionEtiqueta.cancionId, nombre: etiqueta.nombre }) .from(cancionEtiqueta) @@ -265,12 +297,14 @@ async function cargar(): Promise { const porCancion = { personas: porClave(personas, (p) => p.cancionId), + interpretes: porClave(interpretes, (i) => i.cancionId), etiquetas: porClave(etiquetas, (e) => e.cancionId), generos: porClave(generosDeCancion, (g) => g.cancionId), enlaces: porClave(enlacesCancion, (e) => e.cancionId), videos: porClave(videos, (v) => v.cancionId), partes: porClave(partes, (p) => p.cancionId) }; + const audioDeEscucha = new Map(audiosDeEscucha.map((a) => [a.cancionId, a])); const traduccionDe = new Map(traducciones.map((t) => [t.cancionId, t.texto])); const registroDe = new Map(registros.map((r) => [r.cancionId, r])); const enlacesDeAlbum = porClave(enlacesAlbum, (e) => e.albumId); @@ -331,20 +365,39 @@ async function cargar(): Promise { const canciones: Cancion[] = filasCancion .filter((f) => f.fila.versionDeId === null) - .map(({ fila, estiloSlug, albumSlug, audioClave, audioDuracion, portadaClave }) => { + .map(({ fila, estiloSlug, albumSlug, portadaClave }) => { const suyas = porCancion.personas.get(fila.id) ?? []; + const susInterpretes = porCancion.interpretes.get(fila.id) ?? []; + const escucha = audioDeEscucha.get(fila.id); const registro = registroDe.get(fila.id); const misPartes = porCancion.partes.get(fila.id) ?? []; const versiones: VersionMusical[] = (versionesDe.get(fila.id) ?? []) .sort((a, b) => a.fila.versionOrden - b.fila.versionOrden) - .map((v) => ({ - id: v.fila.slug.replace(`${fila.slug}-`, ''), - nombre: v.fila.versionNombre ?? '', - audio: v.audioClave?.replace(/^audio\//, '') ?? '', - duracion: v.fila.duracionS ?? v.audioDuracion ?? undefined, - nota: v.fila.versionNota ?? undefined - })); + .map((v) => { + const escuchaVersion = audioDeEscucha.get(v.fila.id); + const interpretesVersion = porCancion.interpretes.get(v.fila.id) ?? []; + return { + id: v.fila.versionCodigo ?? v.fila.slug.replace(`${fila.slug}-`, ''), + codigo: v.fila.codigo, + slug: v.fila.slug, + nombre: v.fila.versionNombre ?? '', + audio: rutaPublica(escuchaVersion?.clave ?? null), + duracion: v.fila.duracionS ?? escuchaVersion?.duracion ?? undefined, + nota: v.fila.versionNota ?? undefined, + interpretes: interpretesVersion.map((i) => ({ + id: i.id, + codigo: i.codigo, + slug: i.estado === 'publicado' ? i.slug : undefined, + nombre: i.acreditadoComo ?? i.nombre, + tipo: i.tipo, + rol: i.rol, + instrumento: i.instrumento ?? undefined, + acreditadoComo: i.acreditadoComo ?? undefined, + imagen: i.estado === 'publicado' ? rutaPublica(i.imagenClave) : undefined + })) + }; + }); const registroDeObra: RegistroDeObra = { iswc: registro?.iswc ?? undefined, @@ -365,17 +418,28 @@ async function cargar(): Promise { })); return { + codigo: fila.codigo, slug: fila.slug, titulo: fila.titulo, estilo: estiloSlug, album: albumSlug ?? null, numero: fila.numero, - audio: audioClave?.replace(/^audio\//, ''), - duracion: fila.duracionS ?? audioDuracion ?? undefined, + audio: rutaPublica(escucha?.clave ?? null), + duracion: fila.duracionS ?? escucha?.duracion ?? undefined, ano: fila.ano ?? undefined, letra: suyas.filter((p) => p.rol === 'letra').map((p) => p.nombre), musica: suyas.filter((p) => p.rol === 'musica').map((p) => p.nombre), - interpretes: suyas.filter((p) => p.rol === 'interprete').map((p) => p.nombre), + interpretes: susInterpretes.map((i) => ({ + id: i.id, + codigo: i.codigo, + slug: i.estado === 'publicado' ? i.slug : undefined, + nombre: i.acreditadoComo ?? i.nombre, + tipo: i.tipo, + rol: i.rol, + instrumento: i.instrumento ?? undefined, + acreditadoComo: i.acreditadoComo ?? undefined, + imagen: i.estado === 'publicado' ? rutaPublica(i.imagenClave) : undefined + })), destacada: fila.destacada, etiquetas: (porCancion.etiquetas.get(fila.id) ?? []).map((e) => e.nombre), generos: (porCancion.generos.get(fila.id) ?? []).map((g) => ({ diff --git a/src/lib/server/db/schema/artistas.ts b/src/lib/server/db/schema/artistas.ts index ffa745e..8e73ba7 100644 --- a/src/lib/server/db/schema/artistas.ts +++ b/src/lib/server/db/schema/artistas.ts @@ -6,6 +6,7 @@ import { check, index, integer, + pgSequence, pgTable, primaryKey, text, @@ -16,6 +17,7 @@ import { ahora, estado, id, instante, procedencia } from './comun'; export const TIPOS_ARTISTA = ['persona', 'grupo', 'proyecto'] as const; export const RELACIONES_ARTISTA_PERSONA = ['identidad', 'miembro', 'fundador'] as const; +export const artistaCodigoSeq = pgSequence('artista_codigo_seq'); /** * Una identidad acreditada ante el público. No equivale necesariamente a una diff --git a/src/lib/server/db/schema/base.ts b/src/lib/server/db/schema/base.ts index 6fcb8b6..cee4a8a 100644 --- a/src/lib/server/db/schema/base.ts +++ b/src/lib/server/db/schema/base.ts @@ -48,10 +48,9 @@ export const medio = pgTable( /** * Si se puede pedir por URL sin más. * - * Los fragmentos de muestra sí; el audio completo no, nunca. Hasta ahora - * eso lo garantizaba la carpeta en la que estaba el archivo —un directorio - * privado fuera de `build/`—, que es una garantía que depende de que nadie - * mueva nada. Aquí es un dato, y el que sirve los archivos lo comprueba. + * Las copias completas de escucha sí; los másteres y descargas no. Antes + * eso dependía solo de la carpeta física. Aquí es un dato y cada consumidor + * selecciona además el uso concreto en `cancion_audio`. * * Con los medios en su propio dominio esto se vuelve más importante, no * menos: allí la cookie de sesión no viaja, así que lo privado se entrega diff --git a/src/lib/server/db/schema/catalogo.ts b/src/lib/server/db/schema/catalogo.ts index 667960c..ec6e89d 100644 --- a/src/lib/server/db/schema/catalogo.ts +++ b/src/lib/server/db/schema/catalogo.ts @@ -16,6 +16,7 @@ import { index, integer, numeric, + pgSequence, pgTable, primaryKey, text, @@ -25,6 +26,8 @@ import { ahora, estado, id, instante } from './comun'; import { medio, pais, persona } from './base'; import { artista } from './artistas'; +export const cancionCodigoSeq = pgSequence('cancion_codigo_seq'); + /** * Una sección del catálogo: baladas románticas, clásica, rock, pop melódico. * diff --git a/src/lib/server/descargas.ts b/src/lib/server/descargas.ts index 5618f99..7cc801e 100644 --- a/src/lib/server/descargas.ts +++ b/src/lib/server/descargas.ts @@ -1,12 +1,11 @@ /** Registro auditable del inicio de una descarga autorizada. */ -import { eq } from 'drizzle-orm'; import { db } from './db'; -import { cancion, descarga } from './db/schema'; +import { descarga } from './db/schema'; export interface EntregaIniciada { usuarioId: string; - cancionSlug: string; + cancionId: string; ip: string | null; agente: string | null; } @@ -16,17 +15,9 @@ export interface EntregaIniciada { * navegador haya terminado de guardarlo: HTTP no proporciona ese acuse. */ export async function registrarEntregaIniciada(datos: EntregaIniciada): Promise { - const [tema] = await db - .select({ id: cancion.id }) - .from(cancion) - .where(eq(cancion.slug, datos.cancionSlug)) - .limit(1); - - if (!tema) throw new Error(`No existe la canción descargada «${datos.cancionSlug}».`); - await db.insert(descarga).values({ usuarioId: datos.usuarioId, - cancionId: tema.id, + cancionId: datos.cancionId, ip: datos.ip, agente: datos.agente }); diff --git a/src/lib/server/markdown.spec.ts b/src/lib/server/markdown.spec.ts new file mode 100644 index 0000000..e40aac6 --- /dev/null +++ b/src/lib/server/markdown.spec.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; +import { componer } from './markdown'; + +describe('componer', () => { + it('conserva Markdown editorial', () => { + expect(componer('## Historia\n\nUna **voz**.')).toContain('voz'); + expect(componer('## Historia')).toContain('id="historia"'); + }); + + it('no deja ejecutar HTML ni protocolos activos', () => { + const html = componer('\n\n[abre](javascript:alert(1))'); + expect(html).not.toContain(' + +Artistas · Panel + +
+

Artistas

+

Personas, grupos y proyectos acreditados en las grabaciones.

+
+ +
+ + + + +
+{#if form?.error}

{form.error}

{/if} + + + + + {#each encontrados as artista (artista.id)} + + {/each} + +
IdentidadTipoEstadoGrabaciones
{artista.nombre}{artista.codigo} · {artista.slug}{artista.tipo}{artista.estado}{artista.grabaciones}
+ + diff --git a/src/panel/rutas/(dentro)/artistas/[slug]/+page.server.ts b/src/panel/rutas/(dentro)/artistas/[slug]/+page.server.ts new file mode 100644 index 0000000..7751ea1 --- /dev/null +++ b/src/panel/rutas/(dentro)/artistas/[slug]/+page.server.ts @@ -0,0 +1,58 @@ +import { error, fail } from '@sveltejs/kit'; +import { + artistaParaEditar, + guardarArtista, + opcionesDeArtista, + revisarArtista, + type CambiosDeArtista, + type Estado +} from '$lib/server/panel-artistas'; +import type { TipoArtista } from '$lib/types'; +import type { Actions, PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ params }) => { + const [artista, opciones] = await Promise.all([ + artistaParaEditar(params.slug), + opcionesDeArtista() + ]); + if (!artista) error(404, 'No existe esa identidad artística.'); + return { artista, ...opciones }; +}; + +const numero = (valor: FormDataEntryValue | null) => + String(valor ?? '').trim() ? Number(valor) : null; + +export const actions: Actions = { + guardar: async ({ request, params }) => { + const datos = await request.formData(); + const identificadores = String(datos.get('identificadores') ?? '') + .split('\n') + .map((l) => l.trim()) + .filter(Boolean) + .map((linea) => { + const corte = linea.indexOf(':'); + return { + sistema: corte < 0 ? '' : linea.slice(0, corte).trim(), + valor: corte < 0 ? linea : linea.slice(corte + 1).trim() + }; + }); + const cambios: CambiosDeArtista = { + nombre: String(datos.get('nombre') ?? ''), + tipo: String(datos.get('tipo') ?? 'persona') as TipoArtista, + descripcion: String(datos.get('descripcion') ?? ''), + biografia: String(datos.get('biografia') ?? ''), + imagenId: String(datos.get('imagenId') ?? '') || null, + paisCodigo: String(datos.get('paisCodigo') ?? '') || null, + lugarOrigen: String(datos.get('lugarOrigen') ?? ''), + inicio: numero(datos.get('inicio')), + fin: numero(datos.get('fin')), + sitioWeb: String(datos.get('sitioWeb') ?? ''), + estado: String(datos.get('estado') ?? 'borrador') as Estado, + identificadores + }; + const errores = revisarArtista(cambios); + if (Object.keys(errores).length) return fail(400, { errores }); + if (!(await guardarArtista(params.slug, cambios))) error(404, 'La ficha ya no existe.'); + return { guardado: true }; + } +}; diff --git a/src/panel/rutas/(dentro)/artistas/[slug]/+page.svelte b/src/panel/rutas/(dentro)/artistas/[slug]/+page.svelte new file mode 100644 index 0000000..abc259a --- /dev/null +++ b/src/panel/rutas/(dentro)/artistas/[slug]/+page.svelte @@ -0,0 +1,146 @@ + + +{data.artista.nombre} · Panel +

← Artistas

+
+
+
+

{data.artista.codigo} · {data.artista.slug}

+

+ +

+
+ +
+ {#if form?.guardado}

Guardado

{/if} +
+

Ficha pública

+
+ + + + + + + + +
+ {#if form?.errores}

{Object.values(form.errores).join(' ')}

{/if} + + + +
+
+ + diff --git a/src/panel/rutas/(dentro)/canciones/[slug]/+page.server.ts b/src/panel/rutas/(dentro)/canciones/[slug]/+page.server.ts index c568fda..0ac2a03 100644 --- a/src/panel/rutas/(dentro)/canciones/[slug]/+page.server.ts +++ b/src/panel/rutas/(dentro)/canciones/[slug]/+page.server.ts @@ -24,6 +24,17 @@ function numeroDe(valor: FormDataEntryValue | null): number | null { export const actions: Actions = { default: async ({ request, params }) => { const datos = await request.formData(); + const versiones = datos.getAll('versionId').map((valor) => { + const id = String(valor); + return { + id, + nombre: String(datos.get(`versionNombre:${id}`) ?? ''), + nota: String(datos.get(`versionNota:${id}`) ?? ''), + estado: String(datos.get(`versionEstado:${id}`) ?? 'borrador') as Estado + }; + }); + const nombreVersion = String(datos.get('nuevaVersionNombre') ?? '').trim(); + const codigoVersion = String(datos.get('nuevaVersionCodigo') ?? '').trim(); const cambios: CambiosDeCancion = { titulo: String(datos.get('titulo') ?? ''), @@ -35,10 +46,17 @@ export const actions: Actions = { estado: String(datos.get('estado') ?? '') as Estado, instrumental: datos.get('instrumental') !== null, destacada: datos.get('destacada') !== null, - alaVenta: datos.get('alaVenta') !== null + alaVenta: datos.get('alaVenta') !== null, + interpreteIds: datos.getAll('interpreteId').map(String), + versiones, + nuevaVersion: { nombre: nombreVersion, codigo: codigoVersion } }; const errores = revisarCancion(cambios); + const actual = await cancionParaEditar(params.slug); + if (cambios.estado === 'publicado' && !actual?.tieneAudio) { + errores.audio = 'No se puede publicar sin una representación completa de escucha.'; + } if (Object.keys(errores).length > 0) return fail(400, { errores, cambios }); const guardado = await guardarCancion(params.slug, cambios); diff --git a/src/panel/rutas/(dentro)/canciones/[slug]/+page.svelte b/src/panel/rutas/(dentro)/canciones/[slug]/+page.svelte index eee8435..bf02c54 100644 --- a/src/panel/rutas/(dentro)/canciones/[slug]/+page.svelte +++ b/src/panel/rutas/(dentro)/canciones/[slug]/+page.svelte @@ -15,7 +15,7 @@ let { data, form } = $props(); - const copiar = (c: CambiosDeCancion): CambiosDeCancion => ({ + const copiar = (c: CambiosDeCancion | typeof data.cancion): CambiosDeCancion => ({ titulo: c.titulo, letra: c.letra, inspiracion: c.inspiracion, @@ -25,16 +25,22 @@ estado: c.estado, instrumental: c.instrumental, destacada: c.destacada, - alaVenta: c.alaVenta + alaVenta: c.alaVenta, + interpreteIds: [...c.interpreteIds], + versiones: c.versiones.map((v) => ({ + id: v.id, + nombre: v.nombre, + nota: v.nota, + estado: v.estado + })), + nuevaVersion: 'nuevaVersion' in c ? { ...c.nuevaVersion } : { nombre: '', codigo: '' } }); let campos = $state(untrack(() => copiar(form?.cambios ?? data.cancion))); let guardado = $state(untrack(() => copiar(data.cancion))); let guardando = $state(false); - const sinGuardar = $derived( - (Object.keys(guardado) as (keyof CambiosDeCancion)[]).some((k) => campos[k] !== guardado[k]) - ); + const sinGuardar = $derived(JSON.stringify(campos) !== JSON.stringify(guardado)); const enLaWeb = $derived(`${site.url}/canciones/${data.cancion.slug}`); const errores = $derived(form?.errores ?? {}); @@ -113,6 +119,7 @@ {/if} {#if errores.titulo}

{errores.titulo}

{/if} + {#if errores.audio}

{errores.audio}

{/if}
@@ -175,6 +182,85 @@
+
+ Intérpretes principales +

Son identidades públicas, no nombres copiados dentro de la canción.

+
+ {#each data.cancion.artistas as artista (artista.id)} + + {/each} +
+ {#if errores.interpretes}{errores.interpretes}{/if} +
+ +
+

Versiones musicales

+

+ Cada versión es otra fila de canción vinculada a esta principal; sus audios son + representaciones aparte. +

+ {#if campos.versiones.length === 0} +

No hay versiones.

+ {:else} +
    + {#each campos.versiones as version (version.id)} + {@const datosVersion = data.cancion.versiones.find((v) => v.id === version.id)} +
  • + + + + + + {datosVersion?.versionCodigo} · {datosVersion?.codigo}{#if datosVersion?.audios.length} + · {datosVersion.audios.map((a) => `${a.uso}/${a.formato}`).join(' · ')}{/if} + +
  • + {/each} +
+ {/if} +
+ + +
+ {#if errores.versiones}{errores.versiones}{/if} +
+

@@ -348,6 +434,60 @@ cursor: pointer; } + .interpretes { + margin: 0; + padding: var(--sp-4); + border: 1px solid var(--c-border); + } + .interpretes legend { + padding-inline: var(--sp-2); + } + .opciones { + display: flex; + flex-wrap: wrap; + gap: var(--sp-2) var(--sp-5); + margin-top: var(--sp-3); + } + .opciones label { + display: flex; + align-items: center; + gap: 0.45rem; + color: var(--c-text); + } + .opciones small { + color: var(--c-text-muted); + font-family: var(--font-mono); + } + .opciones .retirado { + opacity: 0.65; + } + .versiones { + padding-block: var(--sp-4); + border-block: 1px solid var(--c-border); + } + .lista-versiones { + display: grid; + gap: var(--sp-3); + padding: 0; + list-style: none; + } + .lista-versiones li { + display: grid; + grid-template-columns: 1fr 2fr minmax(8rem, 0.6fr); + gap: var(--sp-3); + padding: var(--sp-3); + background: var(--c-bg-raised); + } + .lista-versiones small { + grid-column: 1 / -1; + } + .nueva-version { + display: grid; + grid-template-columns: 2fr 1fr; + gap: var(--sp-3); + margin-top: var(--sp-4); + } + /* * La letra se lleva la altura que queda de ventana. Es donde se pasa el * rato, y en una caja de seis líneas se escribe mirando por una rendija. diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index e16234f..d25d33f 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -37,16 +37,23 @@ export async function load({ locals, cookies, url }) { } const usuario = locals.usuario; + const [carrito, compras, favoritos, legales, catalogo] = await Promise.all([ + leerCarrito(cookies, usuario?.id ?? null), + usuario ? comprasDeUsuario(usuario.id) : Promise.resolve([]), + usuario ? favoritosDeUsuario(usuario.id) : Promise.resolve([]), + indiceLegal(), + leerCatalogo() + ]); return { // Solo lo que la interfaz necesita: nada de fechas internas ni ids de // sesión viajando al cliente. usuario: usuario ? { id: usuario.id, email: usuario.email } : null, - carrito: await leerCarrito(cookies, usuario?.id ?? null), - compras: usuario ? await comprasDeUsuario(usuario.id) : [], + carrito, + compras, // Los favoritos viajan enteros, como el carrito: son unas decenas de // slugs y cualquier tarjeta necesita saber si el suyo esta dentro. - favoritos: usuario ? await favoritosDeUsuario(usuario.id) : [], + favoritos, /* * Con qué proveedores se puede entrar en ESTE servidor. Sale de si hay * credenciales puestas, que es información del servidor, y lo necesita el @@ -60,7 +67,7 @@ export async function load({ locals, cookies, url }) { * viven en la base tienen que bajar por aquí, porque un componente no * puede consultar. */ - legales: await indiceLegal(), + legales, /* * El catálogo entero, una vez. Hasta ahora viajaba en el paquete de * JavaScript —lo cargaba `import.meta.glob` al construir— y por eso @@ -72,6 +79,6 @@ export async function load({ locals, cookies, url }) { * Son veintiún kilobytes, menos de lo que ocupaba en el paquete. La * letra completa no va: esa la pide la ficha de cada tema. */ - catalogo: await leerCatalogo() + catalogo }; } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 67903e4..f4b15dc 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -69,7 +69,7 @@ titulo="{CANCIONES_POR_PACK} canciones por un café" texto="{formatearPrecio( PRECIO_PACK_CENTIMOS - )} el pack, en MP3 y con todas sus versiones. En la web suena un fragmento; la descarga es el archivo completo, para llevártelo donde quieras." + )} el pack, en MP3 y con todas sus versiones. En la web se escucha la canción completa a calidad reducida; la compra entrega el archivo de mayor calidad." > {#snippet acciones()} Elegir canciones diff --git a/src/routes/canciones/[slug]/+page.svelte b/src/routes/canciones/[slug]/+page.svelte index b67860e..55e0391 100644 --- a/src/routes/canciones/[slug]/+page.svelte +++ b/src/routes/canciones/[slug]/+page.svelte @@ -168,8 +168,10 @@

{#if cancion.alaVenta}

- En la web suena un fragmento. La descarga completa en MP3, con todas sus versiones, va - en packs de {CANCIONES_POR_PACK} canciones por {formatearPrecio(PRECIO_PACK_CENTIMOS)}. + Escúchala completa aquí a calidad reducida. La compra entrega el archivo de mayor + calidad, con todas sus versiones, en packs de {CANCIONES_POR_PACK} canciones por {formatearPrecio( + PRECIO_PACK_CENTIMOS + )}.

{/if} @@ -180,7 +182,7 @@ @@ -260,6 +262,17 @@ {#if version.nota}

{version.nota}

{/if} + {#if version.interpretes.length > 0} +

+ {#each version.interpretes as interprete, j (`${interprete.id}-${interprete.rol}`)} + {#if j > 0}, + {/if}{#if interprete.slug}{interprete.nombre}{:else}{interprete.nombre}{/if} + {/each} +

+ {/if}
{version.duracion ? segundosADuracion(version.duracion) : '--:--'} @@ -496,6 +509,10 @@ font-size: var(--fs-xs); } + .interpretes { + font-size: var(--fs-xs); + } + .duracion { font-size: var(--fs-sm); font-variant-numeric: tabular-nums; diff --git a/src/routes/descargas/[cancion]/[[version]]/+server.ts b/src/routes/descargas/[cancion]/[[version]]/+server.ts index da39a7c..c620961 100644 --- a/src/routes/descargas/[cancion]/[[version]]/+server.ts +++ b/src/routes/descargas/[cancion]/[[version]]/+server.ts @@ -1,6 +1,6 @@ import { error } from '@sveltejs/kit'; import { extname } from 'node:path'; -import { obtenerCancion } from '$lib/server/catalogo'; +import { archivoComprado } from '$lib/server/audios'; import { respuestaDeDescarga } from '$lib/server/medios'; import { tieneCompra } from '$lib/server/pedidos'; import { registrarEntregaIniciada } from '$lib/server/descargas'; @@ -19,34 +19,25 @@ export const GET: RequestHandler = async ({ params, locals, request, getClientAd error(401, 'Entra en tu cuenta para descargar este tema.'); } - const cancion = await obtenerCancion(params.cancion); - if (!cancion) error(404, 'Ese tema no existe.'); + const archivo = await archivoComprado(params.cancion, params.version); + if (!archivo) error(404, 'Ese tema, versión o archivo no existe.'); - if (!(await tieneCompra(locals.usuario.id, cancion.slug))) { + if (!(await tieneCompra(locals.usuario.id, archivo.principalSlug))) { error(403, 'Este tema no está entre tus compras.'); } // Una versión alternativa va incluida en la compra del tema: quien lo ha // comprado se lleva todos sus arreglos. - const version = params.version - ? cancion.versiones.find((v) => v.id === params.version) - : undefined; + const nombre = archivo.versionNombre + ? `${archivo.titulo} (${archivo.versionNombre})${extname(archivo.clave)}` + : `${archivo.titulo}${extname(archivo.clave)}`; - if (params.version && !version) error(404, 'Esa versión no existe.'); - - const clave = version?.audio ?? cancion.audio; - if (!clave) error(404, 'Este tema todavía no tiene archivo descargable.'); - - const nombre = version - ? `${cancion.titulo} (${version.nombre})${extname(clave)}` - : `${cancion.titulo}${extname(clave)}`; - - const respuesta = await respuestaDeDescarga(clave, nombre); + const respuesta = await respuestaDeDescarga(archivo.clave, nombre); if (!respuesta) error(404, 'El archivo no está disponible ahora mismo.'); await registrarEntregaIniciada({ usuarioId: locals.usuario.id, - cancionSlug: cancion.slug, + cancionId: archivo.cancionId, ip: getClientAddress(), agente: request.headers.get('user-agent') }); diff --git a/src/routes/interpretes/+page.server.ts b/src/routes/interpretes/+page.server.ts new file mode 100644 index 0000000..dc7c2c8 --- /dev/null +++ b/src/routes/interpretes/+page.server.ts @@ -0,0 +1,5 @@ +import { artistasPublicos } from '$lib/server/artistas'; + +export async function load() { + return { artistas: await artistasPublicos() }; +} diff --git a/src/routes/interpretes/+page.svelte b/src/routes/interpretes/+page.svelte new file mode 100644 index 0000000..c6aae46 --- /dev/null +++ b/src/routes/interpretes/+page.svelte @@ -0,0 +1,105 @@ + + + + +
+

Créditos de grabación

+

Intérpretes

+

+ Las identidades que dan voz y sonido a cada versión, con su ficha y la discografía en la que + participan. +

+
+ + + +{#if data.artistas.length === 0} +

Aún no hay intérpretes publicados.

+{/if} + + diff --git a/src/routes/interpretes/[slug]/+page.server.ts b/src/routes/interpretes/[slug]/+page.server.ts new file mode 100644 index 0000000..92c1242 --- /dev/null +++ b/src/routes/interpretes/[slug]/+page.server.ts @@ -0,0 +1,8 @@ +import { error } from '@sveltejs/kit'; +import { obtenerArtistaPublico } from '$lib/server/artistas'; + +export async function load({ params }) { + const artista = await obtenerArtistaPublico(params.slug); + if (!artista) error(404, 'Ese intérprete no existe.'); + return { artista }; +} diff --git a/src/routes/interpretes/[slug]/+page.svelte b/src/routes/interpretes/[slug]/+page.svelte new file mode 100644 index 0000000..e981bab --- /dev/null +++ b/src/routes/interpretes/[slug]/+page.svelte @@ -0,0 +1,122 @@ + + + + +
+ {#if data.artista.imagen} + Retrato de {data.artista.nombre} + {/if} +
+

{data.artista.tipo} · {data.artista.codigo}

+

{data.artista.nombre}

+ {#if data.artista.descripcion}

{data.artista.descripcion}

{/if} +

+ {[data.artista.lugarOrigen, data.artista.pais, periodo].filter(Boolean).join(' · ')} +

+ {#if data.artista.sitioWeb} + Sitio oficial + {/if} +
+
+ +{#if data.artista.biografia} + + +
{@html data.artista.biografia}
+{/if} + +
+

Discografía

+ {#if data.artista.canciones.length > 0} + + {:else} +

No hay grabaciones publicadas todavía.

+ {/if} +
+ +{#if data.artista.identificadores.length > 0} +
+ {#each data.artista.identificadores as identificador (identificador.sistema)} +
{identificador.sistema}
+
{identificador.valor}
+ {/each} +
+{/if} + + diff --git a/src/routes/musica/+page.svelte b/src/routes/musica/+page.svelte index ac30afe..a904831 100644 --- a/src/routes/musica/+page.svelte +++ b/src/routes/musica/+page.svelte @@ -31,7 +31,7 @@ String(cancion.ano ?? ''), ...cancion.etiquetas, ...cancion.generos.map((g) => g.nombre), - ...cancion.interpretes + ...cancion.interpretes.map((i) => i.nombre) ].join(' ') ) })) diff --git a/src/routes/sitemap.xml/+server.ts b/src/routes/sitemap.xml/+server.ts index 601bfac..ce9f0ba 100644 --- a/src/routes/sitemap.xml/+server.ts +++ b/src/routes/sitemap.xml/+server.ts @@ -3,6 +3,7 @@ import { entradasPublicadas, paginasPublicadas } from '$lib/server/editorial'; import { site } from '$lib/site'; import { indiceDelTaller } from '$lib/server/taller'; import { indiceLegal } from '$lib/server/legal'; +import { artistasPublicos } from '$lib/server/artistas'; import type { RequestHandler } from './$types'; /** @@ -44,7 +45,8 @@ export const GET: RequestHandler = async () => { discos, temas, entradas, - paginas + paginas, + artistas ] = await Promise.all([ indiceDelTaller(), indiceLegal(), @@ -52,12 +54,14 @@ export const GET: RequestHandler = async () => { slugsDeAlbum(), etiquetas(), entradasPublicadas(), - paginasPublicadas() + paginasPublicadas(), + artistasPublicos() ]); const rutas = [ url('/', PRIORIDAD.portada), url('/musica', PRIORIDAD.seccion), + url('/interpretes', PRIORIDAD.seccion), url('/taller', PRIORIDAD.seccion), url('/blog', PRIORIDAD.seccion), url('/etiquetas', PRIORIDAD.resto), @@ -68,6 +72,7 @@ export const GET: RequestHandler = async () => { // Incluye los álbumes de singles, que son sintéticos pero tienen página. ...discos.map((slug) => url(`/albumes/${slug}`, PRIORIDAD.obra)), ...canciones.map((c) => url(`/canciones/${c.slug}`, PRIORIDAD.obra)), + ...artistas.map((a) => url(`/interpretes/${a.slug}`, PRIORIDAD.obra)), url('/generos', PRIORIDAD.seccion), url('/generos/glosario', PRIORIDAD.resto), url('/generos/letristas', PRIORIDAD.resto), diff --git a/static/escucha/como-si-nada/01-como-si-nada-maqueta.mp3 b/static/escucha/como-si-nada/01-como-si-nada-maqueta.mp3 new file mode 100644 index 0000000..fc9419e Binary files /dev/null and b/static/escucha/como-si-nada/01-como-si-nada-maqueta.mp3 differ diff --git a/static/escucha/como-si-nada/01-como-si-nada-orquestal.mp3 b/static/escucha/como-si-nada/01-como-si-nada-orquestal.mp3 new file mode 100644 index 0000000..cd7b789 Binary files /dev/null and b/static/escucha/como-si-nada/01-como-si-nada-orquestal.mp3 differ diff --git a/static/escucha/como-si-nada/01-como-si-nada.mp3 b/static/escucha/como-si-nada/01-como-si-nada.mp3 new file mode 100644 index 0000000..e157185 Binary files /dev/null and b/static/escucha/como-si-nada/01-como-si-nada.mp3 differ diff --git a/static/escucha/como-si-nada/02-media-verdad.mp3 b/static/escucha/como-si-nada/02-media-verdad.mp3 new file mode 100644 index 0000000..408a545 Binary files /dev/null and b/static/escucha/como-si-nada/02-media-verdad.mp3 differ diff --git a/static/escucha/como-si-nada/03-el-ultimo-tren-duo.mp3 b/static/escucha/como-si-nada/03-el-ultimo-tren-duo.mp3 new file mode 100644 index 0000000..355f7cc Binary files /dev/null and b/static/escucha/como-si-nada/03-el-ultimo-tren-duo.mp3 differ diff --git a/static/escucha/como-si-nada/03-el-ultimo-tren.mp3 b/static/escucha/como-si-nada/03-el-ultimo-tren.mp3 new file mode 100644 index 0000000..6e17ebf Binary files /dev/null and b/static/escucha/como-si-nada/03-el-ultimo-tren.mp3 differ diff --git a/static/escucha/como-si-nada/04-domingo-largo.mp3 b/static/escucha/como-si-nada/04-domingo-largo.mp3 new file mode 100644 index 0000000..aa5593f Binary files /dev/null and b/static/escucha/como-si-nada/04-domingo-largo.mp3 differ diff --git a/static/escucha/dos-orillas/01-dos-orillas.mp3 b/static/escucha/dos-orillas/01-dos-orillas.mp3 new file mode 100644 index 0000000..1506682 Binary files /dev/null and b/static/escucha/dos-orillas/01-dos-orillas.mp3 differ diff --git a/static/escucha/dos-orillas/02-carta-sin-enviar.mp3 b/static/escucha/dos-orillas/02-carta-sin-enviar.mp3 new file mode 100644 index 0000000..ee4073b Binary files /dev/null and b/static/escucha/dos-orillas/02-carta-sin-enviar.mp3 differ diff --git a/static/escucha/dos-orillas/03-el-invierno-que-viene.mp3 b/static/escucha/dos-orillas/03-el-invierno-que-viene.mp3 new file mode 100644 index 0000000..66e5594 Binary files /dev/null and b/static/escucha/dos-orillas/03-el-invierno-que-viene.mp3 differ diff --git a/static/escucha/preludios-para-piano/01-preludio-i-cuarteto.mp3 b/static/escucha/preludios-para-piano/01-preludio-i-cuarteto.mp3 new file mode 100644 index 0000000..7eb6024 Binary files /dev/null and b/static/escucha/preludios-para-piano/01-preludio-i-cuarteto.mp3 differ diff --git a/static/escucha/preludios-para-piano/01-preludio-i.mp3 b/static/escucha/preludios-para-piano/01-preludio-i.mp3 new file mode 100644 index 0000000..fc9419e Binary files /dev/null and b/static/escucha/preludios-para-piano/01-preludio-i.mp3 differ diff --git a/static/escucha/preludios-para-piano/02-preludio-ii.mp3 b/static/escucha/preludios-para-piano/02-preludio-ii.mp3 new file mode 100644 index 0000000..cd7b789 Binary files /dev/null and b/static/escucha/preludios-para-piano/02-preludio-ii.mp3 differ diff --git a/static/escucha/preludios-para-piano/03-preludio-iii.mp3 b/static/escucha/preludios-para-piano/03-preludio-iii.mp3 new file mode 100644 index 0000000..e157185 Binary files /dev/null and b/static/escucha/preludios-para-piano/03-preludio-iii.mp3 differ diff --git a/static/escucha/ruido-blanco/01-ruido-blanco.mp3 b/static/escucha/ruido-blanco/01-ruido-blanco.mp3 new file mode 100644 index 0000000..408a545 Binary files /dev/null and b/static/escucha/ruido-blanco/01-ruido-blanco.mp3 differ diff --git a/static/escucha/ruido-blanco/02-nadie-mira.mp3 b/static/escucha/ruido-blanco/02-nadie-mira.mp3 new file mode 100644 index 0000000..355f7cc Binary files /dev/null and b/static/escucha/ruido-blanco/02-nadie-mira.mp3 differ diff --git a/static/escucha/ruido-blanco/03-cinco-de-la-manana.mp3 b/static/escucha/ruido-blanco/03-cinco-de-la-manana.mp3 new file mode 100644 index 0000000..6e17ebf Binary files /dev/null and b/static/escucha/ruido-blanco/03-cinco-de-la-manana.mp3 differ diff --git a/static/escucha/singles/dorina.mp3 b/static/escucha/singles/dorina.mp3 new file mode 100644 index 0000000..ef3aa34 Binary files /dev/null and b/static/escucha/singles/dorina.mp3 differ diff --git a/static/escucha/singles/la-ultima-carta.mp3 b/static/escucha/singles/la-ultima-carta.mp3 new file mode 100644 index 0000000..aa5593f Binary files /dev/null and b/static/escucha/singles/la-ultima-carta.mp3 differ diff --git a/static/escucha/singles/tres-de-la-tarde.mp3 b/static/escucha/singles/tres-de-la-tarde.mp3 new file mode 100644 index 0000000..1506682 Binary files /dev/null and b/static/escucha/singles/tres-de-la-tarde.mp3 differ diff --git a/static/escucha/singles/verano-tarde-acustica.mp3 b/static/escucha/singles/verano-tarde-acustica.mp3 new file mode 100644 index 0000000..ee4073b Binary files /dev/null and b/static/escucha/singles/verano-tarde-acustica.mp3 differ diff --git a/static/escucha/singles/verano-tarde.mp3 b/static/escucha/singles/verano-tarde.mp3 new file mode 100644 index 0000000..66e5594 Binary files /dev/null and b/static/escucha/singles/verano-tarde.mp3 differ