A personal TV show tracker. Search shows via TVmaze, build a private library, mark episodes/seasons/shows as watched, write reviews, manage favorites, and explore watch analytics..
- Authentication — Username + password sign-up and login (JWT)
- Show discovery & library — TVmaze search, add-to-library, shared global catalog with per-user library entries
- Library statuses —
None,Plan to Watch, andWatched(set automatically when every episode is watched) - Watch tracking — Mark/unmark at episode, season, or show level, with upward/downward cascade and confirmation for destructive unmarks; append-only watch history
- Reviews & ratings — Independent half-star ratings (1.0–5.0) and text reviews at episode, season, and show level
- Favorites — Manual show favorites plus suggestions derived from review scores
- Analytics — Watch counts by period, longest time-to-watch, totals, streaks, library completion, and favorites dashboard
- i18n — English and Finnish UI with persisted language preference
- Responsive UI — Mobile-friendly layout with shared design tokens and components
| Layer | Stack |
|---|---|
| Frontend | React 19, TypeScript, Vite, TanStack Query, React Router, i18next |
| Backend | Spring Boot 4, Java 21, Gradle, JWT, Flyway |
| Database | PostgreSQL 16 |
| External API | TVmaze (metadata only; proxied through the backend) |
- Docker (Postgres locally; optional full-stack boot)
- Java 21 (native backend development)
- pnpm (native frontend development)
Pick one of the two local setups below.
Requires Docker only (no local Java or pnpm):
./scripts/docker-up.shApp: http://localhost:8080 (UI, API at /api/*, Swagger at /swagger-ui.html)
Postgres is also exposed on localhost:5433 for seed scripts. Re-run the script after code changes; pass -d for detached mode. Stop the stack: docker compose --profile full down
Run Postgres in Docker, backend and frontend on the host (hot reload, Gradle/pnpm tooling).
docker compose up -dPostgres listens on localhost:5433 (credentials in docker-compose.yml).
cd backend
./gradlew bootRunAPI: http://localhost:8080
Swagger UI: http://localhost:8080/swagger-ui.html
cd frontend
pnpm install
pnpm devApp: http://localhost:5173 (API calls use same-origin /api/*, proxied to the backend by Vite)
Optional: copy frontend/.env.example to frontend/.env if the backend runs on a non-default host or port.
With Postgres running (backend may be up or down). Works with either quick-start option above — API defaults to http://localhost:8080.
./scripts/populate_test_data.shDefault credentials:
- Username:
firstmock_lastmock - Password:
TestPassword123!
To seed watch history and analytics fixtures for the test user (requires backend running):
./scripts/populate_analytics_test_data.shBackend and frontend are independent projects—run commands from each directory.
cd backend
./gradlew build # compile + test (+ JaCoCo coverage gate)
./gradlew test # tests only
./gradlew bootRun # run locally
./gradlew spotlessApply # format
./gradlew checkstyleMain checkstyleTestBackend tests use Testcontainers for integration tests against PostgreSQL. ./gradlew check enforces ≥ 80% line coverage via JaCoCo.
cd frontend
pnpm dev
pnpm build
pnpm test:run
pnpm lint
pnpm formatbackend/ Spring Boot REST API (feature-based packages: auth, show, watch, …)
frontend/ React SPA (features mirror backend domains)
openspec/ Spec-driven development: live specs, roadmap, change archive
docs/ PRD, architecture notes, historical planning docs
scripts/ Local dev helpers (including `docker-up.sh` for full-stack Docker)
docker-compose.yml Postgres (default); use `./scripts/docker-up.sh` for backend + frontend
Behavior is defined in openspec/specs/. Implementation detail and package structure live in docs/ARCHITECTURE.md.
| Area | Endpoints |
|---|---|
| Auth | POST /api/auth/register, POST /api/auth/login, GET /api/me |
| Library | GET /api/shows/search, POST /api/shows, GET/PATCH/DELETE /api/shows/{id} |
| Watch | POST/DELETE /api/watch/{episodes|seasons|shows}/{id} |
| Reviews | POST/GET /api/reviews, PUT/DELETE /api/reviews/{id} |
| Favorites | GET/POST /api/favorites, GET /api/favorites/suggestions, GET /api/favorites/status, DELETE /api/favorites/{showId} |
| Analytics | GET /api/analytics/watch-counts, /longest-to-watch, /totals, /favorites, /watch-streaks, /library-completion, /plan-to-watch-count |
All library, watch, review, favorite, and analytics routes require a valid JWT (Authorization: Bearer …).
Interactive API docs: http://localhost:8080/swagger-ui.html
This repo uses OpenSpec for spec-driven changes:
- Propose a change under
openspec/changes/ - Implement against the change tasks and delta specs
- Archive when complete (merges specs into
openspec/specs/)
Phase-level progress is tracked in openspec/TASKS.md.
Show metadata and images come from the TVmaze API, licensed under CC BY-SA 4.0. See the in-app About page for details.
Copyright (C) 2026 Ville Saloranta.
This project is licensed under the GNU General Public License v3.0 or later. Third-party software and data attributions are in NOTICE.md.