🎬 Catálogo de Películas
  • Catálogo
  • Estadísticas

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.

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)

📊 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

// Download button
html`<a href="data/final_results/catalog.json" download class="download-btn">
  📥 Descargar JSON completo
</a>`

Datos extraídos de catálogos PDF y enriquecidos con OMDB API