35 Commits
Author SHA1 Message Date
adminandClaude Opus 4.8 8e61dc111c feat: keyboard seek, playback speed, and persist edited settings
Player
- Arrow keys seek by 5s, Shift for a 30s jump on long recordings, J/L for +-30s,
  Space toggles playback, and up/down adjust volume. Dragging a scrubber across a
  multi-hour replay is unusable — one pixel is tens of seconds — so keys are the
  practical way to move around a long recording.
- The shortcuts ignore key events from inputs, so typing a chapter time in the
  replay editor no longer scrubs the video.
- Add a playback speed selector. It is a replay tool: speeding a live stream up
  would only push it further ahead of the encoder's buffer.

Replay editor
- Stop playback restarting on every edit. The player's source effect depended on
  the `lines` and `qualities` arrays, and callers such as the replay editor pass
  fresh literals on every render, so adding a chapter or typing a title was
  treated as a source change and reloaded the stream from the beginning. The
  effect now depends on the resolved source URL instead of the arrays it came
  from. Measured: adding three chapters leaves the media requests unchanged and
  the playhead advancing.

Settings persistence
- Settings edited in the UI were applied to the in-memory settings object and
  never written down, so the brand, transcode ladder and load-balancing strategy
  silently reverted to the config.py defaults on every restart. They are now
  saved to backend/settings.json (atomic temp-file replace) and re-applied at
  startup, with only known keys accepted so a hand-edited file cannot inject
  arbitrary attributes.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-09-29 12:44:33 +08:00
adminandClaude Opus 4.8 cdf591f933 fix: serve replay media from nginx and unbreak settings saves
Replay
- Hand the replay file to nginx with X-Accel-Redirect instead of streaming it
  through the backend. A replay is tens of gigabytes, so Python was copying the
  whole file and holding a worker for the entire transfer: measured 8 MB in
  8.46s (~1 MB/s). nginx serves it from disk with sendfile at ~400 MB/s (1 GB in
  2.68s) and the backend process does not appear in CPU usage during the transfer.
  Authentication and the path-traversal check still run in Python; only the bytes
  moved. The internal location is what keeps the path from being reachable
  directly and bypassing that check.
- nginx reloads and the media directory is aliased internally.

Settings
- GET /settings/transcode-profiles returned the bare mapping while the settings
  page reads `data.profiles`, so it always saw undefined, fell back to an empty
  object, and every save posted `{"profiles": {}}` — which the PUT rejects as
  "at least one profile". Saving settings could never succeed. The response now
  matches the shape PUT accepts, verified by round-tripping it.

Auth
- Derive the admin token instead of storing it. The token is
  sha256(password + ":token"), but it was kept in a module-level set, which only
  works with one process: with uvicorn --workers N, logging in on one worker left
  the others rejecting the token and the admin appeared logged out at random.

Viewer
- Show the real session id in the stats panel; the previous code assigned it
  from a local variable that was never populated, so it always read "不可用".
- Send the first heartbeat before the first command poll. Both fired together on
  mount, so the poll reached the backend before the session existed and 404'd on
  every page load.

Housekeeping
- Ignore backend/nodes.json; the admin node manager writes it at runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-09-29 11:47:52 +08:00
adminandClaude Opus 4.8 4ee945ca0d feat: encode on the GPU and restart individual renditions
Hardware encoding
- Detect the best usable encoder at startup by encoding a test frame with each
  candidate in turn (nvenc, qsv, amf, then libx264). Being compiled into ffmpeg
  is not enough: h264_amf fails without amfrt64.dll and h264_qsv fails without an
  Intel iGPU, so the probe is the only reliable answer.
- Translate the quality target into the chosen encoder's own vocabulary. libx264's
  -crf and "ultrafast" mean nothing to h264_nvenc, which takes -cq and p1..p7, so
  passing the configured values straight through would have been silently wrong.
- On the reference GTX 1060 a 1080p->720p rendition costs ~62% of a core on NVENC
  versus ~109% on libx264 ultrafast.

