Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cdc0908
feat(options): add About section with version and author info
jvillegasd Mar 4, 2026
1298940
feat(cloud): implement S3 and Google Drive upload support
jvillegasd Mar 4, 2026
70d38cd
refactor(tabs): unify tab style across popup and options with shared …
jvillegasd Mar 4, 2026
d6ad1d9
feat(security): encrypt S3 secret key at rest with AES-GCM
jvillegasd Mar 4, 2026
bc589bf
feat(options): show confirm passphrase field only when passphrase is set
jvillegasd Mar 4, 2026
ef28e85
feat(cloud): remove auto-upload, keep manual deferred upload only
jvillegasd Mar 4, 2026
7091f81
fix(cloud): implement proper chunked resumable upload for Google Drive
jvillegasd Mar 4, 2026
ccf65ca
fix(cloud): lower S3 multipart threshold to match part size
jvillegasd Mar 4, 2026
2ac4ec9
feat: s3 client done
jvillegasd Mar 4, 2026
fe9f930
feat(cloud): single-provider upload selection with video MIME validation
jvillegasd Mar 4, 2026
8949244
refactor(cloud): factory/abstraction pattern for cloud providers
jvillegasd Mar 5, 2026
843d996
docs(claude): update cloud upload section to reflect provider abstrac…
jvillegasd Mar 5, 2026
00cb0b8
docs(claude): document S3 bucket CORS requirement
jvillegasd Mar 5, 2026
4006d02
feat(cloud): add manual upload action to history page
jvillegasd Mar 5, 2026
55c27f9
fix: s3-like API upload fixed, signature error handled.
jvillegasd Mar 5, 2026
aa816d1
feat(cloud): add upload progress UI and uploaded status badge
jvillegasd Mar 5, 2026
693865c
feat: created GEMINI.md
jvillegasd Mar 5, 2026
afc82bc
fix(cloud): upload flow robustness — race conditions, progress, and d…
jvillegasd Mar 5, 2026
57a1a73
feat(cloud): water-fill upload progress icon with cloud + arrow
jvillegasd Mar 5, 2026
ce4ab7d
feat(cloud): abort signal support for uploads and cancel UI
jvillegasd Mar 5, 2026
d2eead1
feat(cloud): crash-resilient S3 multipart upload cleanup
jvillegasd Mar 5, 2026
4c3db3d
feat(cloud): add confirmation dialog before cancelling uploads
jvillegasd Mar 5, 2026
cf40c36
feat(cloud): Google Drive OAuth setup with user-provided client ID
jvillegasd Mar 5, 2026
0617d25
fix(cloud): live re-render history items on upload state changes
jvillegasd Mar 5, 2026
2e11ad0
fix(cloud): use correct upload endpoint for Drive simple multipart up…
jvillegasd Mar 5, 2026
f53aae6
fix(cloud): handle abort gracefully in Google Drive upload
jvillegasd Mar 5, 2026
936b1e8
docs: update CLAUDE.md and README for cloud upload feature
jvillegasd Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,49 @@ All HLS, M3U8, and DASH downloads are processed by **FFmpeg.wasm** running insid

