UzzUTV is a Netflix-style streaming platform built with Django that lets you discover, search, and watch movies and TV series. Movie and TV content data comes from The Movie Database (TMDB) API; the Aniuzu anime catalogue uses AniList. The interface is responsive and mobile-friendly, and accounts are powered by Supabase, giving you watchlist, Continue Watching, ratings, reviews, public profiles, and Watch Parties.
- Trending, popular, and top-rated movies and TV shows
- Genre rows (Action, Romance, Comedy, Anime) with a mixed movie + TV feed
- Top 10 row and animated hero banners with movie/TV logos
- Landing-page genre cards that jump straight into a random title from that genre
- Dedicated category pages at
/category/<slug>/for Action, Romance, Comedy, Animation, Thriller, Drama, Horror, Sci-Fi — plus Popular and Top Rated - Each page mixes movies and TV shows in one balanced feed
- Paginated browsing (24 titles per page)
- Dedicated detail pages with full metadata and a hero banner using the official title logo
- Top Cast row with profile photos
- "More Like This" recommendations row
- Restyled Ratings & Reviews section matching the site theme
- Episode streaming for TV series with season/episode navigation
- Movie playback pages
- Works on mobile and desktop
- AniList-powered anime discovery, metadata, detail pages, watchlist, and episode navigation
- AniList is the primary anime catalogue provider; its API can be temporarily unavailable during upstream outages
- Anime watch route:
/aniuzu/anime/<title-year>/watch/<episode>/(legacy AniList-ID URLs remain supported) - Playback is limited to the documented AniLink and TryEmbed iframe providers
- SUB/DUB audio selection and AniLink/TryEmbed server switching preserve the current episode
- Responsive desktop two-column player/episode layout with independently scrollable episode lists
- Episode search and 26-episode paging (with Previous/Next controls) keep long-running anime usable
- Airing anime with an unconfirmed AniList episode count use
nextAiringEpisodeto list episodes available so far - Watch pages include a Related Anime row and playback guidance to retry before switching servers; Brave is recommended
- Authenticated Continue Watching cards show anime, season, episode, progress, server, and variant
- Detail pages switch the Watch action to Resume when anime progress exists
- Aniuzu navigation includes Home, Genres, Seasons, Studios, Collections, Watchlist, and Search; Schedule, Top, and Upcoming pages are not part of the current product
- Responsive Aniuzu navigation collapses into an accessible menu across desktop, tablet, and mobile viewports
- Instant search across movies and TV shows
- Sign up / sign in with client-side field validation, live username availability checks, and friendly API error messages
- Watchlist, ratings, and comments sync across devices; UzzUTV exact Continue Watching resume depends on the playback provider, with Vidfast currently providing the best support
- UzzUTV and Aniuzu share one Supabase Auth session
- Context-aware login redirects preserve the originating application
- Forgot-password and password-reset flows use Supabase Auth recovery sessions
- "Danger Zone" account deletion on your own profile (
/profile/): a confirmation modal requires typingDELETE, then the server verifies your Supabase session and permanently deletes the shared Supabase Auth user. The service-role credential stays server-side only and is never shipped to the browser.
- Star-rate any movie or TV show on its detail page
- Rating summary with average score and a 5-star distribution bar chart
- Comment/review on detail pages, with paginated comment feeds and author star ratings
- Create or join watch parties at
/party/ - Start a party from any movie or series detail page without copying an ID from the readable URL; the title slug is resolved server-side and the internal TMDB ID is prefilled
- Synchronized playback rooms at
/party/<room_code>/powered by Supabase Realtime - One-to-one host/guest video calling with Accept, Decline, Cancel, timeout, reconnect, camera, microphone, minimize, and End Call controls
- Desktop Expand mode turns the call into a full-page view with the remote video as the main stage and a small self-preview in the bottom-right
- The UzzUTV player-wrapper fullscreen control keeps the VidFast movie and call overlay visible together
- Save favorites to watch later
- Personal profile page with your rated titles, watchlist, and recent comments
- Public, shareable profile pages at
/profile/<user_id>/ - Dedicated
/rated/page listing all your ratings
- UzzUTV movie and TV cards store the title, release year, selected server, TV season/episode, playback position, duration, and progress percentage when those values are available
- Vidfast currently provides the most reliable exact playback position and duration; other external UzzUTV providers may only preserve the title, episode, and selected server because they do not expose player state
- UzzUTV history is stored in
continue_watchingand is associated with the authenticated user - Aniuzu history is stored separately in
aniuzu_continue_watching - One Continue Watching row/card is maintained per user and anime; it is updated with the latest episode watched
- Aniuzu playback state stores AniList ID, episode, server, variant, position, duration, and progress percentage
- Progress writes are debounced/upserted and completed episodes are removed
- Row Level Security restricts Aniuzu history to the owning authenticated user
- Dynamic
robots.txtandsitemap.xml
- Database-backed response caching (TMDB calls cached for hours)
- AniList responses are cached, and outbound AniList requests are spaced and backed off after rate-limit or API-unavailable responses
- GZip compression middleware
- Lazy-loaded images for faster page loads
| Layer | Technology |
|---|---|
| Backend | Python, Django 5.2.16 |
| Frontend | HTML5, CSS3, Bootstrap 5, JavaScript |
| Data | TMDB API (movies/TV metadata) |
| Auth/Data | Supabase (auth, watchlist, ratings, comments, profiles, realtime) |
| Database | SQLite (Django) |
| Caching | Django DatabaseCache |
| Deploy | PythonAnywhere (see settings.py) |
- Python 3.10+
- A free TMDB API key
- A Supabase project (optional, for auth/watchlist)
# 1. Clone the repository
git clone https://github.com/ujjwaluzu/uzzutv.git
cd uzzutv
# 2. Create and activate a virtual environment
python -m venv venv
venv\Scripts\activate # Windows
source venv/bin/activate # macOS/Linux
# 3. Install dependencies
pip install -r requirements.txt
# 4. Set up environment variables (see below)
# 5. Run migrations and cache table
python manage.py migrate
python manage.py createcachetable
# 6. Start the development server
python manage.py runserverCreate a .env file in the project root:
TMDB_KEY="your_tmdb_api_key"
SUPABASE_URL="https://your-project.supabase.co"
SUPABASE_KEY="your_supabase_key"
SUPABASE_SERVICE_ROLE_KEY="your_supabase_service_role_key"
DJANGO_SECRET_KEY="a_long_random_secret"
DJANGO_DEBUG=True
DJANGO_ALLOWED_HOSTS=*
# Optional: minimum spacing between AniList API requests. Default: 2.1 seconds.
ANILIST_MIN_INTERVAL_SECONDS="2.1"
SECURE_SSL_REDIRECT=False
SESSION_COOKIE_SECURE=False
CSRF_COOKIE_SECURE=False
SUPABASE_SERVICE_ROLE_KEY(server-only, required for account deletion): the Supabase service_role key from Project Settings → API →service_rolesecret. It is used exclusively by the Django backend (uzzutv/supabase_service.py) to verify the caller's Supabase session and to delete the shared Supabase Auth user via the GoTrue admin API. Keep it in the server environment /.envonly. It must never be placed in JavaScript, HTML templates, static files, or client-side code. Without it the delete-account endpoint cannot verify sessions or delete users; deletion requests fail closed with a friendly error.
Run sql/aniuzu_tables.sql in the Supabase SQL editor. It creates the Aniuzu watchlist and aniuzu_continue_watching tables, indexes, constraints, and Row Level Security policies. Continue Watching uses one row per authenticated user/anime and updates that row with the latest episode, server, variant, and position. The SQL also migrates older per-episode data by retaining the most recently updated row for each user/anime. Policies allow each authenticated user to select, insert, update, and delete only their own rows.
Run sql/continue_watching_progress.sql once for the UzzUTV Continue Watching table. It adds the selected server, playback position, duration, progress percentage, title and release year used by the resume cards. Vidfast reports the most complete playback state; other providers can leave position or percentage unavailable.
Run sql/watch_parties.sql to create the Watch Party tables, indexes, and Row Level Security policies for synchronized playback rooms.
Deleting a Supabase Auth user removes its data only where the tables below reference auth.users(id). Verify each foreign key exists with ON DELETE CASCADE so private user data is removed automatically when the auth user is deleted (this is what "Danger Zone" account deletion relies on):
| Table | Owner column | Rule needed |
|---|---|---|
profiles |
id (references auth.users(id)) |
ON DELETE CASCADE |
watchlist |
user_id |
ON DELETE CASCADE |
continue_watching |
user_id |
ON DELETE CASCADE |
ratings |
user_id |
ON DELETE CASCADE |
comments |
user_id |
ON DELETE CASCADE |
aniuzu_watchlist |
user_id |
ON DELETE CASCADE (present in sql/aniuzu_tables.sql) |
aniuzu_continue_watching |
user_id |
ON DELETE CASCADE (present in sql/aniuzu_tables.sql) |
aniuzu_ratings |
user_id |
ON DELETE CASCADE (present in sql/aniuzu_tables.sql) |
aniuzu_comments |
user_id |
ON DELETE CASCADE (present in sql/aniuzu_tables.sql) |
watch_parties |
host_user_id |
ON DELETE CASCADE (present in sql/watch_parties.sql) |
watch_parties |
guest_user_id |
ON DELETE SET NULL (present in sql/watch_parties.sql) |
Example verification query (Supabase SQL Editor):
select tc.table_name, kcu.column_name, rc.delete_rule
from information_schema.table_constraints tc
join information_schema.foreign_keys fk
on fk.constraint_name = tc.constraint_name
join information_schema.referential_constraints rc
on rc.constraint_name = tc.constraint_name
join information_schema.key_column_usage kcu
on kcu.constraint_name = tc.constraint_name
where tc.constraint_type = 'FOREIGN KEY'
and tc.table_schema = 'public'
order by tc.table_name, kcu.column_name;If any of the repository-managed tables are missing their cascade rule, re-run the corresponding script from sql/. For tables created directly in the dashboard (profiles, watchlist, continue_watching, ratings, comments), add the missing FK constraint, for example:
alter table "profiles"
drop constraint if exists profiles_id_auth_fkey,
add constraint profiles_id_auth_fkey foreign key (id)
references auth.users (id) on delete cascade;Add the reset callback URL for every environment to Supabase Auth URL Configuration. The application builds this from the current origin as /auth/reset-password/, for example http://127.0.0.1:8000/auth/reset-password/ during local development.
uzzutv/
├── manage.py
├── requirements.txt
├── db.sqlite3
├── .env
├── sql/ # Supabase SQL setup scripts
│ ├── aniuzu_tables.sql # Aniuzu tables, indexes, RLS
│ ├── continue_watching_progress.sql # UzzUTV resume fields
│ └── watch_parties.sql # Watch Party tables, indexes, RLS
├── stream/ # Django project settings
│ ├── settings.py # env-based config, caching, gzip
│ ├── urls.py
│ └── wsgi.py / asgi.py
└── uzzutv/ # Main app
├── views.py # All views + TMDB integration
├── urls.py # Route table
├── supabase_client.py # Supabase client (client-side auth lives in static JS)
├── supabase_service.py# Server-side Supabase helpers (session verify + admin delete)
├── templates/uzzutv/ # HTML templates
└── static/ # CSS/JS assets
| Route | Description |
|---|---|
/ |
Landing page |
/home/ |
Main browse page (hero + genre rows) |
/movie/ /tv/ |
Movies / TV shows |
/category/<slug>/ |
Category page (mixed movies + TV, paginated): action, romance, comedy, animation, thriller, drama, horror, scifi, popular, top_rated |
/<type>/<title-year>/ |
Detail page (cast, recommendations, rate & comment) |
/movie/<title-year>/watch/ |
Movie player |
/tv/<title-year>/watch/ |
TV player (season/episode select) |
/aniuzu/ |
AniList anime home |
/aniuzu/anime/<title-year>/ |
Anime detail page |
/aniuzu/anime/<title-year>/watch/<episode>/ |
Aniuzu anime player |
/aniuzu/watchlist/ |
Aniuzu watchlist |
/aniuzu/search/ |
AniList anime search |
/aniuzu/continue-metadata/ |
Metadata for Continue Watching cards |
/aniuzu/genres/ |
Anime genre list |
/aniuzu/genres/<genre>/ |
Anime genre browse page |
/aniuzu/seasons/ |
Anime seasons browse page |
/aniuzu/studios/ |
Anime studios list |
/aniuzu/studios/<name>/ |
Anime studio browse page |
/aniuzu/collections/ |
Anime collections list |
/aniuzu/collections/<slug>/ |
Anime collection browse page |
/status/ |
Live catalogue API and playback-host status |
/search/ |
Search |
/faq/ |
FAQ and help centre |
/watchlist/ |
Your saved titles |
/rated/ |
Your ratings (with /rated/<user_id>/ public view) |
/party/ |
Watch Party dashboard (create/join) |
/party/<room_code>/ |
Watch Party room with synchronized playback and video-call controls |
Readable title-year URLs are preferred for detail and watch pages. Existing numeric-ID URLs remain supported for backwards compatibility.
| /auth/ | Sign in / sign up |
| /auth/forgot-password/ | Request a Supabase password reset |
| /auth/reset-password/ | Complete a Supabase password reset |
| /profile/ | Your profile (ratings, watchlist, comments) |
| /profile/<user_id>/ | Public profile |
| POST /delete-account/ | Permanently delete your own Supabase Auth account (CSRF-protected; requires the Supabase session access token + DELETE confirmation) |
| /media-info/<type>/<id>/| Media metadata JSON (used by profile pages) |
| /terms/ /dmca/ | Legal pages |
| /robots.txt /sitemap.xml | SEO endpoints |
The project is configured for PythonAnywhere:
STATIC_ROOTis set to a server path (update insettings.pyif needed)- Collect static files before deploying:
python manage.py collectstatic
- In production, set
DJANGO_DEBUG=FalseandDJANGO_ALLOWED_HOSTSto your domain, and enable theSECURE_*flags for HTTPS. - AniList availability is checked from the Django server. During an AniList outage, the status page may show AniList as temporarily unavailable and Aniuzu may return an unavailable-catalogue state until the upstream API recovers.
- Personalized recommendations
- Better streaming player
- Enhanced episode progress syncing
- Notifications for new episodes
- Progressive Web App (PWA)
This project was created for educational and learning purposes. Users are responsible for ensuring that any content streamed through the platform complies with applicable copyright laws and licensing requirements.
Ujjwal Baunthiyal
This project is licensed under the MIT License.