Per-rendition supervision
- Track encoder processes per rendition, not per stream. is_running answered
  "any alive", so when the source ("原画") encoder died while 1080p and 720p kept
  running, the supervisor saw a healthy stream and never restarted it — the
  source URL then 404'd permanently. It now requires every rendition to be alive
  and reports which ones are down.
- Restart only the dead renditions, so a single failure no longer interrupts
  viewers on the renditions that were fine.

Production serving
- Serve the built frontend from FastAPI with SPA fallback, so production is one
  process and one port with no Node runtime. Node becomes a build-time dependency.
- start.ps1/start.sh build the frontend on first run instead of failing to a 503.

Cleanup
- Drop the duplicated top-left LIVE badge; the control strip already carries it.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-09-27 18:56:21 +08:00
adminandClaude Opus 4.8 996e3fea39 feat: consolidate on the shadcn frontend and harden playback
Frontend
- Replace the Ant Design app with the shadcn/ui app as the single frontend,
  keeping the portfolio side-by-side revertible in this commit.
- Drop the abandoned mdui experiment and the AntD tree so there is one app,
  one router and one design system to maintain.

Live playback
- Stop racing the encoder. lowLatencyMode + liveSyncDurationCount:1 targeted a
  single 4s segment, so playback ran at up to 1.10x and the forward buffer
  bottomed out at 0.1s, re-buffering every segment. Measured over 40s:
  buffer min 0.1s -> 8.9s, rate 1.00 constant, 0 stalls.
- Remove the 落后 labels and the page-level edge-snapping timer that fought
  hls.js over two different definitions of "latency".
- Live can no longer be paused: the control is not rendered while it plays, so
  the icon can never misreport state.
- Fix autoplay: muted autoplay started silently and never unmuted, and the
  gesture that restored sound also toggled playback off. One owner now.

Source ("原画") rendition
- Publish the publisher's stream remuxed losslessly via -c:v copy alongside the
  transcoded ones, so a source option exists even when every ladder entry
  downsizes.
- Give renditions an ASCII path slug: the HLS path is part of the URL and
  MediaMTX rejects non-alphanumeric paths, so 原画 could never be served.

Backend
- Supervise MediaMTX from the backend: start it at boot and restart it on exit,
  never killing an origin it did not start.
- Clear stale HLS output before restarting an encoder; an ENDLIST-less playlist
  left by a dead encoder hangs hls.js forever.
- Mark JSON timestamps as UTC. utcnow().isoformat() emitted a naive string that
  JS read as local time, so every viewer heartbeat looked 8 hours old in UTC+8
  and the live board's active-viewer count was permanently zero.
- Rebind a viewer session when it arrives on another stream instead of failing,
  and survive the StrictMode double-heartbeat race on first insert.
- Replace the Linux-only /proc and os.getloadavg metrics with psutil.
- Complete MediaMTX's cookieCheck redirect inside /hls proxying, and hold the
  httpx client open for the streamed body.

Housekeeping
- Ignore RainOSS (it stores accessKeySecret), runtime output, TLS keys and
  .venv; remove generated screenshots, load test scripts and stray files.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-09-22 18:07:56 +08:00
