A high-performance, browser-native streaming dashboard that reverse-engineers the MovieBox/AOneRoom mobile app API and exposes it through a local web dashboard. Supports H.264 & H.265 content, subtitles, watchlist, history, and external player integration.
For a native mobile experience, check out Genga-World — a native Android application built using Jetpack Compose that integrates with this internal API to deliver the streaming platform on mobile devices.
Browser (localhost:3001)
│
│ HTTP REST calls
▼
FastAPI Backend (localhost:8000) ◄──► MovieBox/AOneRoom API Servers
│ (api6.aoneroom.com, etc.)
│
├── /proxy-media ──────────────► Raw CDN streams (CORS bypass)
├── /play-compat ──────────────► FFMPEG H.265→H.264 Transcode Pipe
└── /download ──────────────► FFMPEG/HTTPX file download pipe
| Component | File | Purpose |
|---|---|---|
| Backend API Server | moviebox_api_server.py |
FastAPI server, all endpoints |
| MovieBox API Client | moviebox_api/client.py |
Signs & sends requests to AOneRoom |
| Auth Module | moviebox_api/auth.py |
Login, session, OTP |
| Content Module | moviebox_api/content.py |
Home, search, detail, episodes |
| Stream Module | moviebox_api/stream.py |
Playback URLs, stream resolution |
| Dashboard (Frontend) | dashboard/ |
Next.js 16 web app |
| Video Player | dashboard/components/VideoPlayer.tsx |
ArtPlayer-based video player |
| Home Page | dashboard/app/page.tsx |
Main browsing UI |
| Detail Page | dashboard/app/detail/[id]/page.tsx |
Movie/series details |
| Watch Page | dashboard/app/watch/[id]/page.tsx |
Fullscreen player UI |
- Python 3.10+ — for the backend
- Node.js 18+ — for the frontend
- FFMPEG — must be in system PATH (for H.265 transcoding and downloads)
# Install via Scoop (Windows) scoop install ffmpeg
cd moviebox-internal-api
python moviebox_api_server.pyBackend runs on http://localhost:8000
Interactive API docs available at: http://localhost:8000/docs
cd dashboard
npm run dev -- --port 3001Frontend runs on http://localhost:3001
| Route | Description |
|---|---|
http://localhost:3001/ |
Home — Browse movies, anime, rankings, search |
http://localhost:3001/detail/{id} |
Detail — Poster, synopsis, cast, episode list, community posts |
http://localhost:3001/watch/{id} |
Watch — Full-screen player with subtitles, seeking, download |
http://localhost:3001/profile |
Profile — User account info |
/watch/{subjectId}?s=1&e=5
s = season number (default: 1)
e = episode number (default: 1)
All endpoints are served from http://localhost:8000.
Authenticate with email/phone + password.
Request Body (JSON):
{
"account": "user@example.com",
"password": "yourpassword",
"authType": 1
}Response:
{
"status": "success",
"user": { "nickname": "...", "avatar": "..." }
}Sets a
session_idcookie on success. All subsequent requests must include this cookie.
Create a new MovieBox account.
Request Body (JSON):
{
"account": "user@example.com",
"password": "yourpassword",
"otp": "123456",
"authType": 1
}Send a verification code to email/phone.
Request Body (JSON):
{
"account": "user@example.com",
"authType": 1,
"type": 1
}
type:1= Register,2= Login
Invalidate the current session.
Get the currently logged-in user's profile.
Response:
{
"logged_in": true,
"user": { "nickname": "...", "avatar": "...", "email": "..." }
}Fetch the main home feed with curated content sections.
Response:
{
"data": {
"list": [
{
"title": "Trending Now",
"type": "movies",
"items": [ { "subjectId": "...", "title": "...", "cover": "..." } ]
}
]
}
}Fetch anime content section.
Fetch movie/series rankings.
Fetch the "Discovery" / "For You" feed.
Fetch trending content.
Fetch the movies section.
Get search autocomplete suggestions.
Query Params:
| Param | Default | Description |
|---|---|---|
q |
"" |
Partial search query |
Full-text search for movies and series.
Query Params:
| Param | Type | Description |
|---|---|---|
q |
string | Search query |
Get full metadata for a movie or series.
Response:
{
"code": 0,
"data": {
"title": "Classroom of the Elite",
"subjectId": "123456",
"subjectType": 2,
"cover": "https://...",
"poster": "https://...",
"score": "9.1",
"runtime": "24",
"releaseTime": "2022",
"description": "...",
"cast": [ { "name": "...", "role": "...", "avatar": "..." } ],
"related": [ { "subjectId": "...", "title": "..." } ],
"likeStatus": 1
}
}
subjectType:1= Movie,2= Series/Anime
Get full season and episode list for a series.
Response:
{
"data": {
"seasons": [
{
"seasonNumber": 1,
"episodes": [
{ "episodeNumber": 1, "title": "Episode 1" }
]
}
]
}
}Resolve the playback URL for a movie or episode.
Query Params:
| Param | Default | Description |
|---|---|---|
season |
1 |
Season number |
episode |
1 |
Episode number |
quality |
720p |
Stream quality (360p, 480p, 720p, 1080p) |
Response (Standard H.264 stream):
{
"url": "https://cdn.example.com/video.mp4",
"quality": "720P",
"cookie": "signCookie=...",
"subtitles": [ { "lanName": "English", "url": "..." } ],
"streamId": "..."
}Response (Auto-transcoded H.265 stream):
{
"url": "http://localhost:8000/play-compat/{subject_id}?season=1&episode=1&quality=720P",
"cookie": "",
"isTranscoded": true,
"runtime": "24"
}When
isTranscoded: true, the frontend automatically routes through the FFMPEG transcode pipe. No manual action needed.
FFMPEG Live Transcode Pipe — converts HEVC/H.265 to browser-native H.264 fMP4.
Streams video directly to the browser. Supports HEAD requests for range negotiation.
Query Params:
| Param | Default | Description |
|---|---|---|
season |
1 |
Season number |
episode |
1 |
Episode number |
quality |
720P |
Quality level |
start_time |
0 |
Seek to this second before transcoding |
This endpoint requires FFMPEG to be installed and in the system PATH.
Universal media proxy. Forwards any CDN URL through the local server to bypass CORS restrictions.
- Auto-rewrites
.m3u8HLS manifests to route segments through the proxy - Converts
.srtsubtitles to WebVTT on the fly - Adds
Access-Control-Allow-Origin: *to all responses
Query Params:
| Param | Description |
|---|---|
url |
The CDN media URL to proxy (URL-encoded) |
cookie |
The signCookie from the stream response |
start_time |
(optional) Resume timestamp in seconds |
Download a movie or episode file to disk.
- For MP4 streams: proxies raw bytes with correct headers (browser native download)
- For DASH/HLS streams: uses FFMPEG to remux to
.tsand pipes to the browser
Query Params:
| Param | Default | Description |
|---|---|---|
season |
1 |
Season number |
episode |
1 |
Episode number |
quality |
720p |
Quality level |
title |
Movie |
Filename prefix for the download |
Get subtitle tracks for a movie or episode.
Response:
{
"data": {
"list": [
{ "id": "...", "lanName": "English", "url": "https://cdn.../sub.srt" }
]
}
}Get subtitles linked to a specific stream ID.
Fetch extended community captions from the official extended subtitles API.
Search OpenSubtitles for matching subtitle tracks.
Get the user's complete watch history.
Response:
{
"data": { "list": [ { "subjectId": "...", "title": "...", "seeTime": 1234 } ] }
}Get the user's watchlist ("Want to Watch" list).
Add or remove a title from the watchlist.
Query Params:
| Param | Type | Description |
|---|---|---|
subject_id |
string | The movie/series ID |
add |
bool | true to add, false to remove |
subject_type |
int | 1 = Movie, 2 = Series |
Get the saved playback position for a specific stream.
Save a playback position.
Query Params:
| Param | Type | Description |
|---|---|---|
subject_id |
string | Movie/series ID |
stream_id |
string | Stream ID from /stream response |
position_ms |
int | Position in milliseconds |
Mark a subject as "have seen".
Delete a specific item from watch history.
Report full playback progress (used for "Continue Watching" sync).
Request Body (JSON):
{
"subject_id": "...",
"progress_ms": 150000,
"total_ms": 1440000,
"status": 1
}Launch MPV or VLC with the stream URL and subtitle file.
Query Params:
| Param | Default | Description |
|---|---|---|
player |
mpv |
mpv or vlc |
url |
required | Stream URL |
cookie |
"" |
Authentication cookie |
start_time |
0 |
Resume timestamp in seconds |
subtitle_url |
"" |
Path or URL to subtitle file |
duration |
0 |
Total duration in seconds (for MPV timeline) |
title |
"" |
Media title shown in player |
subject_id |
"" |
Used to sync history |
Get community discussion posts for a movie/series.
Post a review/comment.
Query Params:
| Param | Description |
|---|---|
subject_id |
The movie ID |
content |
Text content of the post |
Like a post.
Get the total post count for a title.
Get trending community groups.
Get interactive groups.
When playing a transcoded H.265 stream, the standard browser currentTime API reports only the local pipe position (starts at 0). The VideoPlayer.tsx component implements a Virtual Timeline Engine that:
- Intercepts the native
video.currentTimegetter/setter viaObject.defineProperty - Adds a seek offset so the progress bar shows the true global time (e.g.,
15:23 / 24:00) - Re-tunes the transcode pipe when seeking by calling
/play-compat/{id}?start_time={seconds}
Subtitles can be toggled on/off and switched between languages without reloading the video. The player uses:
art.subtitle.url = newSubtitleVttUrl;
art.subtitle.show = true;The backend's /proxy-media endpoint auto-converts .srt to WebVTT.
Clicking "Offline Mirror" triggers a silent <a> element download instead of opening a new tab:
const link = document.createElement('a');
link.href = downloadUrl;
link.setAttribute('download', filename);
link.click();- Check the backend logs for
500errors on/stream/{id} - The stream may be HEVC-only — the system will auto-route to
/play-compat - Ensure FFMPEG is installed:
ffmpeg -version
- This happens on transcoded streams. The Virtual Timeline Engine should handle it.
- Check browser console for
[VIRTUAL VOD]logs to confirm it's active.
- The backend
/play-compatresponse includes aruntimefield (in minutes) - The frontend reads this and passes it as
duration(in seconds) to the player plugin
- Ensure the subtitle URL is proxied through
/proxy-media(for CORS) - The backend auto-detects
.srtand converts to WebVTT
- Duration is now passed as
--length={seconds}via the/launch-playerendpoint - Ensure your frontend passes
durationin the axios POST to/launch-player
- Ensure the backend is running on port 8000
- The
CORSMiddlewareallowslocalhost:3000andlocalhost:3001
| Layer | Technology |
|---|---|
| Frontend Framework | Next.js 16.2 (App Router) |
| UI Styling | Tailwind CSS v4 |
| Video Player | ArtPlayer v5 + hls.js + dash.js |
| HTTP Client (Frontend) | Axios |
| Backend Framework | FastAPI |
| Media Proxy | HTTPX (async) |
| Transcoding | FFMPEG (subprocess pipe) |
| Auth | Cookie-based session (httpOnly) |
moviebox/
├── moviebox_api_server.py # Main FastAPI server (all endpoints)
├── moviebox_api/
│ ├── __init__.py # Module exports
│ ├── auth.py # Login, register, OTP
│ ├── client.py # Signed HTTP client for AOneRoom API
│ ├── content.py # Home, search, movie detail
│ ├── stream.py # Stream/playback URL resolution
│ ├── user.py # User profile, history, watchlist
│ └── utils.py # Request signing utilities
├── dashboard/
│ ├── app/
│ │ ├── page.tsx # Home page
│ │ ├── detail/[id]/page.tsx # Detail page
│ │ ├── watch/[id]/page.tsx # Watch/player page
│ │ └── profile/page.tsx # Profile page
│ ├── components/
│ │ ├── VideoPlayer.tsx # ArtPlayer-based video player
│ │ └── MovieCard.tsx # Movie card UI component
│ └── lib/
│ └── api.ts # Frontend API client (axios wrappers)
├── local_history.json # Local watch history cache (auto-created)
└── README.md # This file