Skip to content

feat: add +download shortcut for minutes media download#101

Merged
Ren1104 merged 17 commits intomainfrom
feat/minutes-media-download
Apr 2, 2026
Merged

feat: add +download shortcut for minutes media download#101
Ren1104 merged 17 commits intomainfrom
feat/minutes-media-download

Conversation

@Ren1104
Copy link
Copy Markdown
Collaborator

@Ren1104 Ren1104 commented Mar 30, 2026

Summary

Add a minutes +download shortcut to download audio/video media files from Lark Minutes, or retrieve a 1-day valid download URL without downloading.

Changes

  • Add shortcuts/minutes/minutes_download.go — implements the +download shortcut with --minute-token, --output, --url-only, --overwrite flags
  • Add shortcuts/minutes/shortcuts.go — minutes shortcut registry
  • Register minutes shortcuts in shortcuts/register.go
  • Add shortcuts/minutes/minutes_download_test.go — 10 unit and integration tests (81.8% coverage)
  • Update skills/lark-minutes/SKILL.md — add +download documentation, Shortcuts table, and minutes:minutes.media:export scope
  • Add skills/lark-minutes/references/lark-minutes-download.md — detailed reference for the download shortcut
  • Update skills/lark-vc/SKILL.md — clarify note_doc_token vs verbatim_doc_token, add cover image guidance

Test Plan

  • go vet ./... passes
  • go test -race -count=1 ./shortcuts/minutes/... — 10/10 tests pass (81.8% coverage)
  • go test -race -count=1 -timeout=5m ./cmd/... ./internal/... ./shortcuts/... passes (excluding pre-existing mail test timeout)
  • lark-cli minutes +download --dry-run outputs correct API request
  • lark-cli minutes +download --minute-token <token> --url-only returns valid download URL
  • lark-cli minutes +download --minute-token <token> downloads media file with correct filename

Summary by CodeRabbit

  • New Features

    • Added media download for minutes: single and batch modes (up to 50 tokens), --url-only preview, overwrite protection, atomic writes, and filename de-duplication.
  • Documentation

    • Updated skill docs with download overview, permissions, examples; added a detailed reference guide; enhanced VC notes guidance.
  • Tests

    • Added comprehensive unit and integration tests covering filename resolution, deduplication, CLI validation, dry-run, URL-only, and download flows.
  • Chores

    • Registered the new minutes shortcut so it’s discoverable in the CLI.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 30, 2026

CLA assistant check
All committers have signed the CLA.

@Ren1104 Ren1104 force-pushed the feat/minutes-media-download branch from c397651 to 52cf828 Compare March 31, 2026 08:52
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces a minutes +download shortcut enabling download of minute media from pre-signed URLs. Supports single and batch download modes with options for URL-only retrieval, overwrite control, and atomic file writing with SSRF validation, redirect policy enforcement, and concurrent batch processing.

Changes

