catalog = FileAttachment("data/final_results/catalog.json").json()
// Extract movies array
movies = catalog.movies || []
// Get unique genres for filter (flatten and dedupe)
genres = [...new Set(movies.flatMap(m => m.genre || []))].filter(Boolean).sort()
// Stats
enrichedCount = movies.filter(m => m.enriched).length
totalCount = movies.length
enrichmentRate = Math.round(enrichedCount / totalCount * 100)Catálogo de Películas
🎬 Catálogo Interactivo — CINE.pdf
Explora el catálogo de películas de la Biblioteca Vasconcelos (colección CINE). Usa los filtros para buscar por título, director o género.
📊 Estadísticas del Catálogo
// Display stats cards
html`<div class="stats-container">
<div class="stat-card">
<div class="stat-value">${totalCount}</div>
<div class="stat-label">Total Películas</div>
</div>
<div class="stat-card">
<div class="stat-value">${enrichedCount}</div>
<div class="stat-label">Con Datos OMDB</div>
</div>
<div class="stat-card">
<div class="stat-value">${enrichmentRate}%</div>
<div class="stat-label">Enriquecidas</div>
</div>
<div class="stat-card">
<div class="stat-value">${genres.length}</div>
<div class="stat-label">Géneros</div>
</div>
</div>`🔍 Filtros
viewof searchQuery = Inputs.text({
label: "Buscar",
placeholder: "Título, director, género, país, año...",
value: ""
})
// Genre filter
viewof selectedGenre = Inputs.select(
["Todos", ...genres],
{label: "Género", value: "Todos"}
)
// Director filter (dynamic based on data)
directors = [...new Set(movies.map(m => m.director).filter(Boolean))].sort()
viewof selectedDirector = Inputs.select(
["Todos", ...directors.slice(0, 100)],
{label: "Director", value: "Todos"}
)
// Enrichment filter
viewof showEnrichedOnly = Inputs.toggle({label: "Solo con datos OMDB", value: false})
// Reset button
viewof resetFilters = Inputs.button("Restablecer filtros", {value: null})
// Reactive: Reset filters when button clicked
resetTrigger = {
resetFilters;
return Date.now();
}
// Filtered movies (reactive)
filteredMovies = {
resetTrigger;
let result = movies;
// Enriched-only filter
if (showEnrichedOnly) {
result = result.filter(m => m.enriched);
}
// Genre filter
if (selectedGenre !== "Todos") {
result = result.filter(m => (m.genre || []).includes(selectedGenre));
}
// Director filter
if (selectedDirector !== "Todos") {
result = result.filter(m => m.director === selectedDirector);
}
// Search query filter
if (searchQuery) {
const query = searchQuery.toLowerCase();
result = result.filter(m =>
(m.search_text || "").includes(query) ||
(m.title || "").toLowerCase().includes(query) ||
(m.matched_title || "").toLowerCase().includes(query) ||
(m.director || "").toLowerCase().includes(query) ||
(m.country || "").toLowerCase().includes(query) ||
String(m.catalogue_id || "").includes(query)
);
}
return result;
}🎥 Resultados ( películas)
movieGrid = {
if (filteredMovies.length === 0) {
return html`<div class="no-results">No se encontraron películas con los filtros seleccionados.</div>`;
}
const grid = html`<div class="movie-grid"></div>`;
for (const movie of filteredMovies) {
const poster = movie.poster || "https://i.imgur.com/Ngnm5v6.png";
const imdbUrl = movie.imdb_id ? `https://www.imdb.com/title/${movie.imdb_id}` : null;
const idLabel = movie.catalogue_id != null ? `#${movie.catalogue_id}` : "";
const card = html`
<div class="movie-card ${movie.enriched ? '' : 'unenriched'}">
<div class="movie-poster-container">
<img class="movie-poster" src="${poster}" alt="${movie.title}" loading="lazy"
onerror="this.src='https://i.imgur.com/Ngnm5v6.png'">
${movie.imdb_rating ? html`<div class="rating-badge">⭐ ${movie.imdb_rating}</div>` : null}
</div>
<div class="movie-info">
<div class="card-header-row">
${idLabel ? html`<span class="catalogue-id-badge">${idLabel}</span>` : null}
${!movie.enriched ? html`<span class="unenriched-badge">Sin datos</span>` : null}
</div>
<h3 class="movie-title" title="${movie.title}">${movie.title}</h3>
${movie.matched_title && movie.matched_title !== movie.title ?
html`<div class="original-title">${movie.matched_title}</div>` : null}
${movie.year ? html`<div class="movie-year">📅 ${movie.year}</div>` : null}
${movie.director ? html`<div class="movie-director">🎬 ${movie.director}</div>` : null}
${movie.genre?.length ? html`<div class="movie-genres">${movie.genre.slice(0, 3).map(g =>
html`<span class="genre-tag">${g}</span>`
)}</div>` : null}
${movie.country ? html`<div class="movie-country">🌍 ${movie.country}</div>` : null}
${movie.plot ? html`<div class="movie-plot">${movie.plot.substring(0, 120)}${movie.plot.length > 120 ? "..." : ""}</div>` : null}
${imdbUrl ? html`<a href="${imdbUrl}" target="_blank" class="imdb-link">Ver en IMDb →</a>` : null}
</div>
</div>
`;
grid.appendChild(card);
}
return grid;
}
movieGrid📥 Descargar Datos
Datos extraídos de catálogos PDF y enriquecidos con OMDB API