adminandClaude Fable 5 186e32caf1 feat: improve replay workflow and viewer UI
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-30 15:35:28 +08:00
admin 57b00affbd feat: add Windows edge node launcher 2026-08-20 10:37:31 +08:00
admin da01967c2e feat: add Windows-first deployment flow 2026-08-20 10:21:44 +08:00
admin 7353d140b8 fix: restore admin sidebar routing 2026-08-20 09:56:53 +08:00
admin 1e87be31da fix: keep dashboard aggregate metrics visible 2026-08-20 09:47:39 +08:00
admin 38824a0055 fix: render metrics and smooth route transitions 2026-08-20 09:45:49 +08:00
admin 655783b724 fix: place app routes inside router context 2026-08-20 09:25:29 +08:00
admin 38bcbfc697 fix: sync program status updates 2026-08-20 09:08:24 +08:00
admin ed62d42a63 feat: expand livestream management platform 2026-08-19 22:24:01 +08:00
admin 383c066d86 feat: complete program platform experience 2026-08-19 17:25:00 +08:00
admin e50b8b480b 完善节目平台与直播监控体验 2026-08-18 09:26:41 +08:00
admin 229a8d0791 修复观看页白屏与异常状态 2026-08-17 16:39:50 +08:00
admin e83b56b22f 完善观众端交互视觉体系 2026-08-17 14:29:31 +08:00
admin a8067db922 完善观众端目录与回放状态降级 2026-08-17 14:15:44 +08:00
admin 3060828774 完善跨平台回放与实时状态更新 2026-08-17 13:16:12 +08:00
admin a493ef2309 完善节目管理与回放播放体验 2026-08-17 09:59:04 +08:00
admin b265518084 补充直播创建时间回归测试 2026-08-12 21:58:27 +08:00
admin 20cd99efa4 完善直播节点监控与边缘代理 2026-08-12 21:14:42 +08:00
admin 058bb4b99f 重做观众端衬线体直播界面 2026-08-12 17:59:28 +08:00
admin 2685ebdd47 UI彻底重做: 全新设计语言, 纯手工精调
设计系统:
- CSS变量体系: 暖纸底色(#F9F7F4), 6级灰阶, 5级阴影
- 字体节奏: clamp响应式字号, -0.02em字间距
- 间距韵律: 4-128px 8级递进

首页:
- 96px大留白英雄区, clamp(36px,5vw,56px)巨型标题
- 200px暗色封面卡片(#2C2824)
- LIVE红点脉动标签, 待开播黑底白字徽章
- hover抬升2px + 阴影, 淡棕描边高亮

管理页:
- stat卡片: 1px边框+3px彩色顶线
- 表格卡片化: borderRadius 12, 统一白底+边框
- 表单/设置: 统一圆角8px, 大内边距36px

播放页:
- #1A1814暗底, 毛玻璃顶栏
- 视频圆角12px + boxShadow景深
- document.title动态标题

登录页: 56px内边距, 品牌居中, 返回链接
2026-08-10 17:44:24 +08:00
admin f0b8c2d64f UI全面精雕: 米兰色衬线体系 + 全页质感升级
主题层:
- 更精细的色彩层级(primaryBg/hover/active等语义色)
- 字号体系: heading1-5, fontSize 15 基准
- 控件高度 40/48/32 三档, 圆角 6/10
- 三层阴影分层, motionDuration

首页:
- 大幅面渐变英雄区(64px padding, 40px 标题)
- 直播卡片: 暖棕渐变封面, LIVE 脉冲红点标签
- 悬停 transform+shadow 过渡, 描边高亮

仪表盘:
- 彩色顶部边框卡片(绿/蓝/棕), 品牌名+标语头部

登录页:
- 48px 大内边距, 返回首页链接

直播列表:
- 描述行内展示, 推流码截断, 操作图标组

节点页:
- 发光圆点在线指示器, 三色 CPU/内存进度阈值
- 在线/离线统计, Origin 标签

播放页:
- 暗色 #1A1815 背景, 毛玻璃顶栏
- 圆角视频容器, box-shadow 景深
- document.title 动态标题
2026-08-10 16:30:39 +08:00
admin d4a3742136 品牌真正接入全站渲染 + document.title 同步 + 设置页实时刷新
- BrandContext 全局上下文: 挂载时拉取 GET /api/settings/brand
- HomePage: 品牌名称/标语/Logo 动态渲染, 封面卡片精调
- AdminLayout: 侧栏品牌名称/顶栏标语动态显示
- LoginPage: 登录页品牌 Logo + 名称
- WatchPage: 播放页接入品牌上下文
- Settings: 保存后调用 refresh() 全站即时刷新
- App.tsx: document.title 跟随 brand_name 变化
- 组件分层: AppRoutes + BrandProvider 避免 import 循环
2026-08-10 16:28:00 +08:00
admin efde5597bd 品牌化设置 + Claude米色衬线体主题 + 全站中文
- 品牌设置API: GET/PUT /api/settings/brand (名称/标语/Logo)
- 管理页系统设置: 品牌编辑器表单(名称/标语/Logo URL)
- 恢复Anthropic风格: 米色(#FBF9F6) 底色, Source Serif 4 + Noto Serif SC 衬线体
- 暖棕(#C2693A) 主色调, 更充裕的间距与留白, 庄重大气
- 全站中文: antd zhCN locale, 所有页面文本汉化
- 数据存储在 Config singleton, 运行时更新
2026-08-10 16:24:45 +08:00
admin 409e5d314e Go边缘代理: 单文件可执行+嵌入式MediaMTX+监控+注册
- edge-agent/: Go项目, 支持Windows托盘/Linux无头两种模式
- 使用go:embed将MediaMTX二进制嵌入单一可执行文件
- 首次运行自动释放MediaMTX并生成边缘节点配置
- 自动注册到origin (POST /api/nodes/register)
- 10秒心跳上报CPU/RAM/带宽/连接数 (gopsutil采集)
- Windows: getlantern/systray托盘菜单(状态/CPU/MEM/带宽/退出)
- Linux: headless模式,适合systemd守护
- build.sh/build.ps1: 交叉编译脚本(Linux/Windows)
- 更新start.sh/品牌名StreamCore
2026-08-10 14:54:03 +08:00
admin 980b91f41f 负载均衡重构: 子网感知选择 + 节点健康监控 + 节点注册上报API
- 新增 subnet-aware 负载策略: 根据客户端IP匹配最近/16子网节点
- 节点注册端点 (POST /api/nodes/register): 边缘节点自动注册
- 节点心跳端点 (POST /api/nodes/heartbeat): 上报CPU/RAM/带宽/连接数
- 健康汇总 (GET /api/nodes/health): 服务端汇总所有节点状态
- 配置节点新增subnet/is_origin字段
- 前端NodeManager: 实时健康状态/CPU/RAM进度条/带宽/连接数
- X-Forwarded-For/X-Real-IP透传支持客户端IP
2026-08-10 14:45:28 +08:00
admin 5f19fc6aa5 品牌化重构: StreamCore + 现代UI主题 + 英文界面
- 移除所有emoji (HomePage中的🎬改为VideoCameraOutlined图标)
- 主题重设计: 深蓝色系主色(#1E3A5F),系统sans-serif字体
  白色/浅灰背景,现代卡片阴影和圆角,完全去除复古感
- 首页移除管理入口,纯观众视角(渐变横幅+卡片列表)
- 统一品牌名称 StreamCore (替换LiveStream)
- 全部界面英文(antd默认locale,去除zhCN)
- 登录页渐变+卡片居中布局
2026-08-10 14:40:58 +08:00
admin b81a7bf293 全局整改: 布局重构、鉴权加固、API修复
- 首页/播放页: 去掉 antd Layout 改用 div 避免 flex 布局问题
- 播放页: 返回按钮改为 navigate('/') 替代 window.close()
- 后端登录/验证: 改用 POST JSON body 替代 query param
- 后端鉴权: require_auth 依赖注入保护所有管理接口
- nodes路由: 修复 /strategy 被 /{node_id} 拦截的顺序问题
- 前端 Settings: 策略更新改为 JSON body
2026-08-07 21:22:52 +08:00
admin f3b2a9cb46 修复 HomePage import 路径 2026-08-07 21:05:11 +08:00
admin 891740e0db 修复首页 / 自动跳转 admin;新增公开 HomePage 2026-08-07 21:04:54 +08:00
admin b96323eb99 全站汉化 + antd zhCN locale 2026-08-07 15:04:19 +08:00
admin 2e4594fa77 Initial commit: LiveStream Platform
Backend: FastAPI + SQLAlchemy + SQLite
Frontend: React + Vite + Ant Design (serif Anthropic theme)
Media server: MediaMTX with edge node support
Load balancing: round-robin / weighted / least-conn
Multicast detection with automatic fallback
Transcoding support via FFmpeg (configurable quality)
hls.js player with line & quality switching
Admin panel with password auth
Windows compatible scripts
2026-08-07 15:02:07 +08:00