Cohort / File(s) Summary
Minutes Download Implementation
shortcuts/minutes/minutes_download.go
Adds the minutes +download shortcut: fetches presigned download URLs from minutes API, validates URLs (SSRF), enforces safe redirect policy, resolves filenames from headers/Content-Type/minute title, atomically writes files, supports single-mode --output and batch-mode --output-dir with max 50 tokens and concurrent downloads (max 3 workers).
Minutes Download Tests
shortcuts/minutes/minutes_download_test.go
Comprehensive unit and integration tests for filename resolution, deduplication, CLI validation (flags, token limits), dry-run/URL-only modes, single and batch downloads, overwrite semantics, HTTP error propagation, partial failures, and duplicate-token handling.
Shortcut Registration
shortcuts/minutes/shortcuts.go, shortcuts/register.go
Exports minutes.Shortcuts() and registers the minutes shortcuts in global aggregation so MinutesDownload is discoverable and mountable.
Skill & Reference Documentation
skills/lark-minutes/SKILL.md, skills/lark-minutes/references/lark-minutes-download.md, skills/lark-vc/SKILL.md, skills/lark-vc/references/lark-vc-notes.md
Documents the new +download operation, usage examples and flags, required permission minutes:minutes.media:export, behavior/rate-limits/error codes, guidance for note_doc_token vs verbatim_doc_token, and artifact directory rules.
Test Artifact Cleanup
shortcuts/vc/artifact-*/transcript.txt
shortcuts/vc/artifact-Empty Artifacts-tok003/transcript.txt, shortcuts/vc/artifact-No Note Meeting-tok002/transcript.txt, shortcuts/vc/artifact-Test Minutes-tok001/transcript.txt
Removed leftover JSON payload lines from transcript test artifacts (cleanup).

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as CLI Tool (minutes +download)
    participant MinAPI as Minutes API
    participant Presigned as Presigned URL Service
    participant FS as File System

    User->>CLI: invoke minutes +download --minute-tokens=<tokens> [--url-only|--output|--output-dir]
    CLI->>CLI: Validate flags & batch constraints
    loop per token
        CLI->>MinAPI: GET /open-apis/minutes/v1/minutes/<token>/media
        MinAPI-->>CLI: { download_url }
    end
    par Concurrent downloads (max 3 workers)
        CLI->>Presigned: GET <download_url>
        Presigned-->>CLI: HTTP 2xx + headers/body or HTTP error
        CLI->>CLI: validate URL (SSRF) & safe redirect policy
        CLI->>CLI: resolve filename (Content-Disposition/Content-Type/title) & dedupe
        CLI->>FS: atomic write to output path
        FS-->>CLI: saved_path, size_bytes
    and
        Presigned-->>CLI: HTTP >=400
        CLI->>CLI: read error body (partial) and record per-token error
    end
    CLI-->>User: JSON summary (per-token success/error) or single-mode saved_path/size_bytes or URL-only output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • liangshuo-1

Poem

🐰 Down the warren I hop with a curl and a click,
Fetching presigned links, one token, then six,
Redirects politely checked, SSRF kept at bay,
Files land atomic—safe, deduped on display,
Thump! another shortcut stitched into the mix.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a +download shortcut for minutes media downloads, which aligns with the primary functionality introduced in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/minutes-media-download

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
skills/lark-vc/SKILL.md (1)

34-43: Clarify the <title> placeholder in artifact directory naming.

The artifact path ./artifact-<title>/cover uses <title> as a placeholder, but the documentation doesn't explain:

  • Whether it should be the actual meeting title from the API response
  • How to handle special characters, spaces, or other path-unsafe characters in the title
  • Whether there's a maximum length limit for filesystem compatibility

