feat: add transcript download endpoint to developer REST API (#1656) - #2092
Conversation
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
| return c.json({ | ||
| data: { | ||
| id: video.id, | ||
| transcriptionStatus: video.transcriptionStatus, | ||
| s3Key: video.s3Key, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Transcript response returns video key
When transcription is complete, this endpoint returns developerVideos.s3Key, which identifies the raw uploaded video rather than the separately stored transcription.vtt object. Because the response includes neither transcript content nor a signed transcript URL, API consumers cannot download the completed transcript.
Knowledge Base Used: Infra, Storage and Config
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/developer/v1/[...route]/videos.ts
Line: 158-164
Comment:
**Transcript response returns video key**
When transcription is complete, this endpoint returns `developerVideos.s3Key`, which identifies the raw uploaded video rather than the separately stored `transcription.vtt` object. Because the response includes neither transcript content nor a signed transcript URL, API consumers cannot download the completed transcript.
**Knowledge Base Used:** [Infra, Storage and Config](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/infra-storage-config.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| if (!video.public && video.ownerId !== user?.id) { | ||
| return Response.json({ error: "Unauthorized" }, { status: 401 }); | ||
| } |
There was a problem hiding this comment.
Public flag bypasses password policy
When a public video is password-protected, this direct public check grants unauthenticated access without invoking VideosPolicy.canView, exposing its AI title, summary, and chapters without the configured password. How this was verified: The canonical view policy verifies passwords for public videos, while this handler returns metadata after checking only video.public and ownership.
Knowledge Base Used: Web App (apps/web)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 60-62
Comment:
**Public flag bypasses password policy**
When a public video is password-protected, this direct `public` check grants unauthenticated access without invoking `VideosPolicy.canView`, exposing its AI title, summary, and chapters without the configured password. **How this was verified:** The canonical view policy verifies passwords for public videos, while this handler returns metadata after checking only `video.public` and ownership.
**Knowledge Base Used:** [Web App (apps/web)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/web-app.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| let video_id = if url_or_id.contains('/') { | ||
| url_or_id | ||
| .rsplit('/') | ||
| .next() | ||
| .unwrap_or(&url_or_id) | ||
| .to_string() | ||
| } else { | ||
| url_or_id | ||
| }; | ||
|
|
||
| let server_url = std::env::var("CAP_SERVER_URL") | ||
| .unwrap_or_else(|_| "https://cap.so".to_string()); | ||
|
|
||
| let endpoint = format!("{}/api/video/metadata?videoId={}", server_url.trim_end_matches('/'), video_id); |
There was a problem hiding this comment.
When a valid share URL has a trailing slash or query parameters, rsplit('/').next() produces an empty ID or retains the query string in the ID. The metadata request then receives an invalid videoId and returns 400 or 404 for an otherwise valid video.
Knowledge Base Used: Cap CLI (apps/cli)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/cli/src/recordings.rs
Line: 111-124
Comment:
**Share URL parsing mangles IDs**
When a valid share URL has a trailing slash or query parameters, `rsplit('/').next()` produces an empty ID or retains the query string in the ID. The metadata request then receives an invalid `videoId` and returns 400 or 404 for an otherwise valid video.
**Knowledge Base Used:** [Cap CLI (`apps/cli`)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/cli.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.|
|
||
| export async function GET(request: NextRequest) { | ||
| const { searchParams } = new URL(request.url); | ||
| const videoId = searchParams.get("videoId"); | ||
|
|
||
| if (!videoId) { | ||
| return Response.json({ error: "Missing videoId parameter" }, { status: 400 }); | ||
| } | ||
|
|
||
| const query = await db().select().from(videos).where(eq(videos.id, videoId)); | ||
|
|
||
| if (query.length === 0 || !query[0]) { | ||
| return Response.json({ error: "Video not found" }, { status: 404 }); | ||
| } | ||
|
|
||
| const video = query[0]; | ||
| const user = await getCurrentUser(); | ||
|
|
||
| if (!video.public && video.ownerId !== user?.id) { | ||
| return Response.json({ error: "Unauthorized" }, { status: 401 }); | ||
| } | ||
|
|
||
| const meta = (video.metadata as Record<string, any>) ?? {}; |
There was a problem hiding this comment.
Metadata route bypasses typed API conventions
The new GET endpoint is an ad-hoc Next.js handler and casts persisted metadata to Record<string, any>, bypassing the repository's required HttpApi contract pattern and type narrowing. This leaves its request, response, and metadata fields vulnerable to contract drift.
Context Used: AGENTS.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 42-64
Comment:
**Metadata route bypasses typed API conventions**
The new GET endpoint is an ad-hoc Next.js handler and casts persisted metadata to `Record<string, any>`, bypassing the repository's required `HttpApi` contract pattern and type narrowing. This leaves its request, response, and metadata fields vulnerable to contract drift.
**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Fixes #1656
Summary
Add endpoint to the developer REST API ().
Greptile Summary
This PR adds a developer transcript endpoint alongside CLI recording metadata lookup, video metadata retrieval, configurable OpenAI-compatible endpoints, rate limits, tray shortcut labels, and teleprompter playback fixes.
/videos/:id/transcriptto the authenticated developer API.cap recordings infoand a supporting video metadata GET route.OPENAI_BASE_URLsupport to AI generation and messenger flows.Confidence Score: 1/5
The PR is not safe to merge until transcript delivery, metadata authorization, and CLI share-URL parsing are corrected.
The completed-transcript response cannot provide transcript bytes, the metadata route discloses password-protected AI content, and valid share-URL variants fail in the new CLI command.
Files Needing Attention: apps/web/app/api/developer/v1/[...route]/videos.ts, apps/web/app/api/video/metadata/route.ts, apps/cli/src/recordings.rs
Security Review
The new video metadata GET bypasses the canonical video-view policy, allowing unauthenticated callers to retrieve AI summaries and chapters for public videos that are password-protected.
Important Files Changed
recordings infocommand.Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "feat: add transcript download endpoint t..." | Re-trigger Greptile
Context used (6)
apps/cli)