project-image
web

Jun 26, 2026

    NextJs
    Redux Toolkit
    HLS.js
    Spring Boot
    Postgresql

Hypertube

A full-stack streaming web app: search legally distributable torrents, watch them as they download, with adaptive-bitrate playback, subtitles, comments, and an OAuth2-secured REST API.

Built for the 42 school Hypertube project.


Overview

LayerDescriptionKey Tools
BackendREST API, auth, torrent engine, transcodingSpring Boot 4, Java 21, PostgreSQL, jlibtorrent, ffmpeg
FrontendSPA / SSR clientNext.js 16, React 19, Redux Toolkit, HLS.js, shadcn/Radix UI
InfraReverse proxy + orchestrationDocker Compose, Nginx

Features

Authentication & Profile

  • Register/login with email, username, first/last name, and a hashed password (no plaintext storage).
  • OAuth2 login via 42, Google, and GitHub (3 strategies, only 2 required).
  • JWT-based stateless sessions.
  • Forgot/reset password flow via email (Spring Mail).
  • Preferred language (EN / FR / AR), defaulting to English.
  • Edit own email, avatar, and profile info; view other users' public profiles (email stays private).

Library

  • Search queries two external sources in parallel: YTS and archive.org.
  • Results enriched with ratings/metadata via the OMDb API.
  • Thumbnails show title, year, rating, and cover; watched vs. unwatched state is visually distinguished.
  • No-query view falls back to the most popular/top-rated titles.
  • Infinite scroll (async pagination, no "load more" link) with sorting/filtering by name, genre, rating, and year.

Video Playback

  • Selecting a video starts a real BitTorrent download server-side via jlibtorrent (no webtorrent/peerflix-style libraries).
  • Downloaded bytes are piped live into ffmpeg, which transcodes into multi-bitrate HLS (240p up to 4K, picked based on source resolution) so playback can start before the download finishes.
  • Player (HLS.js) supports adaptive quality switching and seeking into already-downloaded ranges.
  • English subtitles, plus the viewer's preferred language when available, fetched via OpenSubtitles and served as WebVTT/HLS subtitle tracks.
  • Finished downloads are cached on disk; a scheduled job purges videos unwatched for 30+ days.
  • Per-video comments (post + list).

REST API

  • POST /oauth/token — client-credentials grant for third-party API access.
  • GET /public/top10 — public front-page listing of top movies.
  • Full CRUD on users, movies, and comments per the spec (ownership checks return 403 on cross-user writes).
  • OpenAPI/Swagger docs exposed via springdoc.

Project Structure

hypertube/
├── backend/                        # Spring Boot API
│   └── src/main/java/com/hypertube/
│       ├── controller/              # Auth, User, Movie, Library, Comment, Streaming, Subtitle, OAuthToken, Public, ApiClient
│       ├── service/                 # Business logic
│       │   ├── BtTorrentService.java / TorrentDownloadService.java   # jlibtorrent download engine
│       │   ├── TranscodingService.java                               # ffmpeg → multi-quality HLS
│       │   ├── StreamingService.java                                # serves master/quality playlists & segments
│       │   ├── ScheduledMovieCleanupService.java                     # purges stale downloads
│       │   └── external/            # YtsService, ArchiveService, OmdbEnrichService, SubtitleService
│       ├── security/                 # JWT filter, OAuth2 success/failure handlers, provisioning
│       ├── entity/, repository/, dto/, config/
│       └── ...
│
├── frontend/                       # Next.js app
│   └── src/
│       ├── app/(auth)/              # signin, signup, forgot/reset password
│       ├── app/(home)/              # library, movie/[id], profile/[id], profile/me, history, saved-movies
│       ├── components/               # VideoPlayer (HLS.js), navbar, profile, history, ui (shadcn)
│       ├── services/                 # API clients (moviesApi, commentApi, appsApi)
│       └── lib/                      # Redux store, auth slice, api client
│
└── infra/
    ├── docker-compose.yml           # postgres + backend + frontend + nginx
    └── nginx.conf                   # routes /, /api/, /uploads/, /videos/

Tech Stack

Backend: Spring Boot 4 (Java 21), Spring Security + OAuth2 Client, Spring Data JPA, PostgreSQL, JJWT, jlibtorrent, ffmpeg (via ProcessBuilder), Apache Tika, springdoc-openapi.

Frontend: Next.js 16 / React 19, Redux Toolkit, HLS.js, Zod, shadcn/ui + Radix, Tailwind CSS, react-intersection-observer (infinite scroll).

Infra: Docker multi-stage builds (Maven → Temurin JRE for backend, Node → standalone Next.js runtime for frontend), Nginx reverse proxy, Docker Compose.


Getting Started

git clone <repo-url>
cd hypertube
cp .env.example .env        # fill in DB, JWT, mail, OAuth (42/Google/GitHub), OMDb & OpenSubtitles keys

make            # docker compose up --build
# or
make run        # up without rebuilding
make down       # tear down

Once running:

ServiceURL
App (via Nginx)http://localhost
API directlyhttp://localhost:8081
Swagger UIhttp://localhost:8081/swagger-ui/index.html

Required environment variables (see .env.example)

  • DB_URL, DB_USERNAME, DB_PASSWORD — PostgreSQL
  • JWT_SECRET, ACCESS_TOKEN_EXPIRATION
  • MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD — password reset emails
  • GOOGLE_CLIENT_ID / SECRET, FT_CLIENT_ID / SECRET (42), GITHUB_CLIENT_ID / SECRET
  • OPENSUBTITLES_API_KEY, OPENSUBTITLES_LANGUAGES
  • OMDB_API_KEY
  • FRONTEND_URL, STORAGE_DIR

All secrets stay in a local, git-ignored .env — never committed.


How Streaming Works

  1. User selects a result → backend resolves a torrent (YTS magnet or archive.org file) and starts the download with jlibtorrent.
  2. As pieces arrive, a feeder thread streams the partial file into ffmpeg's stdin.
  3. ffmpeg transcodes into several HLS quality renditions (240p–4K, capped to source resolution) and writes segments + playlists to disk as they're ready.
  4. The frontend's HLS.js player starts playback against /stream/{movieId}/master.m3u8 as soon as the first segments exist, switching quality adaptively.
  5. On completion, the file is kept on disk for instant replay; a scheduled cleanup job deletes anything unwatched for 30+ days.

Security Notes

  • Passwords are hashed, never stored in plain text.
  • Stateless JWT auth (no server-side sessions) + Spring Security OAuth2 login flow.
  • CORS is restricted to known frontend origins.
  • Profile update endpoints enforce ownership (403 on attempts to edit another user).
  • Credentials and API keys are environment-driven via .env, excluded from git.

Disclaimer

This project only integrates with sources that provide royalty-free or legally distributable content (e.g. YTS, archive.org). Built for educational purposes as part of the 42 school curriculum — do not use to download or stream copyrighted material without proper rights.