Add explicit guidance on sanitizing the title for filesystem safety (e.g., replacing spaces with hyphens, removing special characters, truncating if needed). This pattern is used consistently in the skill (./artifact-<title>/transcript.txt, ./artifact-<title>/cover), so clarifying it once would benefit all use cases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/lark-vc/SKILL.md` around lines 34 - 43, The docs use
./artifact-<title>/cover and ./artifact-<title>/transcript.txt but don't define
what <title> should be; update SKILL.md to state that <title> is the meeting
title extracted from the API response (from the result of the `lark-cli docs
+fetch --doc <note_doc_token>` call) and add an explicit sanitization rule: trim
and lowercase the title, replace whitespace with hyphens, remove or
percent-encode filesystem-unsafe characters (keep only alphanumeric, hyphen,
underscore, dot), and truncate to a safe max length (e.g., 100 chars); show one
concrete example mapping a raw title to the sanitized directory name and note
that if sanitization yields an empty string, fall back to a timestamp or note
token.
shortcuts/minutes/minutes_download_test.go (1)

227-227: URL scheme inconsistency in test stubs.

The mediaStub returns a full URL with scheme (https://example.com/presigned/download), but downloadStub is registered with a URL without the scheme (example.com/presigned/download). This works because the mock registry likely matches on the host+path portion, but it's inconsistent and could cause confusion.

Consider using consistent URL formats in the stubs, or document why the scheme is omitted for downloadStub.

Also applies to: 254-254, 279-279

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/minutes/minutes_download_test.go` at line 227, The test uses
inconsistent URL formats between mediaStub (which returns
"https://example.com/presigned/download") and the downloadStub registrations
(which use "example.com/presigned/download"); update the downloadStub
registrations (the calls to reg.Register(downloadStub(...))) to use the same
full URL with scheme ("https://example.com/presigned/download") or change
mediaStub to match the scheme-less form so both stubs are consistent, and apply
the same change for the other occurrences referenced (the downloadStub
registrations at the other locations).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/minutes/minutes_download.go`:
- Around line 21-23: The code currently sets const disableClientTimeout = 0
which removes the HTTP client timeout and relies on the caller context
(cmd.Context()) that typically has no deadline; update the logic so downloads
have a fallback deadline (suggest 5 minutes) by either changing
disableClientTimeout to an actual timeout value (e.g., 5*time.Minute) or,
better, wrap the context inside the Execute path (where Execute / cmd.Context()
is used to start the download) with context.WithTimeout when ctx has no deadline
so the HTTP client call(s) in the minutes download code (e.g., where Execute
invokes the download request) get a bounded context and the client Timeout
remains safe.

---

Nitpick comments:
In `@shortcuts/minutes/minutes_download_test.go`:
- Line 227: The test uses inconsistent URL formats between mediaStub (which
returns "https://example.com/presigned/download") and the downloadStub
registrations (which use "example.com/presigned/download"); update the
downloadStub registrations (the calls to reg.Register(downloadStub(...))) to use
the same full URL with scheme ("https://example.com/presigned/download") or
change mediaStub to match the scheme-less form so both stubs are consistent, and
apply the same change for the other occurrences referenced (the downloadStub
registrations at the other locations).

In `@skills/lark-vc/SKILL.md`:
- Around line 34-43: The docs use ./artifact-<title>/cover and
./artifact-<title>/transcript.txt but don't define what <title> should be;
update SKILL.md to state that <title> is the meeting title extracted from the
API response (from the result of the `lark-cli docs +fetch --doc
<note_doc_token>` call) and add an explicit sanitization rule: trim and
lowercase the title, replace whitespace with hyphens, remove or percent-encode
filesystem-unsafe characters (keep only alphanumeric, hyphen, underscore, dot),
and truncate to a safe max length (e.g., 100 chars); show one concrete example
mapping a raw title to the sanitized directory name and note that if
sanitization yields an empty string, fall back to a timestamp or note token.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ee69dd44-c8a0-401b-a4e4-2ce27150d928

📥 Commits

Reviewing files that changed from the base of the PR and between c8341bb and c95b224.

📒 Files selected for processing (11)
  • shortcuts/minutes/minutes_download.go
  • shortcuts/minutes/minutes_download_test.go
  • shortcuts/minutes/shortcuts.go
  • shortcuts/register.go
  • shortcuts/vc/artifact-Empty Artifacts-tok003/transcript.txt
  • shortcuts/vc/artifact-No Note Meeting-tok002/transcript.txt
  • shortcuts/vc/artifact-Test Minutes-tok001/transcript.txt
  • skills/lark-minutes/SKILL.md
  • skills/lark-minutes/references/lark-minutes-download.md
  • skills/lark-vc/SKILL.md
  • skills/lark-vc/references/lark-vc-notes.md
💤 Files with no reviewable changes (3)
  • shortcuts/vc/artifact-Empty Artifacts-tok003/transcript.txt
  • shortcuts/vc/artifact-Test Minutes-tok001/transcript.txt
  • shortcuts/vc/artifact-No Note Meeting-tok002/transcript.txt

Comment thread shortcuts/minutes/minutes_download.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
shortcuts/minutes/minutes_download_test.go (2)

103-114: Minor: chdir modifies global state, preventing parallel test execution.

Tests using chdir cannot safely use t.Parallel(). This is fine for now since no parallel calls exist, but documenting this constraint would help future maintainers.

♻️ Add a comment to document the constraint
-// chdir 切换工作目录并在测试结束后恢复
+// chdir 切换工作目录并在测试结束后恢复
+// NOTE: Tests using chdir cannot be run in parallel (no t.Parallel).
 func chdir(t *testing.T, dir string) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/minutes/minutes_download_test.go` around lines 103 - 114, The chdir
helper modifies process-wide working directory (uses os.Chdir and t.Cleanup to
restore) which makes tests using it incompatible with t.Parallel; update the
chdir function comment to state that it mutates global state and therefore tests
that call chdir must not run in parallel (or suggest using a temp
process/workdir isolation alternative), and include references to chdir,
os.Chdir and t.Cleanup so future maintainers understand the constraint.

54-60: Consider checking the Execute() error to surface setup failures.

Ignoring the error from parent.Execute() in cache warming could mask initialization problems, leading to confusing failures in subsequent tests.

