# Movies Module This module implements service-first Movies functionality, including: - Admin search + accept flow (TMDb) - Public browsing (Inertia pages) backed by JSON API - Strict controller→service separation ## Endpoints Admin (auth required) - GET /admin/movies → Inertia admin page (search + accept) - GET /admin/movies/search?q=&page= → JSON results from TMDb (20 per page) - GET /admin/movies/exists?provider_id= → Duplicate check - POST /admin/movies/accept { provider_id, mode: overwrite|duplicate } → Persist movie Public - Inertia pages: GET /movies, GET /movies/{id} - JSON API: GET /api/movies, GET /api/movies/{id} ## Data model - Fully normalized tables: movies, genres, actors, directors, studios, countries, languages + pivot tables. - Provider ID is not unique. Duplicates are allowed when “Save as Duplicate” is selected. - Images are stored as remote URLs only (not downloaded). ## Services & Contracts - Provider: App\\Modules\\Movies\\Services\\Contracts\\MovieProvider (TMDb implementation) - Browse: ListMoviesServiceInterface, ShowMovieServiceInterface - Admin: UpsertMovieServiceInterface, CheckMovieExistsServiceInterface All DI bindings are registered in MoviesServiceProvider. ## Admin flow 1. Search calls /admin/movies/search (TMDb search). List shows poster, title, year only (no details calls). 2. Accept button: - If unique, accepts immediately (no modal). - If duplicate exists by provider_id, shows modal: Overwrite | Save as Duplicate | Cancel. 3. On successful accept, a small toast appears and the row is marked “Accepted”. ## Public browsing - /movies (Inertia) fetches from /api/movies with infinite scroll. Each row shows poster, title, year, rating, genres, and a ~50-word description snippet. - /movies/{id} (Inertia) fetches from /api/movies/{id} and shows details with all relations. ## Environment Add to .env (values shown as placeholders): ``` # Active provider MOVIES_PROVIDER=tmdb # TMDb (v4 Read Access Token recommended) TMDB_API_TOKEN= TMDB_LANGUAGE=en-US TMDB_CACHE_TTL=3600 TMDB_IMAGE_BASE=https://image.tmdb.org/t/p/ TMDB_POSTER_SIZE=w500 TMDB_BACKDROP_SIZE=w780 TMDB_PROFILE_SIZE=w185 ``` Set your real TMDb token locally (you will provide it). Timezone is America/Chicago. Actor images - When accepting a movie, actor profile images are stored on the `actors.profile_path` column as FULL TMDb CDN URLs (e.g., `https://image.tmdb.org/t/p/w185/abc123.jpg`). - You can adjust the size used via `TMDB_PROFILE_SIZE`. ## Development - Run backend: `php artisan serve` - Frontend dev: `npm install && npm run dev` - Tests: `./vendor/bin/pest` ## Notes - Pagination: TMDb search uses 20/page; public DB pagination defaults to 20/page (configurable at request). - Controller classes contain no business logic; all logic resides in services.