{"id":51193197,"url":"https://github.com/bitoceango/civideo","last_synced_at":"2026-07-16T03:00:51.530Z","repository":{"id":364490289,"uuid":"1268096608","full_name":"bitoceango/civideo","owner":"bitoceango","description":"家长可控的自建儿童视频流媒体播放器：无广告、无推荐流、封闭内容花园 · Self-hosted, ad-free kids video streaming player with parental controls — Cloudflare Workers + R2 and native SwiftUI (iOS / iPadOS / macOS)","archived":false,"fork":false,"pushed_at":"2026-06-22T06:22:37.000Z","size":4070,"stargazers_count":56,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-22T07:23:04.854Z","etag":null,"topics":["ad-free","avplayer","children","cloudflare","cloudflare-r2","cloudflare-workers","education","family","ios","ipados","kids","macos","media-server","parental-controls","screen-time","self-hosted","swift","swiftui","video-player","video-streaming"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bitoceango.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-13T06:18:44.000Z","updated_at":"2026-06-22T05:31:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bitoceango/civideo","commit_stats":null,"previous_names":["bitoceango/civideo"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bitoceango/civideo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitoceango%2Fcivideo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitoceango%2Fcivideo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitoceango%2Fcivideo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitoceango%2Fcivideo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitoceango","download_url":"https://codeload.github.com/bitoceango/civideo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitoceango%2Fcivideo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35528483,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-16T02:00:06.687Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ad-free","avplayer","children","cloudflare","cloudflare-r2","cloudflare-workers","education","family","ios","ipados","kids","macos","media-server","parental-controls","screen-time","self-hosted","swift","swiftui","video-player","video-streaming"],"created_at":"2026-06-27T18:00:22.931Z","updated_at":"2026-07-16T03:00:51.524Z","avatar_url":"https://github.com/bitoceango.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# child-video — 自建儿童视频流媒体 + 听书\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/intro.png\" alt=\"child-video — 一个封闭、专注、零打扰的家庭自建儿童视频/听书流媒体（五端原生 App + Serverless 后端）\" width=\"920\"\u003e\n\u003c/p\u003e\n\n一套**家庭自用**的私有儿童视频流媒体：家长把视频上传到 Cloudflare R2，孩子在原生 App（**iPhone / iPad / Mac / Windows / Android**）里**看视频 + 听书**。家长可在桌面端（Windows/macOS）开发者模式里**自助上传本地视频或从网址（YouTube/Bilibili）下载入库**。\n\n\u003e **English** — *child-video* is a self-hosted, ad-free **kids' video + audiobook** streaming player with parental controls. Parents upload to their own **Cloudflare R2**; kids watch in a closed player — no recommendations, ads, comments, or external links. Native apps for **iOS / iPadOS / macOS** (SwiftUI) and **Windows / Android** (Tauri); serverless backend on **Cloudflare Workers + R2 + D1**. Runs at roughly **$0** on the free tier.\n\n**为什么自己建**：商业视频平台充斥推荐流、广告和不适合孩子的内容，孩子很容易被无关视频吸引、无法专注。这个项目是一个**封闭内容花园**——孩子端没有外部搜索、没有外部入口、没有推荐算法、没有弹幕/社交，**只能看到家长放进去的内容**。\n\n\u003e 月成本只有 R2 存储费（起步在免费额度内 = $0，约 500GB ≈ $7/月），出口流量永久免费，**无任何年费**（孩子端用 SideStore 免费侧载，不交 Apple Developer $99/年）。\n\n## 架构一览\n\n```\n家长下载视频 ──cpv 上传──▶ Cloudflare R2 (私有桶)\n                              │  manifest.json = 播放列表单一事实源\n                              ▼\n                    Cloudflare Worker (鉴权流媒体网关 + 边缘缓存 + D1 进度/规则)\n                              │  绑定自有域名（绕开被墙的 *.workers.dev）\n              ┌───────────────┼───────────────┐\n              ▼               ▼               ▼\n   SwiftUI App           Tauri App        Tauri App\n   (iPhone/iPad/Mac)     (Windows)        (Android)\n   AVPlayer              WebView2         WebView\n        └────── 共用：首页/分类/听书/我的 + 商业级播放器 ──────┘\n\n家长桌面端（Windows/macOS）开发者模式：捆绑 yt-dlp+ffmpeg，本地处理 →\n预签名直传 R2 → 写 manifest（密钥只在 Worker，App 内不存）。听书：cpv 调\n豆包 seed-tts-2.0 把电子书转成音频，同一套 R2/Worker/App 链路。\n```\n\n\u003e **两套前端代码**：Apple 端 SwiftUI（`app/`）；Windows + Android 共用一套 Web UI（`windows/src`，Tauri v2 分别出安装包/APK）。功能\"三端同时实现\"为硬性规则（见 `CLAUDE.md`）。\n\n- **存储**：R2 按字节计费 + 出口免费（明确否决按时长计费的 Cloudflare Stream）。\n- **不转码**：互联网视频多为 H.264/AAC，`cpv` 用 ffprobe 探测，只在必要时重封装/转码。\n- **播放**：MP4 渐进式 + HTTP Range，系统 AVPlayer 原生支持，零第三方播放内核。\n- **边缘缓存**：Worker 用免费 Cache API（按 R2 etag 作键，覆盖视频自动失效），降起播延迟。\n- **封闭花园**：孩子端无外部内容入口；家长 PIN + 每日时长上限 + 允许时段 + 护眼提醒。\n\n完整设计见 [`docs/architecture.md`](docs/architecture.md)。\n\n## 下载 App\n\n预编译安装包见 **[Releases](../../releases)**（你需先自部署后端，首次启动填自己的 Worker 地址 + 激活密钥）：\n\n| 平台 | 产物 | 安装 |\n|---|---|---|\n| **Windows** | `childvideo-windows-x64-setup.exe` | 双击装。未签名 → SmartScreen 点「更多信息 → 仍要运行」 |\n| **macOS** | `儿童视频_*_aarch64.dmg` | 拖入 Applications。未签名/未公证 → 右键「打开」或 `xattr -dr com.apple.quarantine` |\n| **Android** | `app-universal-debug.apk` | 手机开「允许安装未知来源」后装（debug 通用包，体积偏大） |\n| **iOS** | `*-unsigned.ipa` | ⚠️ 不能直接装，需 SideStore/AltStore 自签侧载 |\n\n\u003e Windows/macOS 含**家长上传**（开发者模式）；Android 为纯消费端（看视频+听书）。\n\n## 功能\n\n**孩子端 App（三大模块）**\n- 🏠 **首页**：学科金刚区 + 继续观看 + 我喜欢的 + 系列聚合（横排预览，多集进竖向网格）。自动刷新，新上传的视频自动出现。\n- 📚 **分类**：按「学科/能力」（科学/英语/数理/国学/艺术…）二级浏览，无热度榜。\n- 🎧 **听书**：电子书经豆包 seed-tts-2.0 转成音频，按章收听；独立书架（继续收听/分类/网格）+ 音频播放器（封面+章节+进度，无屏幕，护眼）。\n- 👤 **我的**：家长 PIN 门 → 每日时长 / 允许时段 / 护眼提醒 / 学习报告（今日·本周）/ 设备管理。\n\n**播放器（对齐商业流媒体）**\n- 播放/暂停、进度拖动 + 缓冲、±10 秒、倍速（0.75–1.5，儿童克制档）、播完重播\n- 音量滑块 + 静音、CC 字幕开关（内嵌字幕轨）、选集面板、上一集/下一集、同系列自动连播、断点续播、收藏\n- AirPlay 投屏、Now Playing 锁屏/控制中心/耳机线控\n- 防误触锁定（长按解锁）、护眼休息提醒\n\n**家长端（桌面 Windows/macOS，开发者模式·家长 PIN 门后）**\n- 📤 **本地上传**：选单个 / **多选** / **整个文件夹**视频，自动截封面+读时长，预签名直传 R2 入库。\n- ⬇ **网址下载**：粘贴 **YouTube / Bilibili 等**链接，捆绑的 yt-dlp 在本机下载（自动用浏览器登录态绕 B 站风控），合并成 H.264/AAC mp4 入库。\n- 安全：R2 密钥只在 Worker，App 走预签名、内不存密钥；入口藏在家长 PIN 门后，孩子端无感。\n\n\u003e 有意**不做**（违背封闭专注理念）：算法推荐流、弹幕、评论/社交、热度榜、外链、电商/会员、孩子端 UGC 上传。\n\n## 仓库结构\n\n| 目录 | 内容 |\n|---|---|\n| `cli/` | 上传 CLI `cpv`（Node + S3 SDK）：探测/转码/上传 R2/维护 manifest。专为 AI 调用设计。含 `import-youtube.sh` 一键导入 |\n| `worker/` | Cloudflare Worker：激活、媒体网关（Range + 边缘缓存）、进度、家长规则 + D1 schema |\n| `app/` | SwiftUI 多平台 App（iOS 17+ / macOS 14+，xcodegen 工程），含图标生成脚本 |\n| `windows/` | Tauri v2 桌面 App（Windows + macOS 共用）：`src/` 是 Web UI（首页/分类/听书/我的+播放器+家长上传），`src-tauri/` 是 Rust 壳；CI 出 NSIS 安装包 / macOS .dmg |\n| `android/` | Tauri v2 Android 工程：复用 `windows/src` 的 Web UI，CI 出 APK（消费端：视频+听书） |\n| `tests/` | `e2e-backend.sh` 端到端后端验收测试（curl 断言，带退出码） |\n| `docs/` | 架构设计 + `requirements/`（需求文档：PRD/Epic/Story、验收与测试计划） |\n| `designs/` | 孩子端 UI 设计原型（可在浏览器打开的 HTML） |\n\n## ⚠️ 开发流程（硬性规则）\n\n任何新需求/功能/改动，**必须先有需求文档（`docs/requirements/`，含 Epic 与 Story）→ 按 Story 建 GitHub issue → 再开发 → 提交关联 issue**。详见 [`CONTRIBUTING.md`](CONTRIBUTING.md)。没有需求文档和 issue，不写代码。\n\n## 快速开始\n\n\u003e 私密部署参数（域名 / Account ID / 数据库 ID 等）放在 `deploy.local.json`（已 gitignore，不进仓库）。下面用占位符。\n\n### 0. 必需的人工步骤（无法自动化，先做好这些）\n\n`npm run setup` 和 CI 能自动建桶/建库/迁移/部署，但下面这些**必须你手动完成**：\n\n| 步骤 | 说明 |\n|---|---|\n| **注册 Cloudflare 账号** | 免费：[dash.cloudflare.com](https://dash.cloudflare.com) |\n| **`npx wrangler login`** | 浏览器授权 wrangler（首次部署前必需） |\n| **建 R2 API Token**（给 CLI 上传用） | 控制台 → R2 → *Manage R2 API Tokens* → 建 S3 凭证，填进 `~/.zshrc` 环境变量（见 [`cli/README.md`](cli/README.md)）。⚠️ wrangler 部署**不**需要它，但 CLI 上传视频需要 |\n| **（可选）自定义域名** | 把域名托管到 Cloudflare；不绑就用免费 `*.workers.dev`（中国大陆可能被墙） |\n| **iOS 设备分发** | 见 §3：免费 Apple ID 自建（证书 7 天）或 SideStore 自动续签侧载 |\n| **（可选）开启 CI 自动部署** | 仓库 Settings → Actions 配 Secret `CLOUDFLARE_API_TOKEN` + Variables `CF_ACCOUNT_ID`/`D1_DATABASE_ID`/`CF_DOMAIN`，之后合并到 main 自动部署 worker |\n\n### 1. 后端（Cloudflare）\n\n先 `npx wrangler login`（浏览器登录，首次必需）。然后**一键部署**（推荐）——自动建 R2 桶 / 建 D1 并写回 id / 迁移表 / 设密钥 / 部署，只问你域名和密钥：\n\n```bash\ncd worker\nnpm install\nnpm run setup       # 默认免费 *.workers.dev（零域名）；过程中可选绑定自定义域名\n```\n\n\u003e 💡 `*.workers.dev` 在中国大陆可能被墙；需要稳定访问时按提示绑定自定义域名（需已托管在 Cloudflare）。\n\n\u003cdetails\u003e\u003csummary\u003e或手动逐步\u003c/summary\u003e\n\n```bash\ncd worker\nnpm install\ncp wrangler.example.jsonc wrangler.jsonc       # 默认 workers.dev；要自定义域名改这里\nnpx wrangler r2 bucket create child-video       # 建 R2 桶\nnpx wrangler d1 create child-video-db           # 建 D1，把返回的 database_id 填进 wrangler.jsonc\nnpx wrangler d1 execute child-video-db --remote --file=./schema.sql\nnpx wrangler secret put ACTIVATION_KEY          # 设备激活密钥：长随机串（openssl rand -hex 24）；不设则回退用 PARENT_PIN\nnpx wrangler secret put PARENT_PIN              # 家长门 PIN：改每日时长/允许时段规则用，可短数字\nnpx wrangler deploy\n```\n\u003c/details\u003e\n\n### 两个密钥（你自己定，部署时设置）\n\n后端有两个**完全由你自定义**的密钥（互相独立，issue #17 起分离）：\n\n| 密钥 | 是什么 | 在哪用 | 怎么设 |\n|---|---|---|---|\n| **设备激活密钥 `ACTIVATION_KEY`** | 一台设备首次\"加入\"你家库的口令 | App 激活页（家长把它给家人，在 Windows/Android/Mac/iOS 上激活设备时输入一次） | `npm run setup` 时回车=随机生成，或自己输任意值；改用 `wrangler secret put ACTIVATION_KEY`。建议长随机串 `openssl rand -hex 24` |\n| **家长门 `PARENT_PIN`** | 进 App 内「家长中心」的 PIN | 改每日时长/允许时段规则、**桌面端家长上传**时输入 | `npm run setup` 时你输入，可短数字（如 `1234`）；改用 `wrangler secret put PARENT_PIN` |\n\n\u003e 二者分工：**激活密钥**管\"哪台设备能连你的库\"；**家长 PIN**管\"谁能改规则/上传\"。`setup` 结束会打印激活密钥，妥善保存。若不设 `ACTIVATION_KEY`，激活会回退用 `PARENT_PIN`（不推荐，二者最好分开）。\n\n### 2. 上传 CLI\n\n```bash\ncd cli \u0026\u0026 npm install\n# 配置 R2 S3 凭证（见 cli/README.md），然后：\nnode src/index.js doctor --json                # 自检 ffmpeg + R2 连通性\nnode src/index.js upload video.mp4 --title \"标题\" --series \"系列名\" --category \"科学\" --json\n```\n\n需要本机 `ffmpeg`（`brew install ffmpeg`）。\n\n**从 YouTube / Bilibili 等导入视频**，三种方式任选：\n1. **桌面 App 内**（最省事）：家长中心 → 网址下载并上传，粘链接即可（自动用浏览器登录态绕 B 站风控）。\n2. **CLI 批量**（YouTube 播放列表整季）：\n   ```bash\n   brew install yt-dlp ffmpeg\n   bash cli/import-youtube.sh \"\u003c播放列表URL\u003e\" \"动物兄弟 第五季\" \"科学\"   # 720p+中文字幕 → 逐个上传\n   ```\n   \u003e `import-youtube.sh` 已内置 `env -u NODE_OPTIONS` + `--extractor-args player_client=...` 绕过常见拦截；带 `--cookies-from-browser` 可下需登录内容。\n3. **任意下载器**（如 SnapAny）下到本地 → 用桌面 App「选择本地视频上传」入库（万能兜底）。\n\n### 3. App（孩子看 + 家长传）\n\n**最省事：下预编译包** → [Releases](../../releases)（Windows `.exe` / macOS `.dmg` / Android `.apk` / iOS `.ipa`）。首次启动在激活页填你的 **Worker 域名 + 设备激活密钥**。\n\n**从源码构建：**\n\n- **Apple（iOS / macOS，SwiftUI · `app/`）**\n  ```bash\n  cd app \u0026\u0026 brew install xcodegen\n  cp Local.example.xcconfig Local.xcconfig   # 可填 CV_SERVER_HOST=你的主机名，免每次手输\n  xcodegen generate \u0026\u0026 open ChildVideo.xcodeproj\n  ```\n  Mac 直接 Run；iPhone/iPad 见 [`app/README.md`](app/README.md)（免费 Apple ID + SideStore）。\n\n- **Windows / macOS（Tauri · `windows/`，含家长上传）**\n  ```bash\n  cd windows\n  npx @tauri-apps/cli@2 build     # 本机出 .exe(Win) / .app+.dmg(Mac)；或 `dev` 开发模式跑\n  ```\n  桌面端进「我的 → 家长中心」（家长 PIN）后有 **📤 上传视频**：本地文件（单个/多选/整文件夹）+ ⬇ 网址下载（YouTube/Bilibili）。需把 `yt-dlp`/`ffmpeg` 按 target-triple 命名放进 `windows/src-tauri/binaries/`（CI 自动拉；本地手动放，见各 CI 步骤）。\n\n- **Android（Tauri · `android/`，纯消费端）**\n  复用 `windows/src` 的 Web UI，由 CI 出 arm64 APK（`.github/workflows/android-build.yml`），见 Release。\n\n\u003e 所有端**共用同一后端**；首次启动填 Worker 域名 + 设备激活密钥即可（个人 Apple 构建可用 `Local.xcconfig` 预填）。\n\n## 测试\n\n```bash\nsource ~/.zshrc\nPIN=\u003c你的PIN\u003e bash tests/e2e-backend.sh        # 端到端后端验收，退出码 0=全过\n```\n覆盖：健康检查 / 鉴权 401 / PIN 403 / 激活 / 拉库 / 媒体 Range 206 / 边缘缓存 HIT / 进度读写 / 家长规则 / 超时段·超时长拦截 / 本周报告。验收清单见 [`docs/requirements/002-acceptance-e2e.md`](docs/requirements/002-acceptance-e2e.md)。\n\n## 隐私与安全\n\n- R2 桶保持**私有**，所有媒体请求经 Worker 校验设备令牌后才返回字节，R2 永不公开直连。\n- 家长 PIN 是 Worker secret，R2 密钥是本机环境变量，孩子姓名等**都不在仓库里**（放本地 `deploy.local.json`）。\n- **桌面端家长上传不内置任何长期密钥**：R2 写权限只在 Worker；App 向 Worker（家长 PIN 校验）换**短时效预签名 PUT URL** 直传 R2，密钥永不进 App/安装包（开源可拆包也安全）。捆绑的 yt-dlp 读浏览器 cookies 仅在本机使用、不上传。\n- **激活防爆破**（issue #16）：`/api/activate` 内置限流——同一 IP 连续失败 5 次即锁定 15 分钟（返回 `429`），计数持久化在 D1；可用环境变量 `MAX_ACTIVATION_FAILS` / `ACTIVATION_LOCK_MIN` 调整。⚠️ 自定义域名会进公开的 Certificate Transparency 日志（**域名不是秘密**），安全只靠 **PIN 强度 + 限流**，请设强 PIN。（升级老部署需重跑 `schema.sql` 以建 `activation_attempts` 表。）\n- **零代码加固**（可选，推荐叠加）：Cloudflare 控制台 → Security → WAF → Rate limiting rules，对表达式 `http.request.uri.path eq \"/api/activate\"` 设「每 IP 每分钟 ≤ 5 次，超出 Block」。\n- R2 出口免费 = 没有带宽账单可被「盗刷」。\n- 仅供家庭内部私密访问，请勿公开分享链接或二次分发受版权保护的内容。\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitoceango%2Fcivideo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitoceango%2Fcivideo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitoceango%2Fcivideo/lists"}