> **Planned**: Migrate FFmpeg.wasm to [mediabunny](https://github.com/nicktindall/mediabunny) for native-speed muxing without the 2 GB constraint.

## Cloud Upload (Planned)
## Cloud Upload

The code infrastructure for Google Drive uploads exists in `src/core/cloud/` (`GoogleAuth`, `GoogleDriveClient`, `UploadManager`) and the `uploadToDrive` flag is plumbed through `DownloadManager`, but **no actual upload is ever triggered** — `this.uploadToDrive` is stored but never used in `download()`. Do not document this as a working feature. Future work will wire this up and add support for additional providers (S3, Dropbox, etc.).
`src/core/cloud/` contains the full provider abstraction:

- `base-cloud-provider.ts` — abstract `BaseCloudProvider` with `id: CloudProvider` and `upload(blob, filename, onProgress?, signal?): Promise<string>`
- `google-auth.ts` — `GoogleAuth` static class; OAuth via `chrome.identity.launchWebAuthFlow()` with user-provided client ID (stored in `chrome.storage.local` under `google_client_id`). No `oauth2` manifest key needed.
- `google-drive.ts` — `GoogleDriveClient extends BaseCloudProvider`; simple multipart for files ≤ 5 MB, resumable chunked upload for larger files
- `s3-client.ts` — `S3Client extends BaseCloudProvider`; SigV4-signed PUT for files < 10 MB, multipart for ≥ 10 MB. Persists in-flight multipart `uploadId` to `chrome.storage.local` (`s3_pending_uploads`) for crash-resilient cleanup.
- `upload-manager.ts` — `Map<CloudProvider, BaseCloudProvider>` registry; routes `uploadBlob()` through `client.upload()`; `isConfigured()` checks `providers.size > 0`

### Upload Flow

Upload is **manual only**. Completed downloads show an "Upload to cloud" action in both the History page (Options → History → item menu) and the popup Downloads tab. Clicking it opens a file picker (`showOpenFilePicker`); the user selects the local video file, which is stored in IDB (since `chrome.runtime.sendMessage` uses JSON serialization that destroys `ArrayBuffer`), then an `UPLOAD_REQUEST` message is sent to the service worker. If both providers are configured, a provider picker dialog appears. There is no automatic upload on download completion.

Upload progress is tracked via `DownloadStage.UPLOADING` and displayed with a water-fill cloud icon in history. Uploads can be cancelled via an `AbortController`; cancellation prompts a confirmation dialog. On cancel, `AbortError` (DOMException) is expected and handled silently — not logged as an error.

### Crash Resilience

- **S3 multipart**: In-flight `(key, uploadId)` pairs are persisted to `chrome.storage.local`. On service worker startup, `S3Client.cleanupOrphanedUploads()` aborts any orphaned uploads and clears the list.
- **Stuck uploads**: Downloads in `UPLOADING` stage after a crash are restored to `COMPLETED` by `cleanupStaleUploads()` in `init()`.

### Google Drive Setup

Users must create their own OAuth credentials (free). The options page shows step-by-step instructions:
1. Enable Google Drive API in Google Cloud Console
2. Create OAuth 2.0 Client ID (type: **Web application**)
3. Add the extension's redirect URI (`chrome.identity.getRedirectURL()`) as an authorized redirect URI
4. Paste the client ID into the options page
5. Clicking "Sign in with Google" auto-saves settings with `enabled: true`

The `chromiumapp.org` redirect works for unpacked extensions — Chrome intercepts it internally without needing the Chrome Web Store.

### Adding a New Provider

Create a class extending `BaseCloudProvider`, add its key to the `CloudProvider` union in `shared/messages.ts`, instantiate and register it in the `UploadManager` constructor — no other code needs to change.

### S3 Bucket CORS Requirement

The bucket must have a CORS policy whitelisting the extension origin. The options page S3 section generates the correct JSON dynamically (using `chrome.runtime.id`) and provides a "Copy CORS Config" button. Paste it into **S3 → bucket → Permissions → Cross-origin resource sharing (CORS) → Edit**. Without it the browser will block upload requests from the `chrome-extension://` origin.

### S3 Secret Key Encryption

The S3 secret access key can be encrypted at rest with AES-GCM via `SecureStorage`. The user sets a passphrase in the options page; the key is stored as an `EncryptedBlob` in `chrome.storage.local`. On upload, `resolveS3Secret()` in the service worker decrypts it using the passphrase from session storage.

## Architecture

Expand Down Expand Up @@ -203,11 +243,14 @@ src/
│ │ └── chunks.ts # storeChunk(), deleteChunks(), getChunkCount()
│ ├── storage/
│ │ ├── chrome-storage.ts
│ │ ├── secure-storage.ts # AES-GCM encrypt/decrypt for S3 secret key
│ │ └── settings.ts # AppSettings interface + loadSettings() — always use this
│ ├── cloud/ # ⚠️ Planned — not wired up yet
│ │ ├── google-auth.ts
│ │ ├── google-drive.ts
│ │ └── upload-manager.ts
│ ├── cloud/
│ │ ├── base-cloud-provider.ts # Abstract base + ProgressCallback type
│ │ ├── google-auth.ts # OAuth via launchWebAuthFlow (user-provided client ID)
│ │ ├── google-drive.ts # GoogleDriveClient extends BaseCloudProvider
│ │ ├── s3-client.ts # S3Client extends BaseCloudProvider + orphaned upload cleanup
│ │ └── upload-manager.ts # Provider registry (Map<CloudProvider, BaseCloudProvider>)
│ ├── metadata/
│ │ └── metadata-extractor.ts
│ └── utils/
Expand Down
Loading