♻️ Proposed fix
 		parent.SetArgs([]string{"+warm"})
 		parent.SilenceErrors = true
 		parent.SilenceUsage = true
-		parent.Execute()
+		if err := parent.Execute(); err != nil {
+			t.Logf("warmTokenCache: %v", err)
+		}
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/minutes/minutes_download_test.go` around lines 54 - 60, The test
currently ignores the error returned by parent.Execute(), which can hide setup
failures; update the test to capture and assert the Execute() error (e.g., err
:= parent.Execute()) and fail the test if err != nil (using t.Fatalf or
require.NoError) after mounting the command via s.Mount and setting args with
parent.SetArgs([]string{"+warm"}), so any cache-warm initialization errors
surface immediately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/minutes/minutes_download_test.go`:
- Around line 316-338: The test setup in TestDownload_OverwriteProtection
currently ignores the error from os.WriteFile when creating "existing.mp4";
update the TestDownload_OverwriteProtection function to check the error returned
by os.WriteFile and fail the test immediately (e.g., t.Fatal or t.Fatalf) if it
is non-nil so the test fails fast and doesn’t produce misleading results—locate
the os.WriteFile call near the start of TestDownload_OverwriteProtection and add
the error check there.

---

Nitpick comments:
In `@shortcuts/minutes/minutes_download_test.go`:
- Around line 103-114: The chdir helper modifies process-wide working directory
(uses os.Chdir and t.Cleanup to restore) which makes tests using it incompatible
with t.Parallel; update the chdir function comment to state that it mutates
global state and therefore tests that call chdir must not run in parallel (or
suggest using a temp process/workdir isolation alternative), and include
references to chdir, os.Chdir and t.Cleanup so future maintainers understand the
constraint.
- Around line 54-60: The test currently ignores the error returned by
parent.Execute(), which can hide setup failures; update the test to capture and
assert the Execute() error (e.g., err := parent.Execute()) and fail the test if
err != nil (using t.Fatalf or require.NoError) after mounting the command via
s.Mount and setting args with parent.SetArgs([]string{"+warm"}), so any
cache-warm initialization errors surface immediately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e070cad9-dd8c-4bb1-af08-880883a07ac0

📥 Commits

Reviewing files that changed from the base of the PR and between c95b224 and 1434ef1.

📒 Files selected for processing (2)
  • shortcuts/minutes/minutes_download.go
  • shortcuts/minutes/minutes_download_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/minutes/minutes_download.go

Comment thread shortcuts/minutes/minutes_download_test.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
shortcuts/minutes/minutes_download.go (1)

270-287: Edge-case collision if multiple tokens share same 6-character prefix.

If three or more tokens share the same 6-character prefix and all resolve to the same filename (from Content-Disposition), the deduplication could generate identical filenames. Line 285 uses Store without verifying the deduped name is unique, potentially causing file overwrites.

While unlikely in practice, consider using LoadOrStore for the deduped name or using the full token as the suffix:

♻️ Proposed fix using LoadOrStore for deduped name
 func deduplicateFilename(name, minuteToken string, usedNames *sync.Map) string {
 	if _, loaded := usedNames.LoadOrStore(name, true); !loaded {
 		return name
 	}

 	// collision: insert _<token_prefix> before extension
 	ext := filepath.Ext(name)
 	base := strings.TrimSuffix(name, ext)
-	suffix := minuteToken
-	if len(suffix) > tokenSuffixLen {
-		suffix = suffix[:tokenSuffixLen]
-	}
+	// Use full token to guarantee uniqueness since tokens are unique
+	suffix := minuteToken
 	deduped := base + "_" + suffix + ext
-	usedNames.Store(deduped, true)
+	usedNames.LoadOrStore(deduped, true)
 	return deduped
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/minutes/minutes_download.go` around lines 270 - 287, The
deduplicateFilename function can still produce duplicate names when multiple
minuteToken values share the same tokenPrefix because it unconditionally
usedNames.Store(deduped, true); change this to attempt to reserve the deduped
name with usedNames.LoadOrStore and, if LoadOrStore indicates a collision, retry
by expanding the suffix (e.g., use a longer slice of minuteToken beyond
tokenSuffixLen or the full minuteToken) or append an incrementing counter until
LoadOrStore returns not-loaded, then return that reserved name; update the logic
around minuteToken, tokenSuffixLen, and usedNames accordingly to loop until a
unique name is successfully stored.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@shortcuts/minutes/minutes_download.go`:
- Around line 270-287: The deduplicateFilename function can still produce
duplicate names when multiple minuteToken values share the same tokenPrefix
because it unconditionally usedNames.Store(deduped, true); change this to
attempt to reserve the deduped name with usedNames.LoadOrStore and, if
LoadOrStore indicates a collision, retry by expanding the suffix (e.g., use a
longer slice of minuteToken beyond tokenSuffixLen or the full minuteToken) or
append an incrementing counter until LoadOrStore returns not-loaded, then return
that reserved name; update the logic around minuteToken, tokenSuffixLen, and
usedNames accordingly to loop until a unique name is successfully stored.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2a67a8fa-59a4-425b-8349-298a64da9ba0

📥 Commits

Reviewing files that changed from the base of the PR and between ce6ee5b and a29e437.

📒 Files selected for processing (1)
  • shortcuts/minutes/minutes_download.go

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@6637afc506db016d2b9105c70682fef6e3ff828b

🧩 Skill update

npx skills add larksuite/cli#feat/minutes-media-download -y -g

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 1, 2026

Greptile Summary

This PR adds a minutes +download shortcut that fetches pre-signed download URLs from the Lark Minutes API and streams media files to disk, supporting single and batch (up to 50 tokens) modes. Previous critical issues (shared HTTP client timeout mutation, concurrent RuntimeContext access from goroutines) have been addressed: the execution loop is now fully sequential and the client is cloned by value before mutating Timeout.

Confidence Score: 5/5

Safe to merge; prior P0/P1 issues are resolved and only minor style findings remain.

All previously flagged critical issues have been addressed: the shared client mutation is fixed by struct-copy cloning, concurrency problems are eliminated by making the loop sequential, and the intentional omission of transport-level IP validation is clearly documented. The two remaining findings are both P2 style issues (orphaned comment and unused export) that do not affect correctness or security.

shortcuts/minutes/minutes_download.go (stale comment block at line 319), internal/validate/url.go (unexported candidate WrapDialContextWithIPCheck)

Important Files Changed

Filename Overview
shortcuts/minutes/minutes_download.go Core download shortcut implementation — sequential batch loop, struct-copy client cloning (shared-timeout mutation fixed), URL/redirect SSRF guards, and filename deduplication; stale orphaned comment block remains on the extFromContentType docstring.
internal/validate/url.go Adds exported WrapDialContextWithIPCheck utility for post-connect IP validation; function is well-implemented but currently has no callers in the codebase.
shortcuts/minutes/minutes_download_test.go 10 unit and integration tests covering filename resolution, validation, dry-run, URL-only, overwrite protection, HTTP errors, batch partial failure, deduplication, and dry-run batch — good coverage.
shortcuts/minutes/shortcuts.go Simple shortcut registry for the minutes package; registers MinutesDownload correctly.
shortcuts/register.go Adds minutes.Shortcuts() to the global shortcut registry — clean, no issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([minutes +download]) --> B{Validate tokens}
    B -- invalid --> Z1([ErrValidation])
    B -- valid --> C{--dry-run?}
    C -- yes --> Z2([Print API shape])
    C -- no --> D[Clone HTTP client\nset Timeout=0\nset CheckRedirect]
    D --> E[For each token\n rate-limit 5 req/s]
    E --> F[fetchDownloadURL\nDoAPIJSON GET /media]
    F -- error --> G[record error]
    F -- ok --> H{--url-only?}
    H -- yes --> I[record URL]
    H -- no --> J[downloadMediaFile]
    J --> K[ValidateDownloadSourceURL]
    K --> L[HTTP GET presigned URL]
    L --> M[resolveFilenameFromResponse\nContent-Disposition > Content-Type > token.media]
    M --> N[deduplicateFilename\nif batch collision]
    N --> O[SafeOutputPath\npath traversal guard]
    O --> P[AtomicWriteFromReader]
    P --> Q[record saved_path + size]
    G & I & Q --> R{single mode?}
    R -- yes --> S([Output single result or error])
    R -- no --> T([Output batch results array])
Loading

Reviews (9): Last reviewed commit: "fix(minutes): add transport-level SSRF p..." | Re-trigger Greptile

Comment thread shortcuts/minutes/minutes_download.go Outdated
Comment thread shortcuts/minutes/minutes_download.go Outdated
Comment thread shortcuts/minutes/minutes_download.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/minutes/minutes_download.go`:
- Around line 133-136: executeBatch currently serializes URL fetching but allows
concurrent workers to call runtime via resolveOutputFromResponse ->
fetchMinuteTitle, reintroducing shared RuntimeContext access; fix by preloading
the title (or filename stem) for each token during phase 1 using
fetchMinuteTitle (or equivalent) and attach that value to the per-download
metadata so that downloadMediaFile no longer calls runtime.DoAPIJSON. Thread the
preloaded title/filename into downloadOpts (and into resolveOutputFromResponse
where it builds the final filename) so phase 2 only performs pure HTTP I/O;
apply the same change to other places that call
resolveOutputFromResponse/downloadMediaFile to keep RuntimeContext off the
concurrent path.
- Around line 154-190: The loop that calls fetchDownloadURL over tokens can
exceed the documented 5 req/s limit; modify the phase-1 URL fetch to rate-limit
calls to 5 requests per second (e.g., use a token bucket or time.Ticker) while
iterating tokens in the for i, token := range tokens loop, and add retry logic
for rate-limit responses from fetchDownloadURL (detect HTTP 429 or equivalent
error, retry with exponential backoff and jitter up to a small max attempts) so
transient rate limits are retried before marking results[i] as an error; ensure
duplicate detection (seen map), urlOnly handling, and appending to tasks remain
unchanged, only pacing and retry wrapping are added around the fetchDownloadURL
call.
- Around line 263-275: The deduplication can still collide because deduped :=
base+"_"+suffix+ext is stored without checking; modify deduplicateFilename to
probe in a loop using usedNames.LoadOrStore on each candidate until you actually
reserve a free name: try the original name, if taken compute suffix :=
minuteToken[:tokenSuffixLen], form candidate := base+"_"+suffix+ext and call
usedNames.LoadOrStore(candidate, true); if that returns loaded==true, append or
increment a numeric counter (e.g., base+"_"+suffix+"_"+strconv.Itoa(i)+ext) and
retry until LoadOrStore returns loaded==false, then return the reserved
candidate. Ensure references to usedNames, minuteToken, tokenSuffixLen and the
function deduplicateFilename are used so the change is applied in the right
place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13a75ae5-fd65-48b9-9e3c-1111b6cf01a7

📥 Commits

Reviewing files that changed from the base of the PR and between a29e437 and 23746bf.

📒 Files selected for processing (4)
  • shortcuts/minutes/minutes_download.go
  • shortcuts/minutes/minutes_download_test.go
  • skills/lark-minutes/SKILL.md
  • skills/lark-minutes/references/lark-minutes-download.md
✅ Files skipped from review due to trivial changes (1)
  • shortcuts/minutes/minutes_download_test.go

Comment thread shortcuts/minutes/minutes_download.go Outdated
Comment thread shortcuts/minutes/minutes_download.go Outdated
Comment thread shortcuts/minutes/minutes_download.go Outdated
@Ren1104 Ren1104 force-pushed the feat/minutes-media-download branch from fc62027 to cc47e84 Compare April 1, 2026 07:42
Comment thread shortcuts/minutes/minutes_download.go Outdated
@github-actions github-actions Bot added domain/vc PR touches the vc domain size/L Large or sensitive change across domains or core paths labels Apr 2, 2026
Comment thread shortcuts/minutes/minutes_download.go Outdated
Comment thread shortcuts/minutes/minutes_download_test.go
Comment thread shortcuts/minutes/minutes_download.go
@Ren1104 Ren1104 force-pushed the feat/minutes-media-download branch 2 times, most recently from ee60cbb to ec09b3c Compare April 2, 2026 07:03
Comment thread shortcuts/minutes/minutes_download.go
@Ren1104 Ren1104 force-pushed the feat/minutes-media-download branch from ec09b3c to 6637afc Compare April 2, 2026 07:16
@Ren1104 Ren1104 requested a review from liangshuo-1 April 2, 2026 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/vc PR touches the vc domain size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants