keepercheky

Pestaña de Películas

Descripción General

La pestaña de películas muestra todas las películas disponibles en las bibliotecas de Jellyfin, con una interfaz moderna tipo tarjetas (cards).

Arquitectura Backend

Handler: internal/handler/movies.go

Responsabilidades:

Endpoints:

Estructuras de Datos:

type MovieFile struct {
    Path      string                 `json:"path"`
    Title     string                 `json:"title"`
    Year      int                    `json:"year,omitempty"`
    Size      int64                  `json:"size"`
    Extension string                 `json:"extension"`
    Directory string                 `json:"directory"`
    PosterURL string                 `json:"poster_url,omitempty"`
    Jellyfin  *JellyfinMovieMetadata `json:"jellyfin,omitempty"`
}

type JellyfinMovieMetadata struct {
    ID              string  `json:"id"`
    Overview        string  `json:"overview,omitempty"`
    CommunityRating float64 `json:"community_rating,omitempty"`
    PlayCount       int     `json:"play_count"`
    LastPlayed      string  `json:"last_played,omitempty"`
    DateAdded       string  `json:"date_added,omitempty"`
}

Cliente Jellyfin: internal/service/clients/jellyfin.go

Filtrado de Tipos: El cliente de Jellyfin devuelve diferentes tipos de items:

Issue Conocido: Las temporadas (Seasons) de series están siendo devueltas como “movies” porque el código asume que todo lo que no es “Series” ni “Episode” es una película. Esto necesita ser corregido.

Solución Pendiente: Agregar filtro explícito para item.Type == "Season" en convertToMedia().

Arquitectura Frontend

Template: web/templates/pages/movies.html

Componente Alpine.js: moviesPage()

Estado:

{
    movies: [],              // Array de películas
    libraryPaths: [],        // Rutas de bibliotecas de Jellyfin
    loading: true,           // Estado de carga
    error: null,             // Errores
    searchQuery: '',         // Búsqueda
    sortBy: 'title',         // Ordenamiento
    selectedMovies: []       // Películas seleccionadas
}

Funcionalidades:

Diseño de Lista Horizontal

⚠️ IMPORTANTE: Cada película es UNA LÍNEA HORIZONTAL, no una tarjeta vertical.

Especificación (según docs/templates/card.md):

[ ] Título - Año - Tamaño - ICONOS - Carátula

Elementos por fila (izquierda a derecha):

  1. [ ] Checkbox para selección múltiple
  2. Título de la película
  3. Año de lanzamiento (si disponible)
  4. Tamaño en formato legible (KB, MB, GB)
  5. Iconos de servicios:
    • 🎞️ Jellyfin (con popup de metadatos)
    • 🔽 qBittorrent (futuro - estado de torrent)
    • 📡 Radarr (futuro - información de Radarr)
    • 📺 Sonarr (futuro - para series)
  6. Carátula pequeña de Jellyfin al final

Layout: Lista vertical donde cada película ocupa una fila horizontal completa.

Flujo de Datos

1. Usuario accede a /movies
2. Frontend carga y hace fetch a /api/movies
3. Backend llama a jellyfinClient.GetLibrary()
4. Jellyfin devuelve todos los items (movies, series, episodes, seasons)
5. convertJellyfinToMovies() filtra solo type="movie"
6. Backend devuelve JSON con películas filtradas
7. Frontend renderiza las tarjetas con Alpine.js

Problemas Conocidos

1. Temporadas (Seasons) Incluidas como Películas

Estado: 🔴 PENDIENTE

Descripción: Las temporadas de series están siendo incluidas en el conteo de películas porque convertToMedia() asigna type=”movie” por defecto a todo lo que no sea “Series” o “Episode”.

Ejemplo:

Solución: Agregar en jellyfin.go:

func (c *JellyfinClient) convertToMedia(item *jellyfinItem) *models.Media {
    mediaType := "movie"
    if item.Type == "Series" {
        mediaType = "series"
    } else if item.Type == "Episode" {
        mediaType = "episode"
    } else if item.Type == "Season" {
        mediaType = "season"  // Nueva línea
    }
    // ...
}

Y en movies.go:

if media.Type != "movie" {
    // Filtrar todo excepto movies
    continue
}

2. Metadatos Limitados de Jellyfin

Estado: 📝 TODO

Descripción: Actualmente solo mostramos metadatos básicos (ID, fechas, play_count) porque están en models.Media. Los metadatos enriquecidos (Overview, CommunityRating) requieren llamadas adicionales a la API de Jellyfin.

Solución Futura:

3. Extracción de Año

Estado: ⚠️ MEJORABLE

Descripción: El año se extrae del título buscando patrón (YYYY). Esto es frágil y puede fallar con títulos no estándar.

Alternativa: Jellyfin tiene campo ProductionYear que sería más confiable.

Próximos Pasos

Corto Plazo

Mediano Plazo

Largo Plazo

Notas para Series Tab

RECORDATORIO: Más tarde crearemos la pestaña de Series con arquitectura similar:

Templates de Diseño

La carpeta docs/templates/ contiene las especificaciones de diseño de los elementos de la UI:

Propósito: Mantener especificaciones de diseño separadas del código permite:

Referencias


Última actualización: 2025-11-11
Estado: En desarrollo activo
Prioridad: Alta