KVideo aggregates whatever sources you feed it, and its docs contradict each other on where your data lives
一个基于 Next.js 16 构建的现代化视频聚合播放平台。采用独特的 "Liquid Glass" 设计语言,提供流畅的视觉体验和强大的视频搜索功能。
At a glance
- What is it?
- KVideo is a Next.js 16 video aggregation and playback front end that ships with no sources at all: search, IPTV playlists, danmaku and recommendations are all built around catalogs the deployer supplies. What the README does not settle is whether your watch history stays on your device, and the proxy that forwards IPTV streams takes its request headers from the playlist file.
- Who is it for?
- KVideo is a player and an index, not a catalog, and the operator supplies the content, which means the licensing and the trust boundary both sit with whoever writes the playlist files. Use it if you want multi-source search, HLS playback with per-source latency ranking, and IPTV in one Next.js app.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 51 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 5, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The only CI reference in the README points at another account's repository
The first line of the README is a workflow badge, and the workflow it points at lives under a different account than the repository itself: a Github_Upstream_Sync workflow on a repository owned by sky06walker. The name of that workflow says the project tracks something upstream, and no section of the README ever names what it syncs from or how conflicts are resolved. The rest of the header is a DeepWiki link, a Buy Me A Coffee heading that sits above the project title, and a mirror of the project description.
The documentation is written in Chinese throughout, from the design section to the IPTV notes, so the feature names in the interface are the ones to search for rather than English equivalents.
Deployment targets are plural and visible in the manifest. `wrangler.toml` sits at the repository root, `@cloudflare/next-on-pages` is a dev dependency, and there is a `pages:build` script, which lines up with the hosted demo on a pages.dev domain. The Docker path and the two TV wrappers are separate targets in the same repository.
The repository ships no video sources, no premium sources and no IPTV playlists
One blockquote near the top says it outright: the repository does not include any video source, premium source or IPTV source by default, and the deployer has to configure content they are authorized to use, that is legal where they run it, and that permits the way they deploy it.
Everything below that line is machinery for catalogs someone else supplies. Search fans out across sources in parallel and streams results back over Server-Sent Events. Sources can be added by hand, pasted as a JSON array in the import settings, or subscribed to through a JSON link that updates itself. Non-admin users can keep personal sources, and the README says those are isolated per user so they do not disturb anyone else.
That split is the design to understand before deploying. The search, resolution probing, quality labels, danmaku matching and recommendation engine are all real work, and all of them run against entries the operator provides.
The IPTV proxy sends whatever User-Agent and Referer the playlist file names
This is the sharpest edge in the project. M3U playlists can carry `http-user-agent` and `http-referrer` attributes per channel, and the proxy parses them and passes them upstream. A channel that specifies its own User-Agent is routed through the proxy on purpose, because the browser's own headers cause problems on some channels: the README names CCTV-style channels that return audio with no picture as the case this fixes.
The built-in HLS proxy also handles CORS and rewrites M3U8 URLs, sniffs the response body when the content type is unclear while keeping binary stream data intact, and follows 3xx redirects. Timeouts are fixed and documented: 15 seconds per request, 30 seconds for loading, 20 seconds per segment, with 3 playlist retries. At most 3 sources are pulled at once and each source caches its own channel list.
The trust boundary is the playlist file. Whoever supplies a playlist chooses the headers your server sends on your behalf, and the only access control the README documents for any of it is an `iptv_access` permission.
Watch history is local-only in one bullet and Redis-backed in another
The watch history section states a privacy guarantee in plain terms: all data is stored locally and is not uploaded to the server. It also caps history at 50 entries, dedupes by title so the same title from different sources merges into one row, and offers per-entry deletion and a clear-all.
The cross-device configuration section says something else. It describes Upstash Redis holding user configuration under a key of the form `user:config:{profileId}`, with the profile identifier hashed via SHA-256, reached through an `/api/user/config` endpoint, and it says this Redis instance is shared with watch history and favorites sync. Sync pulls on load by comparing an `updatedAt` timestamp, pushes after a 3 second debounce, and covers sources, premium sources, subscriptions, blocked categories, sort order and locale.
Both statements describe the same build, so one of them describes a configuration where Redis is absent. The README says as much in a third place: without Redis configured, sync degrades quietly and the app keeps working with local storage only.
Dev and start share the same LAN access wrapper, and the build is webpack plus a transpile pass
Both `dev` and `start` are the same wrapper script with a different Next.js subcommand:
"dev": "node scripts/next-with-lan-access.mjs dev",
"build": "next build --webpack && node scripts/transpile-client-assets.mjs .next/static",
"start": "node scripts/next-with-lan-access.mjs start",
"pages:build": "next-on-pages && node scripts/transpile-client-assets.mjs .vercel/output/static/_next/static"A script named for LAN access sits in front of the production start command, so how widely the app binds is a property of that wrapper rather than of the environment it runs in. The build is also explicit: `--webpack` rather than the newer bundler, followed by an esbuild pass over the static output, and the Cloudflare target repeats that pass over its own output directory.
The compose file is thinner than any of that. It declares one service, publishes port 3000, restarts always, and passes a single variable, `NODE_ENV=production`. No Redis URL and no Redis token, which means the sync and history features have no credentials through the documented container path.
Premium mode is a URL you type, while IPTV has a permission gate
Two features are supposed to be walled off from ordinary use, and they are walled off in different ways. The IPTV section names a permission, `iptv_access`, that controls who can reach the feature at all.
Premium mode has no such permission described anywhere. Its documented entry is typing `/premium` into the address bar, and its separation is described as complete physical isolation: separate content, separate source management, separate player, display and danmaku settings, separate recommendations built from premium watch history, and multi-source results interleaved rather than grouped.
The ordinary and premium modes also keep independent state in three other places: favorites are capped at 100 per mode, recommendations cache for 30 minutes per mode, and the recommendation tab only appears once two or more titles have been watched. Per-user isolation is documented for favorites and for personal sources, keyed on the same hashed profile identifier the sync uses.
So one gated feature is gated by a permission and the other by a path. Treat the URL as the only thing standing between a user and the premium surface.
Ad filtering has four modes and the danmaku endpoint has a build-time name
Ad filtering offers off, keyword filtering, a heuristic mode marked Beta, and an aggressive mode, switchable from the player settings menu with immediate effect. Extra keywords can come from an environment variable or a file, and the implementation is described as streaming so it adds nothing to load time. On a deployment where the operator supplies the sources, this is the one feature that edits content the operator's own catalogs point at.
Danmaku runs against a self-hosted aggregation API compatible with the danmu_api format, rendered on canvas, with scrolling, top and bottom comments assigned to separate tracks so they do not overlap. Opacity runs from 10 to 100 percent and font size from 14 to 28 pixels, and the display area can be limited to a quarter, half, three quarters or all of the screen.
The endpoint can be preset for every user through `DANMAKU_API_URL` or `NEXT_PUBLIC_DANMAKU_API_URL`. The two names differ only by the prefix that exposes a variable to browser code, so the one that reaches the client is fixed when the bundle is built, not when the container starts.
The only tagged release is a dated Android TV WebView wrapper
There is exactly one GitHub release, `android-tv-apk-2026-04-02`, published on 2026-04-02. It matches the Android TV section, which describes a lightweight APK built on an Android WebView that loads the KVideo web page directly. There is no library release and no version tag for the web app itself.
Application versioning lives elsewhere: the manifest reads 4.9.20, and the repository carries a CHANGELOG.md alongside a CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md and an .npmrc. The last push on record is 2026-08-16 and the repository is not archived, so the tree is ahead of the one dated artifact by several months.
The tree also holds an `apple-tv/` directory with no corresponding release, a `verification/` directory whose purpose no document explains, and an `app-release.json`. Tests run through Node's test runner via tsx over a `tests/` directory, and lint is a flat `eslint` call against `eslint.config.mjs`.
Editorial conclusion
KVideo is a player and an index, not a catalog, and the operator supplies the content, which means the licensing and the trust boundary both sit with whoever writes the playlist files. Use it if you want multi-source search, HLS playback with per-source latency ranking, and IPTV in one Next.js app. Before you deploy, read the IPTV proxy section of this write-up and decide whose M3U files you trust, because they choose the User-Agent and Referer your server sends upstream. Pin `next`, decide whether watch history may live in Redis, and remember the only tagged release is an Android TV WebView wrapper while the app version is 4.9.20.
Frequently asked questions
What is KVideo?
KVideo is a self-hosted video aggregation and playback app built on Next.js 16, React 19 and Tailwind CSS v4, with HLS playback through hls.js, parallel multi-source search over Server-Sent Events, IPTV playlist support, danmaku, Douban metadata and per-source recommendations. Its interface uses a Liquid Glass design built on backdrop-filter, and it runs on desktop, mobile, TV and as a PWA.
Does KVideo include any video sources or IPTV playlists?
No. The README states that the repository bundles no video source, no premium source and no IPTV source, and that the deployer must supply authorized content that is legal for their situation and permits their deployment method. Sources can be added by hand, pasted as a JSON array, imported as an M3U playlist, or subscribed to through a JSON link that updates itself.
What does KVideo need for cross-device config sync?
An Upstash Redis instance reached with `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`, shared with the watch history and favorites sync. Configuration is stored under a `user:config:{profileId}` key, pulled on load by comparing an `updatedAt` timestamp and pushed after a 3 second debounce, and without Redis the feature degrades quietly to local storage only.
How do you deploy KVideo?
Three targets are visible in the repository: Docker Compose, which publishes port 3000 and passes only NODE_ENV=production; Cloudflare Pages, through next-on-pages plus a transpile pass over the output, which matches the hosted demo on a pages.dev domain; and an Android TV APK that is a WebView wrapper around the web app. An `apple-tv/` directory exists without a matching release.
How does the KVideo IPTV proxy handle requests and timeouts?
The built-in proxy handles CORS, rewrites M3U8 URLs, follows 3xx redirects and sniffs the body when the content type is unclear while keeping binary data intact. Timeouts are 15 seconds per request, 30 seconds for loading and 20 seconds per segment, with 3 playlist retries, at most 3 sources pulled at once, and per-source channel caching. Access is gated by an `iptv_access` permission.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kuekhaoyang-kvideo)