A comprehensive media tracking and analytics application built with Next.js, React, and Supabase. Track movies, TV shows, books, games, podcasts, and live theatre performances with detailed analytics, filtering, and metadata integration.
- Features
- Tech Stack
- Getting Started
- Configuration
- Project Structure
- Core Concepts
- Usage
- Database Schema
- Scripts
- Troubleshooting
- Contributing
- License
- Acknowledgments
- Multiple Media Types: Track Movies, TV Shows, Books, Games, Podcasts, and Live Theatre.
- Rich Metadata: Store title, genre, language, ratings, dates, prices, platforms, and more.
- Status Management: Track status (Finished, Watching, On Hold, Dropped, Plan to Watch) with history.
- Custom Ratings: Personal ratings alongside average ratings from external sources.
- Date Tracking: Start and finish dates for each entry.
- Poster Images: Automatic poster/image fetching and display.
- KPI Metrics: Total items, time spent, pages read, money spent, average ratings.
- Visual Charts:
- Spending trends by month and medium.
- Time consumption (minutes/hours) by month.
- Reading volume (pages) by month.
- Rating distributions.
- Counts by medium, language, genre, platform, status, and type.
- Global Filtering: Filter analytics across all metrics simultaneously.
- Time-based Analysis: Monthly breakdowns for all metrics.
- Multi-criteria Filtering: Filter by type, status, medium, platform, language, genre, and date ranges.
- Full-text Search: Search across titles and other fields.
- URL-based Filters: Shareable filtered views via URL parameters.
- Column Customization: Show/hide table columns with persistent preferences.
- Sorting: Multi-column sorting with direction control.
- Manual Entry: Comprehensive form for adding new entries.
- CSV Import: Bulk import entries from CSV files with field mapping.
- Batch Editing: Edit multiple entries simultaneously.
- Entry Editing: Inline editing with dialog forms.
- Entry Deletion: Safe deletion with confirmation.
- Status History: Track status changes over time.
- OMDB Integration: Fetch movie and TV show metadata automatically.
- MyDramaList Support: Integration for Asian drama content.
- Smart Override: Choose which fields to override when fetching metadata.
- Poster Fetching: Automatic poster image retrieval.
- Dark/Light Mode: Theme toggle with system preference detection.
- Responsive Design: Works on desktop, tablet, and mobile devices.
- Toast Notifications: User-friendly feedback for all actions.
- Loading States: Clear loading indicators during operations.
- Error Handling: Graceful error handling with user-friendly messages.
- Framework: Next.js 16.1.1 (App Router)
- UI Library: React 19.2.3
- Language: TypeScript 5.7.2
- Database: Supabase (PostgreSQL)
- Styling: Tailwind CSS 3.4.17
- UI Components: Radix UI primitives
- Forms: React Hook Form with Zod validation
- Tables: TanStack Table (React Table)
- Charts: Recharts
- Date Handling: date-fns
- Icons: Lucide React
- State Management: React hooks and URL state (nuqs)
- AI Integration: Google Generative AI
- Node.js 18+ or Bun
- A Supabase account and project
- (Optional) OMDB API key for metadata fetching
-
Clone the repository:
git clone <repository-url> cd media-review
-
Install dependencies:
bun install
If you only need to run the built application or specific scripts and don't require the full linting and tooling setup, you can install only production dependencies:
bun install --productionThis keeps node_modules smaller (in this project, roughly 450 MB with production deps only vs ~515 MB with full dev tooling, exact numbers will vary by platform and versions). To restore the full developer experience, run bun install again.
-
Set up environment variables: Create a
.env.localfile in the root directory:NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key OMDB_API_KEY=your_omdb_api_key (optional) GOOGLE_GENERATIVE_AI_API_KEY=your_google_ai_key (optional)
Note: The
SUPABASE_SERVICE_ROLE_KEYis required for the user authentication check. You can find it in your Supabase project settings under "API" β "Service Role Key" (keep this secret and never expose it to the client). -
Set up the database:
- Create a Supabase project.
- Run the database migrations to create the
media_entriesandmedia_status_historytables. - Ensure Row Level Security (RLS) policies are configured for your use case.
-
Run the development server:
bun run dev
-
Open http://localhost:3000 in your browser.
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
β | Public Supabase project URL used by the browser client. |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
β | Anonymous public key for the Supabase client. |
SUPABASE_SERVICE_ROLE_KEY |
β | Server-side key used for privileged Supabase calls (never expose in the browser). |
OMDB_API_KEY |
β | Enables metadata lookup for movies and TV shows via OMDB. |
GOOGLE_GENERATIVE_AI_API_KEY |
β | Enables AI-assisted features for enrichment or summaries. |
- Enable Row Level Security (RLS) on all media tables.
- Create policies that allow each authenticated user to read/write only their own data.
- Seed any lookup tables you need for genres, platforms, or languages if you prefer standardized lists.
media-review/
βββ app/ # Next.js app router pages
β βββ add/ # Add/edit entry page
β βββ analytics/ # Analytics dashboard
β βββ entries/ # Entries list view
β βββ import/ # CSV import page
β βββ library/ # Library view
β βββ list/ # List view
β βββ login/ # Authentication
β βββ api/ # API routes
β βββ auth/ # Authentication endpoints
β βββ metadata/ # Metadata fetching
β βββ omdb/ # OMDB integration
β βββ upload/ # File upload
βββ components/ # React components
β βββ analytics/ # Analytics-specific components
β βββ ui/ # Reusable UI components
β βββ data-table.tsx # Data table component
β βββ filters.tsx # Filter components
β βββ media-card-grid.tsx # Card grid view
β βββ media-table.tsx # Table view component
βββ hooks/ # Custom React hooks
β βββ useMediaMetrics.ts # Media metrics calculations
βββ lib/ # Utility functions and types
β βββ actions.ts # Server actions
β βββ database.types.ts # Database type definitions
β βββ filter-types.ts # Filter logic
β βββ parsing-utils.ts # Data parsing utilities
β βββ supabase/ # Supabase client setup
β βββ types.ts # Type definitions
βββ types/ # TypeScript type definitions
Each media entry represents a single item (book, movie, etc.). Status changes are stored in a separate history table so you can visualize progress and trends over time. When an entryβs status is updated, the UI updates the main record and inserts a new history row.
The analytics dashboard pulls raw entries from Supabase and aggregates them in the client. Metrics are calculated in useMediaMetrics, which combines:
- Counts (items by medium, status, platform, language, genre, type)
- Totals (time spent, pages read, money spent)
- Averages (ratings by medium and overall)
- Trends (monthly breakdowns used by charts)
Global filters affect the base dataset before aggregation so all KPIs and charts stay in sync.
Metadata fetch flows allow you to pull external data (OMDB/MyDramaList) and selectively apply it to an entry. The βSmart Overrideβ flow ensures you can keep your own values while still using fetched posters, plot summaries, or runtime details.
- Navigate to the Add page.
- Enter the title and select the medium type.
- Optionally fetch metadata from OMDB or other sources.
- Fill in additional details (genre, language, dates, ratings, etc.).
- Save the entry.
- Navigate to the Import page.
- Upload a CSV file.
- Map CSV columns to database fields.
- Preview and adjust the data.
- Import the entries.
Tip: CSV imports are useful for bulk backfills. For consistent results, make sure your file includes at least title, medium, and status.
- Navigate to the Analytics page.
- Use the global filter bar to filter data.
- View KPIs and charts.
- Explore trends and distributions.
- List View: View all entries in a sortable, filterable table.
- Library View: Browse entries in a card grid layout.
- Edit: Click the edit button on any entry.
- Batch Edit: Select multiple entries and edit them together.
- Delete: Remove entries with confirmation.
id(uuid, primary key)title(text, required)medium(text): Movie, TV Show, Book, Game, Podcast, Live Theatretype(text): Documentary, Variety, Reality, Scripted Live Action, Animation, Special, Audiostatus(text): Finished, In Progress, On Hold, Dropped, Plan to Watchgenre(text array)language(text array)platform(text)start_date(date)finish_date(date)my_rating(numeric)average_rating(numeric)rating(numeric)price(numeric)length(text)episodes(integer)poster_url(text)imdb_id(text)season(text)time_taken(text)created_at(timestamp)updated_at(timestamp)
id(uuid, primary key)media_entry_id(uuid, foreign key)old_status(text)new_status(text)changed_at(timestamp)notes(text)created_at(timestamp)
bun run dev- Start development server.bun run build- Build for production.bun run start- Start production server.bun run lint- Run ESLint.
- Blank analytics or missing data: Confirm RLS policies allow reads for the authenticated user and that the entries table is populated.
- Metadata fetch errors: Ensure the
OMDB_API_KEYis set and valid, and check OMDB rate limits. - Upload issues: Verify Supabase storage buckets exist and permissions are configured.
- Auth errors: Double-check
SUPABASE_SERVICE_ROLE_KEYfor server-side authentication checks.
Contributions are welcome! Please feel free to submit a Pull Request.
See LICENSE file for details.
- Built with Next.js
- UI components from Radix UI
- Styling with Tailwind CSS
- Database powered by Supabase
- Metadata from OMDB API