# Dashboard media-path safety and private-upload MVP notes

Use when extending creator-facing video dashboards that ingest prepared packages, analytics-only rows, website companion state, and platform upload/audit flows.

## Media/thumbnail path pitfall

Analytics-imported or website-only videos may intentionally have no local media file. Do **not** treat a blank path as a file:

```python
Path("")  # resolves to the current directory
Path("").exists()  # often True
Path("").is_file()  # False
```

Guard ffmpeg/thumbnail/media responses with both a non-empty path and `is_file()`:

```python
video_path = Path(video.file_path or "")
if video.file_path and video_path.exists() and video_path.is_file():
    generate_thumbnail(video_path, thumb)
else:
    return placeholder_thumbnail(video.working_title)
```

Similarly, media-serving endpoints should reject empty, missing, or directory paths with a clean 404 rather than passing them into `FileResponse` or ffmpeg.

Regression tests to add:
- `GET /videos/{id}/thumbnail` with `file_path=""` returns a placeholder image, not an ffmpeg error.
- `GET /videos/{id}/media` with `file_path=""` returns `404 Video file missing`.

## YouTube private-upload MVP pattern

For a first real platform-upload sprint, keep the flow deliberately conservative:

- Scope only what is needed: `https://www.googleapis.com/auth/youtube.upload`.
- Store OAuth tokens server-side only; encrypt access/refresh tokens and expose only `has_*_token` booleans.
- Validate OAuth `state` in the callback and show a clear config blocker if OAuth credentials are missing.
- Upload endpoint should be job/audit-backed, not a silent synchronous side-effect hidden in the UI.
- Require explicit confirmations for private upload, metadata review, and disclosure review.
- Hard-block public/unlisted uploads in the MVP; force `privacyStatus=private` and `notifySubscribers=false`.
- Record an upload audit with redacted request/response JSON, scopes used, endpoints called, privacy, synthetic-media flag, and website-link permission.
- Update internal context on success: draft external id, video external YouTube id, external post row, activity events, and website companion status.
- Website links may be stored internally but must remain hidden/publicly inactive until `website_link_allowed=true` and a separate Website QA/push approval happens.

## Dry-run pattern

Always include a dry-run mode before real OAuth/API upload is available:

- Env/config: `YOUTUBE_UPLOAD_DRY_RUN=true` or request payload `dry_run=true`.
- Do not call the provider API.
- Generate a clear fake id such as `dryrun_<uuid>`.
- Exercise the same DB/audit/context update path as a real success.
- Mark any dry-run account as `account_type=dry_run` and `status=dry_run`; do not display it as a real connected OAuth account.
- UI must label the result clearly: `Dry run complete — not uploaded to YouTube`.

## Verification checklist

- Backend tests cover missing confirmations, non-private privacy rejection, missing account rejection, encrypted-token roundtrip, dry-run audit/context updates, and token redaction.
- Frontend build passes.
- Live smoke verifies settings/account status, upload audit logs, website status, and no website repo push.
- Final report states: real upload attempted yes/no, YouTube ID if real/dry-run, audit id, draft/video/external-post updates, website companion status, and website push status.
