免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

OpenReel 桌面端 GPU 云任务(Desktop GPU Cloud Jobs)架构设计与落地解析

OpenReel 桌面端 GPU 云任务(Desktop GPU Cloud Jobs)架构设计与落地解析 OpenReel 桌面端 GPU 云任务Desktop GPU Cloud Jobs架构设计与落地解析【免费下载链接】openreel-videoOpenReel Video - Professional browser-based video editor. Open source CapCut alternative. 100% browser-based, no installation, no cloud uploads, no watermarks.项目地址: https://gitcode.com/GitHub_Trending/op/openreel-video导读本文基于 OpenReel Video 仓库中的设计文档 docs/superpowers/specs/2026-06-02-desktop-gpu-cloud-jobs-design.md 展开完整讲解其设计动机、认证模型、主进程 GPU 客户端、渲染进程轮询与 AI 面板的整体方案。OpenReel Video 是一款 100% 浏览器化、无需安装与上传云端的开源视频编辑器而本文要解决的问题是让桌面端Electron应用与 iOS/Android 一样能够使用 GPU/云渲染服务器ai.openreel.video执行超分upscale、抠像matting、重构图reframe、防抖stabilization、转写transcription、音乐生成music-gen等 AI 任务并跟踪进度、把结果拉回工程。读完本文你将掌握桌面端如何绕过 CORS 获取 GPU JWT、如何设计 token 缓存与单飞single-flight刷新、如何通过 IPC 暴露window.openreel.gpu.*能力以及渲染进程轮询与产物回灌的完整数据流。1. 背景与目标补上桌面端缺失的一环移动端iOS/Android应用已经可以正常调用 GPU/云渲染服务器提交 AI 任务桌面端却有一个关键缺口缺少桌面设备认证device-attestation原语因此无法像移动端一样换取 GPU JWT。本文的设计目标正是补齐这个缺口让桌面安装也能换取 GPU JWT—— 新增 Cloudflare Worker/auth/token的plat:desktop支路签发真实的短期600 秒ES256 JWT建立云任务客户端 完整 AI 面板 UI—— 在编辑器中提供提交、跟踪、结果回灌的完整链路。设计文档明确记录了与用户确认过的两项决策认证模型在现有 Cloudflare Worker/auth/token中新增plat:desktop分支基于持久化的安装 UUID 签发短期600sES256 JWT仅受现有单次挑战single-use challenge 每实例/IP 限流约束不做设备认证——滥用行为是被限流而不是被阻止。GPU worker无需任何改动它忽略plat字段设计文档引用infra/gpu-worker/core/auth.py:100验证过这一点。范围管道token provider cloud-job client加上完整的 AI 面板暴露各类任务。2. 为什么客户端必须放在 Electron 主进程桌面渲染进程加载的是apps/web打包产物app://协议。如果渲染进程直接fetchai.openreel.video、Worker 或 R2 预签名 URL都属于跨源请求需要在三个服务器上为app://开放 CORS 白名单原生移动应用不存在 CORS 问题。因此设计上把 GPU/broker/R2/artifact 的所有网络调用全部路由到主进程Nodefetch无 CORS、无 preflight彻底绕开该问题同时与既有 Phase 4cloud.fetch的先例保持一致。主进程还天然拥有OS 密钥链keychain持久化安装 ID / 缓存 token原生文件系统readFileBytes、tempFilePath、流式writeChunk对渲染进程重载的鲁棒性主进程状态不会因页面 reload 丢失。结论一句话概括所有网络与 token 逻辑跑在apps/desktop主进程渲染进程只负责驱动 UX并通过 IPC 调用window.openreel.gpu.*。3. 总体架构设计文档给出了清晰的架构图┌── apps/web renderer (app://) ───────────────────────────────────────┐ │ AI Panel UI ── gpu-job-store (persisted) ── useGpuJobPoller (1×) │ │ │ │ │ │ │ └──────── window.openreel.gpu.* (IPC, contextBridge) ────────┘ │ ┌── apps/desktop main (Node) ──┴──────────────────────────────────────┐ │ GpuTokenProvider ──(challenge→token, cacherefresh)──► Worker /auth │ │ GpuJobClient: presign→PUT(R2)→submit→status→manifest→artifact │ │ uses native fs for source bytes artifact temp files │ └──────────────────────────────────────────────────────────────────────┘ │ Bearer plat:desktop JWT X-Bundle-ID: com.openreel.video ▼ ai.openreel.video (GPU worker — UNCHANGED) ▲ openreel-cloud Worker (NEW desktop leg in /auth/token)三层分工明确渲染进程负责 UI 与轮询驱动主进程负责 token 换取与所有网络调用Worker 负责签发 token新增 desktop 支路GPU worker 保持不动。设计文档规划共享的线上协议类型放在packages/core/src/ai/cloud-job-types.ts渲染进程和桌面客户端都可以引用若跨包引用不便则按文档所述在apps/desktop中复制该小型类型文件与cloud.ts的DIRECT_CONFIG处理方式一致。4. 组件 1Workerplat:desktop认证支路这是一条仅挑战challenge-only的支路客户端先获取 challenge再在没有证明attestation proof的情况下将其交换为 token。它复用了平台分发前已有的挑战单次使用single-use、限流与吊销机制。4.1 类型拓宽5 处设计文档要求把ios|android拓宽为ios|android|desktop共 5 处位置内容apps/cloud/src/auth/routes.ts:63/challenge平台校验守卫apps/cloud/src/auth/kv.ts:23ChallengeRecord.platformapps/cloud/src/auth/jwt.ts:9JobTokenClaims.platapps/cloud/src/auth/jwt.ts:37mintJobToken的params.platformapps/cloud/src/auth/routes.ts:407mintAndReturn的platform参数4.2 新分发逻辑与处理器在/token中android 分支之后、invalid_platform返回之前约routes.ts:217插入if (body.platform desktop) return handleDesktopToken(c, kv, challengeId, challengeRecord);handleDesktopToken的核心逻辑文档原样const consumed await consumeChallenge(kv, challengeId); if (!consumed) return c.json({ error: challenge_expired_or_used }, 400); return mintAndReturn(c, desktop, challengeRecord.instanceId);注意两点无 proof 字段instanceId来自存储的 challenge 记录不重新发送——这避免了客户端伪造 instanceId 的可能。4.3 外部请求/响应契约POST /auth/challenge{ platform:desktop, instanceId }→{ challengeId, challenge }POST /auth/token{ platform:desktop, challengeId }→{ token, exp }JWT 特征为ES256、issopenreel-cloud、audgpu、scopegpu:submit、platdesktop、subsha256(instance:instanceId)、TTL 600 秒POST /auth/upload-url不变已平台无关只读取sub该支路不需要新增绑定仅复用AUTH_KV和AUTH_SIGNING_JWK。4.4 测试与加固预留设计文档规划的测试apps/cloud/src/auth/auth.test.ts镜像 ios 分支但去掉 crypto 部分baseEnv()createAuthApp()先/challenge再以{platform:desktop}调/token断言 200并用verifyJobToken(publicJwkFromPrivate(env.AUTH_SIGNING_JWK), token)验证platdesktop、scopegpu:submit。加固通道seam在文档中记录但未实现后续可以在handleDesktopToken内增加 proof 校验要求一个通过带外注册的桌面密钥对签名的 challenge复用storeAttest/readAttest或要求账号会话。在此之前该支路是被限流但未被认证rate-limited but unattested的。仓库现状说明在当前仓库快照中apps/下可见desktop、image、studio、web四个应用目录apps/cloudWorker 侧实现未包含在内因此上述 Worker 端改动属于设计文档规划的待实现内容需按文档第 10 节说明部署后桌面 AI 才可用。5. 组件 2桌面主进程 GPU 客户端已在仓库落地与设计文档高度一致桌面端 GPU 客户端已在 apps/desktop/src/main/gpu/ 下实现包含三个文件token-provider.ts、job-client.ts、instance-id.ts以及对应的单元测试job-client.test.ts。5.1GpuTokenProvidertoken-provider.ts实现了文档描述的全部要点安装 ID见 instance-id.ts —— 优先从密钥链getKeyStore().get/set(gpu-instance-id)读写INSTANCE_ID_KEY gpu-instance-id密钥链不可用时回退到userData目录下的openreel-gpu-instance.txt文件mode: 0o600UUID 用randomUUID()生成一次并持久化。getToken()缓存与刷新REFRESH_LEEWAY_SECONDS 60当exp - now 60s时直接返回缓存 token否则走mint()POST {broker}/auth/challenge {platform:desktop, instanceId}→POST {broker}/auth/token {platform:desktop, challengeId}→ 缓存{token, exp}。单飞single-flightinflight: Promisestring | null并发调用共享同一个mint()Promise避免重复签发。invalidate()清空缓存由 GPU host 返回 401 时调用比移动端从不自动恢复更优。统一请求头所有 broker 调用携带Content-Type: application/json、Accept: application/json、X-Bundle-ID: com.openreel.video。依赖注入设计TokenProviderDeps允许传入fetchFn与now为单元测试中的 mock 提供了入口。5.2GpuJobClientjob-client.ts完整的任务生命周期客户端核心方法如下uploadMedia({ srcPath, filename, contentType })/uploadBytes(...)POST {broker}/auth/upload-url {filename, contentType}携带 Bearer→normalizePresign归一化双形态预签名响应 → 流式/字节PUT到预签名 URL。实现细节PUT 请求不带 Bearer/X-Bundle-ID直连 R2仅携带 presign 返回的 headers缺失 Content-Type 时才用参数补充Uint8Array/ArrayBuffer字节处理避免多余拷贝有对应测试断言put[1].body.buffer bytes。normalizePresign纯函数同时兼容uploadURL|putUrl、mediaKey|objectKey、getUrl|downloadURL|downloadUrl双形态缺失必要字段时抛错。normalizePresign的别名归一化有专门测试覆盖。buildSubmitBody纯函数区分两种 submit 体 —— 无媒体时{request}其中request {kind, params}有媒体时{request, mediaKey, mediaFilename}。submitJobPOST {gpu}/jobs携带 Bearer X-Bundle-ID Accept503 时读取Retry-After头并抛出GpuRetryableError带status与retryAfterSeconds供上层做可重试处理。jobStatus(jobID)返回{ jobID, status, progress?, message?, manifestURL?, error?, queuePosition?, pendingAhead? }。fetchManifest(jobID)GET {gpu}/jobs/{id}/manifest。downloadArtifact(jobID, relativePath)GET {gpu}/jobs/{id}/artifacts/{relativePath}根据 Content-Type 取 mime、按扩展名生成临时文件路径tempFilePath依赖注入写入临时文件后返回{ tempPath, mime }—— 大文件不经过 IPC渲染进程用fs.readFileBytes读取。cancelJob(jobID)DELETE {gpu}/jobs/{id}。401 自动恢复authedFetch在首次请求遇 401 时调用tokenProvider.invalidate()并携带新 token 重试一次实现了文档On any GPU-host 401: invalidate then one retry的要求。5.3 IPC 表面window.openreel.gpuIPC 通道已定义在 apps/desktop/src/shared/channels.tsgpuUploadMedia: openreel:gpu:uploadMedia gpuUploadExport: openreel:gpu:uploadExport gpuSubmitJob: openreel:gpu:submitJob gpuJobStatus: openreel:gpu:jobStatus gpuFetchManifest: openreel:gpu:fetchManifest gpuDownloadArtifact: openreel:gpu:downloadArtifact gpuCancelJob: openreel:gpu:cancelJob对应的 zod 参数 schema 定义在 ipc-contract.ts如gpuUploadMediaArgsSchema、gpuSubmitJobArgsSchema、gpuJobIdArgsSchema、gpuArtifactArgsSchema等另有gpuUploadExportArgsSchema支持直接上传字节用于导出产物场景。结合apps/web/src/types/global.d.ts与packages/core的 bridge slice渲染进程得到类型安全的window.openreel.gpu.*调用面。轮询留在渲染进程离散的jobStatus调用镜像既有的useKieAIPoller主进程不维护长生命周期每任务循环避免渲染进程重载后产生孤儿循环。5.4 桌面端测试apps/desktop/src/main/gpu/job-client.test.ts 使用 vitest 注入 mockfetchFn的纯单元测试方式覆盖了可选 download URL 别名归一化、uploadBytes零拷贝字节传递断言 PUT body 的底层 buffer 与输入一致。设计文档还规划了 token 缓存/刷新/单飞、三种 submit 体变体、401→invalidate→retry、503 可重试等测试场景。6. 组件 3渲染进程的云任务类型、store 与 poller6.1 线上类型packages/core/src/ai/cloud-job-types.ts从移动端目录移植、与线上线协议字符串逐字对齐的类型已经落地AI_CLOUD_JOB_KINDS常量映射共25 种任务snake_case 线上值如aiHighlight: ai_highlight含 transcription、ai_highlight、auto_captions、person_matting、object_tracking、face_analysis、stabilization、auto_reframe、audio_separation、color_match、colorize、upscale、scene_detection、background_removal、music_generation、photo_enhance、portrait_bokeh、smart_thumbnail、denoise、silence_removal、frame_interpolation、face_restore、object_removal、voice_enhance、translation。AiCloudJobStatusqueued|uploading|processing|completed|failed|cancelledTERMINAL_STATUSES集合与isTerminalStatus()判定终结态。MEDIA_OPTIONAL_KINDS{music_generation, translation}—— 仅这两种任务可以不传媒体其余全部要求mediaKey与 workermain.py:105/387的校验一致。请求/响应结构AiCloudJobRequest {kind; params}、AiCloudJobCreated、AiCloudJobStatusResponse含可选的queuePosition/pendingAhead、AiWorkerArtifactReference、AiWorkerResultManifest { jobID; kind; status?; model?; artifacts; metadata? }。产物类型判定artifactIsImage/artifactIsVideo/artifactIsAudio合并了 iOSAndroid 两套扩展名集合图片含 jpg/jpeg/png/heic/heif/webp/tiff/tif/bmp视频含 mp4/mov/m4v/webm音频含 wav/m4a/mp3/aac/ogg支持按type字段或扩展名双通道判定还有normalizeResultManifest归一化原始 manifest。6.2gpu-job-storeapps/web/src/stores/gpu-job-store.ts镜像kieai-store持久化keygpu-pending-jobsPendingGpuJob { jobID; mediaId; projectId; kind; suggestedName; createdAt; retries; failed }动作含addJob/removeJob/incrementRetry/markFailed/retryJob/getJobsForProject与 KieAI 相同采用 3 天过期策略。对应测试见 gpu-job-store.test.ts。6.3useGpuJobPollerapps/web/src/hooks/useGpuJobPoller.ts渲染进程唯一的轮询挂载点在App.tsx挂载一次桌面端仅在window.openreel?.platformdesktop时启用。实现细节与文档描述完全对应递归setTimeoutPOLL_BASE_MS 20002 秒基础间隔in-flight/timer 守卫timersRef与inFlightRef防止重复轮询每次 tick 从 store 重新读取任务状态避免 stale closure瞬时错误重试MAX_RETRIES 5对 HTTP 5xx/408/429 与网络错误采用指数退避gpuBackoffMs封顶 15 秒整体 30 分钟上限MAX_AGE_MS 30 * 60 * 1000超时后markFailed并标记媒体资产错误状态因为 worker 没有 408这个上限由客户端强制completed处理链fetchManifest→ 按 kind 判定输出outputForKindisMultiAssetKind处理音频 stems 等多资产场景→downloadArtifact→readFileBytes(tempPath)→ 回灌导入failed/cancelledmarkFailed并设置资产的 pending/error 标志。值得注意的实现细节poller 抽象了桌面端与 Web 端两套 IOisDesktopGpuAvailable()判断桌面端走window.openreel.gpu.*Web 端走getWebGpuClient()同一套轮询逻辑可复用。围绕 poller 与提交链路的配套服务包括 gpu-clip-submit.ts、gpu-result-import.ts、gpu-data-import.ts均有对应测试。7. 组件 4媒体进出与 AI 面板 UI7.1 源字节与结果回灌packages/core/src/media输入选中片段 →clip.mediaId→getMediaItem→blobreload 后为空则用loadMediaBlob重新水合→ 桌面端通过native-media-bridge.ts导出的materializeToTemp物化为临时文件 → 把srcPath传给gpu.uploadMedia。输出downloadArtifact得到临时路径 →readFileBytes→Blob。图片结果复用replacePlaceholderMedia视频/音频结果走通用importMedia(file)路径需要把目前硬编码type:image的replacePlaceholderMedia泛化或非图片结果改走importMedia。MediaItem.type保持video|audio|image不变。7.2 AI 面板新增顶级面板ui-store的PanelId新增ai加DEFAULT_PANELS条目在EditorInterface.tsx中按panels.ai.visible渲染网格区域与audioMixer的实现镜像工具栏切换仅桌面端可见。实现文件位于 apps/web/src/components/editor/ai-panel/。面板内容分为任务目录与提交流两部分任务目录分组每类含一个小型参数表单分组任务 kindsEnhance/Restore增强/修复upscale、denoise、face_restore、photo_enhance、colorizeCut-out抠图background_removal、person_matting、object_removalMotion运动stabilization、auto_reframe、frame_interpolationAnalyze分析transcription、auto_captions、scene_detection、face_analysis、object_tracking、smart_thumbnail、ai_highlightAudio音频audio_separation、voice_enhance、silence_removalGenerate生成music_generation、translation、color_match、portrait_bokeh大部分任务参数很少context从选中片段填充projectID/clipID/mediaID/renderSize/sourceDuration/sourceFrameRate/quality按 worker 的 hoist 优先级放入params.context发送。提交流校验输入除非 kind ∈MEDIA_OPTIONAL_KINDS否则必须选中片段→addPlaceholderMediapending 占位→uploadMedia如有媒体→submitJob→gpu-job-store.addJob。进度/错误在资产上展示复用isPending/kieaiError或并行的gpuError标志 面板内任务列表AIJobList.tsx。结果成为新的媒体项用户可拖入时间线addClip/addClipToNewTrack。v1 的参数表单刻意保持最小kind context 少量按任务选项面板按 kind 可扩展。8. 错误处理与边界情况设计文档系统性地枚举了错误处理策略与源码实现一一对应Token单飞签发60 秒刷新余量REFRESH_LEEWAY_SECONDS401 →invalidate() 重试一次broker 429 → 提示rate limited, retry shortly。Submit 503 Retry-Afterpoller/submit 遵循Retry-After指数退避GpuRetryableError携带retryAfterSeconds。Worker 无 40830 分钟上限由客户端强制poller 的MAX_AGE_MS超时标记为 failed/timed-out。输入在任务后被删worker 会删除已上传的mediaKey提交后绝不重新引用。Artifacts TTL 24h worker 内存态重启后轮询 404已知任务遇 404 视为 failed/expired完成后应尽快下载。mediaKey 前缀必须以预签名签发的jobs/…前缀开头始终原样使用 presign 返回的 keynormalizePresign保证这一点。150MB 视频v1 可能跳过移动端 HEVC 预压缩记为后续项原生 sidecartranscode可在将来提供该能力。9. 代码组织与共享Wire 类型packages/core/src/ai/cloud-job-types.ts 为唯一来源桌面主进程客户端若构建允许则通过 workspace 包导入否则在apps/desktop复制该小型类型文件文档化的复制策略同cloud.ts的DIRECT_CONFIG。桌面客户端apps/desktop/src/main/gpu/{token-provider,job-client,instance-id}.ts IPC 接线channels.ts/ipc-contract.ts/main/index.ts/preload。渲染进程apps/web/src/stores/gpu-job-store.ts、apps/web/src/hooks/useGpuJobPoller.ts、apps/web/src/components/editor/ai-panel/*外加ui-store/EditorInterface/global.d.ts的编辑和packages/corebridge slice 对fs/gpu新方法的扩展。10. 待确认事项与部署前提设计文档明确列出构建前/中需确认的开放项Broker 生产主机iOS 用https://api.openreel.videoweb 端api-endpoints.ts用https://openreel-cloud.niiyeboah1996.workers.dev需要选定一个作为OPENREEL_AUTH_BROKER_BASE_URL的默认值大概率同一个 Worker。无需 CORS所有调用经主进程设计上已确认。部署前提Workerapps/cloud改动必须部署后桌面 token 才能签发在此之前桌面 AI 不可用。GPU worker 无需改动。150MB 预压缩推迟到后续项。11. 分阶段实施计划设计文档为落地计划给出了清晰的阶段划分Phase A — Worker desktop legapps/cloud拓宽 5 处类型、handleDesktopToken、分发逻辑、单元测试。可独立发布与部署。Phase B — Desktop 主进程 GPU 客户端apps/desktoptoken provider、job client、IPC、preload、类型mock fetch 的单元测试。Phase C — 渲染进程类型 store pollerpackages/coreapps/webwire 类型、gpu-job-store、useGpuJobPoller、媒体进出辅助单元测试。Phase D — AI 面板 UIapps/web顶级面板、任务目录、提交/进度/结果进时间线。可行处做组件测试完整端到端渲染需要已部署的 Worker 真实桌面运行人工验证。12. 范围外内容以下内容明确不在本次设计范围内服务端账号/登录账号背书的 broker 支路、移动端改动、150MB HEVC 预压缩、超出最小集的按任务高级参数 UI、以及任何 GPU-worker 代码改动。总结从 设计文档 到仓库实现OpenReel 桌面端 GPU 云任务的方案脉络非常清晰——Worker 端只做一次类型拓宽 新分发的轻量改造GPU worker 完全不动桌面主进程通过GpuTokenProvider的 challenge→token、60 秒余量缓存、单飞与 401 自动恢复以及GpuJobClient的 presign→PUT→submit→status→manifest→artifact 全生命周期管理把跨源问题彻底消化在主进程内渲染进程则用持久化 store 单实例 poller AI 面板完成 UX 闭环。该设计既有详实实操又有源码级实现支撑是理解 OpenReel 桌面端 AI 能力架构的入口文档。【免费下载链接】openreel-videoOpenReel Video - Professional browser-based video editor. Open source CapCut alternative. 100% browser-based, no installation, no cloud uploads, no watermarks.项目地址: https://gitcode.com/GitHub_Trending/op/openreel-video创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表