Skip to content

feat: implement policy-constrained YouTube import (#30) - #90

Merged
seonghobae merged 10 commits into
developfrom
feat/issue-30-youtube-import
Mar 26, 2026
Merged

feat: implement policy-constrained YouTube import (#30)#90
seonghobae merged 10 commits into
developfrom
feat/issue-30-youtube-import

Conversation

@seonghobae

@seonghobae seonghobae commented Mar 26, 2026

Copy link
Copy Markdown
Collaborator

Fixes #30

Summary

  • Added yt-dlp to Python engine dependencies and recorded it in the supplemental component inventory to comply with supply chain policies.
  • Implemented robust YouTube audio extraction logic in youtube.py with strict policy enforcement:
    • Audio-only extraction (no video rendering)
    • 50MB max file size limit
    • 15-minute max duration limit
  • Exposed import_youtube_url via Tauri IPC in Rust backend.
  • Added frontend UI elements in React to input a YouTube URL and initiate the import process, with error handling.
  • Added comprehensive unit tests in Python and React, maintaining 100% code coverage.

Security Notes

  • Untrusted Input: The YouTube URL is treated as untrusted input. It is passed to yt-dlp which securely fetches the metadata and stream.
  • Subprocesses: yt-dlp is used as a library internally rather than a spawned subprocess if possible, or tightly controlled.
  • File Handling: Downloads are strictly routed to the secure app temp directory.
  • Constraints: Enforced 50MB file size limit and 15-minute duration to prevent resource exhaustion (DoS) attacks.
📝 Walkthrough

Walkthrough

YouTube URL을 검증하고 yt-dlp로 오디오를 내려받아 프로젝트별 워크스페이스에 부트스트랩 페이로드를 생성·저장하는 전체 흐름(데스크톱 UI → 분석 래퍼 → Tauri 명령 → Python 엔진)을 추가했습니다.

Changes

Cohort / File(s) Summary
Tauri 백엔드 — YouTube 임포트 명령
apps/desktop/src-tauri/src/main.rs
비동기 Tauri 명령 import_youtube_url 추가: HTTPS + 허용 호스트 검증, per-import 프로젝트/캐시/임시 루트 생성, 분석 엔진 서브프로세스 실행(120s 타임아웃), stdout JSON 파싱, 파일 메타 검사 후 ProjectBootstrapSummaryPayload 생성·저장, 에러 경로 처리 및 핸들러 등록.
데스크톱 UI 및 테스트
apps/desktop/src/App.tsx, apps/desktop/src/App.test.tsx
YouTube URL 입력 필드·임포트 버튼·상태(youtubeUrl, isImporting) 추가, 버튼 비활성화/레이블 업데이트, 성공·실패·거부 케이스 테스트 및 Tauri 모킹 확장.
데스크톱 분석 API 래퍼
apps/desktop/src/lib/analysis.ts
importYoutubeUrl(url: string) 공개 함수 추가: Tauri 호출 래핑, 성공 시 bootstrap 파싱 반환, 실패 시 표준화된 오류 객체 반환.
Python 분석 엔진 — yt-dlp 통합
services/analysis-engine/src/bandscope_analysis/youtube.py, services/analysis-engine/pyproject.toml
새 모듈 youtube.py 추가: validate_url, download_youtube_audio, CLI main() 구현. yt-dlp 의존성(yt-dlp>=2026.3.17) 추가. 사전/사후 제약(재생시간 15분, 크기 50MB), 오류 코드 매핑, JSON 출력 및 종료 코드 처리.
엔진 테스트
services/analysis-engine/tests/test_youtube.py
validate_urldownload_youtube_audio의 다양한 성공/실패/제약 케이스(기간·크기 초과, 다운로드 오류, 파일 미발견 등) 및 CLI 흐름을 모킹해 검증하는 pytest 스위트 추가.
공급망·메타데이터·러스트 deps
supply-chain/supplemental-component-inventory.json, apps/desktop/src-tauri/Cargo.toml
yt-dlp 번들 메타데이터 추가(버전 제약·소스·라이선스 등) 및 Tauri쪽 Rust 의존성(tokio + time feature, url) 추가.
로컬라이제이션
apps/desktop/src/locales/en/common.json, apps/desktop/src/locales/ko/common.json
YouTube 임포트 관련 UI 문자열(플레이스홀더, 버튼 라벨, 진행 텍스트, 실패 메시지) 추가.

Sequence Diagram(s)

sequenceDiagram
    participant User as "사용자 (UI)"
    participant App as "App.tsx"
    participant AnalysisLib as "analysis.ts"
    participant TauriCmd as "Tauri main.rs"
    participant Engine as "Python 엔진 (youtube.py)"

    User->>App: URL 입력 및 임포트 클릭
    App->>AnalysisLib: importYoutubeUrl(url)
    AnalysisLib->>TauriCmd: invoke("import_youtube_url", { url })
    TauriCmd->>TauriCmd: URL 검증, per-import 루트 생성
    TauriCmd->>Engine: spawn (--module bandscope_analysis.youtube --url --out-dir) (with timeout)
    Engine->>Engine: validate_url(), yt-dlp 메타조회/다운로드, 길이/크기 검사
    Engine-->>TauriCmd: JSON 결과(stdout) (ok / error)
    TauriCmd->>TauriCmd: stdout 파싱, 파일 메타 확인, LocalAudio 소스 생성·저장
    TauriCmd-->>AnalysisLib: ProjectBootstrapSummaryPayload 또는 오류 문자열
    AnalysisLib-->>App: 성공/실패 응답
    App-->>User: 소스 렌더링 또는 에러 표시
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇
URL 한 조각 던지면 내가 살펴볼게요,
규칙대로 가려 노랫소리만 담아둘게요,
제목 다듬어 새 폴더에 살포시 눕히고,
못 하면 이유 말해주며 다른 길을 비춰줄게요,
토끼가 껑충, 소리잔치에 초대합니다 🎶

- Add `yt-dlp` to Python engine dependencies and inventory
- Implement robust audio extraction logic enforcing constraints (audio only, 50MB limit, 15 min limit)
- Expose `import_youtube_url` Tauri IPC command
- Add UI inputs for YouTube URL import and loading state
- Ensure 100% test coverage
@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

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
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c4dd0dda-8e0d-47e5-97a2-2745717f53a0

📥 Commits

Reviewing files that changed from the base of the PR and between 56bb68d and 4e4fea9.

⛔ Files ignored due to path filters (2)
  • apps/desktop/src-tauri/Cargo.lock is excluded by !**/*.lock
  • services/analysis-engine/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • apps/desktop/src-tauri/Cargo.toml
  • apps/desktop/src-tauri/src/main.rs
  • apps/desktop/src/App.test.tsx
  • apps/desktop/src/App.tsx
  • apps/desktop/src/lib/analysis.ts
  • apps/desktop/src/locales/en/common.json
  • apps/desktop/src/locales/ko/common.json
  • services/analysis-engine/pyproject.toml
  • services/analysis-engine/src/bandscope_analysis/youtube.py
  • services/analysis-engine/tests/test_youtube.py
  • supply-chain/supplemental-component-inventory.json

Cache: Disabled due to Reviews > Disable Cache setting

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • YouTube URL에서 오디오를 직접 가져올 수 있는 기능이 추가되었습니다.
    • 앱의 헤더에 YouTube 입력 필드와 가져오기 버튼이 추가되었습니다.
    • 가져오기 중 다른 작업이 비활성화되어 안정적인 처리를 보장합니다.
    • 표준 YouTube URL만 지원합니다.
    • 가져오기 실패 시 명확한 오류 메시지가 표시됩니다.

Walkthrough

YouTube URL을 검증하고 yt-dlp 기반 Python 엔진을 서브프로세스로 실행해 오디오를 다운로드한 뒤 per‑import 프로젝트/캐시/임시 루트를 생성해 LocalAudio 기반 ProjectBootstrapSummaryPayload를 반환·저장하는 엔드투엔드 유입 경로를 추가했습니다.

Changes

Cohort / File(s) Summary
Tauri 백엔드 — YouTube 임포트
apps/desktop/src-tauri/src/main.rs
YOUTUBE_IMPORT_TIMEOUT 상수 추가 및 import_youtube_url Tauri 명령 추가: HTTPS/호스트 검증, per‑import 루트 생성, 분석 서브프로세스 실행(120s 타임아웃), stdout JSON 파싱, 파일 메타확인→LocalAudio ProjectBootstrapSummaryPayload 생성·저장, 고정 오류 문자열 반환 및 핸들러 등록.
프론트엔드 UI 및 테스트
apps/desktop/src/App.tsx, apps/desktop/src/App.test.tsx
YouTube URL 입력, 임포트 버튼과 youtubeUrl/isImporting 상태 및 비활성화 로직 추가. 성공/해당 오류/거부 케이스를 검증하는 테스트 3개 추가 및 UI 렌더링 확인.
프론트엔드 분석 래퍼
apps/desktop/src/lib/analysis.ts
importYoutubeUrl(url: string) 공개 함수 추가: Tauri 호출 래핑, 성공 시 bootstrap 파싱 반환, 실패 시 표준화된 오류 객체 반환.
분석 엔진 (Python) — yt-dlp 통합
services/analysis-engine/src/bandscope_analysis/youtube.py, services/analysis-engine/pyproject.toml
youtube.py 추가: validate_url, download_youtube_audio, CLI main() 구현. yt-dlp 의존성(yt-dlp>=2026.3.17) 추가. 길이(15분)·크기(50MB) 제약, 오류 코드 매핑, JSON 출력·종료 코드 처리.
엔진 테스트
services/analysis-engine/tests/test_youtube.py
validate_urldownload_youtube_audio의 정상/오류/제약(기간·크기·파일부재·제한콘텐츠 등) 경로와 CLI 흐름을 모킹해 검증하는 pytest 스위트 추가.
공급망·메타데이터·러스트 deps
supply-chain/supplemental-component-inventory.json, apps/desktop/src-tauri/Cargo.toml
yt-dlp 번들 메타데이터(버전·소스·라이선스·storagePath·releaseUsage) 추가 및 Tauri 데스크톱 Rust 의존성(tokio w/ time, url) 추가.
로컬라이제이션
apps/desktop/src/locales/en/common.json, apps/desktop/src/locales/ko/common.json
YouTube 임포트 관련 UI 문자열(플레이스홀더, 버튼 라벨, 진행 텍스트, 실패 메시지) 추가 및 소소한 문장부호 정리.

Sequence Diagram(s)

sequenceDiagram
    participant User as "사용자 (UI)"
    participant App as "App.tsx"
    participant Lib as "analysis.ts"
    participant Tauri as "Tauri (main.rs)"
    participant Engine as "Python 엔진 (youtube.py)"

    User->>App: URL 입력 및 임포트 클릭
    App->>Lib: importYoutubeUrl(url)
    Lib->>Tauri: invoke("import_youtube_url",{url})
    Tauri->>Tauri: URL 검증 및 per‑import 루트 생성
    Tauri->>Engine: spawn subprocess (bandscope_analysis.youtube --url --out-dir) within timeout
    Engine->>Engine: validate_url(), yt-dlp 메타조회/다운로드, 길이·크기 검사
    Engine-->>Tauri: JSON 결과(stdout) (ok / error)
    Tauri->>Tauri: stdout 파싱, 파일 메타확인, LocalAudio 생성·저장
    Tauri-->>Lib: ProjectBootstrapSummaryPayload 또는 오류 문자열
    Lib-->>App: 성공/실패 응답
    App-->>User: 소스 렌더링 또는 에러 표시
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
URL 한 줌 건네면 토끼가 살짝 검사해요,
규칙 지켜 소리만 건져 새 보관함에 담을게요,
막히면 이유 말해주고 다른 길을 추천할게요,
토끼가 깡충, 소리 축제에 초대합니다 🎶

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 주요 변경사항(정책 제약 YouTube import 구현)을 명확하고 간결하게 요약하며, 이슈 번호도 포함합니다.
Linked Issues check ✅ Passed PR은 이슈 #30의 모든 주요 요구사항을 충족합니다: 명시적 URL 검증(https, 허용 호스트), unsupported case 안전 실패, 로컬 오디오 fallback 지원, 정책 제약 사항(50MB, 15분 제한), 포괄적인 Python/React 테스트 추가.
Out of Scope Changes check ✅ Passed 모든 코드 변경사항이 이슈 #30 범위 내에 있습니다: YouTube import 기능, 관련 UI/테스트, 공급망 메타데이터, 로컬라이제이션 추가로 이루어지며 불필요한 변경은 없습니다.
Description check ✅ Passed PR 설명은 변경 사항과 관련성 있으며, YouTube URL 검증부터 UI 통합까지 전체 구현 흐름을 명확하게 설명합니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-30-youtube-import

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

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

Inline comments:
In `@apps/desktop/src-tauri/src/main.rs`:
- Around line 767-771: The blocking Command::new(...).output() call inside
start_analysis_job blocks the Tauri event loop and lacks a timeout; replace this
synchronous .output() usage with non-blocking handling (either make
start_analysis_job async with #[tauri::command(async)] and await a tokio/async
process, or spawn a dedicated thread via thread::spawn and use channels to
notify the main thread) and enforce a timeout similar to
ANALYSIS_PROCESS_TIMEOUT; specifically, change the code path that currently
invokes Command::new(program).args(args).current_dir(working_dir).output() to
spawn the child process (use Command::spawn / stdout/stderr piping or an async
process API), poll or await its completion with a deadline, kill the child on
timeout, and return an appropriate error message so run_analysis_engine and
callers are not blocked indefinitely.
- Around line 742-744: The URL check is vulnerable because it only inspects
substrings of the input (the variable url) which allows hosts like
"evil.com/youtube.com/..." to pass; replace this heuristic in the validation
(the block using url.starts_with and url.contains) with proper URL parsing
(e.g., using url::Url::parse) and then validate the scheme is "https" and the
host is exactly one of the allowed hosts ("youtube.com", "www.youtube.com",
"youtu.be", and any other official YouTube host you accept), plus any
path-specific checks you need for youtu.be short links; ensure you check
host_str() equality rather than substring containment so malicious domains
cannot bypass the check.

In `@apps/desktop/src/App.tsx`:
- Around line 167-184: Replace hardcoded UI strings in the YouTube input/button
block with i18n keys using the existing translation function (e.g., t).
Specifically update the placeholder "YouTube URL...", the button label when
isImporting ("Importing...") and the default button label ("Import YouTube") to
use t('...') keys; keep the existing props and logic around youtubeUrl,
setYoutubeUrl, handleImportYoutube, isImporting, analysisInFlight and isStarting
intact. Add corresponding translation keys to the locale files and ensure the
component imports/uses the translation hook/provider already used elsewhere in
the app.

In `@services/analysis-engine/src/bandscope_analysis/youtube.py`:
- Around line 26-30: The current string containment checks allow host-poisoning
like "https://evil.com/youtube.com/"; update the URL validation to parse the URL
(e.g., with urllib.parse.urlparse) instead of using substring checks: ensure
parsed.scheme == "https", extract and normalize parsed.netloc (lowercase, strip
optional port), then validate the host is exactly "youtu.be" or "youtube.com" or
endswith ".youtube.com"; return False for anything else. Replace the existing
substring checks around the url variable with this host-based validation so the
function (the block using url.startswith and the "youtube.com"/"youtu.be"
checks) only accepts legitimate YouTube hosts.
- Around line 53-78: The code currently downloads audio via yt_dlp (see ydl_opts
and the with yt_dlp.YoutubeDL(...) block, variable info and actual_filepath) but
does not enforce the PR-specified limits; add checks after info/actual_filepath
are available: validate info.get("duration") <= 15*60 seconds and check the
downloaded file size via os.path.getsize(actual_filepath) <= 50*1024*1024 bytes,
and if either limit is exceeded delete the downloaded file and return a failure
result (e.g., {"ok": False, "reason": "duration_exceeded" / "size_exceeded"});
ensure these checks are applied before returning the success metadata from the
function.

In `@services/analysis-engine/tests/test_youtube.py`:
- Around line 108-126: The test may import a cached bandscope_analysis.youtube
before patches are applied; update test_main_block to ensure the module is
reloaded after setting patches by using importlib.reload on the
bandscope_analysis.youtube module (after patch.object(sys, "exit") and patching
download_youtube_audio) so the patched objects are used by
bandscope_analysis.youtube.main; specifically, reference test_main_block, the
mock_download patch for download_youtube_audio, the patch.object(sys, "exit")
and the bandscope_analysis.youtube.main invocation and call
importlib.reload(bandscope_analysis.youtube) before calling main(), and repeat
reload before the failure case to guarantee test isolation.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 06395d4d-ae6f-42b2-bbae-a5c3a720ed90

📥 Commits

Reviewing files that changed from the base of the PR and between 60bee14 and 73ab892.

⛔ Files ignored due to path filters (1)
  • services/analysis-engine/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • apps/desktop/src-tauri/src/main.rs
  • apps/desktop/src/App.test.tsx
  • apps/desktop/src/App.tsx
  • apps/desktop/src/lib/analysis.ts
  • services/analysis-engine/pyproject.toml
  • services/analysis-engine/src/bandscope_analysis/youtube.py
  • services/analysis-engine/tests/test_youtube.py
  • supply-chain/supplemental-component-inventory.json

Comment thread apps/desktop/src-tauri/src/main.rs Outdated
Comment thread apps/desktop/src-tauri/src/main.rs Outdated
Comment thread apps/desktop/src/App.tsx
Comment thread services/analysis-engine/src/bandscope_analysis/youtube.py Outdated
Comment thread services/analysis-engine/src/bandscope_analysis/youtube.py Outdated
- Add url crate for robust URL validation in Tauri.

- Wrap synchronous CLI execution in spawn_blocking in Tauri.

- Refactor YouTube URL validation in Python engine to limit URL length and handle unknown exceptions.

- Ensure 100% test coverage for YouTube module.

- Fix hardcoded UI strings by migrating to i18n locale files.

- Resolve Ruff line-length formatting issues.
@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review I have addressed the review comments.

@seonghobae
seonghobae enabled auto-merge (squash) March 26, 2026 02:29
@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

@seonghobae: Thank you for the heads-up! I'll perform a fresh review of the PR now, taking into account the addressed comments and the policy-constrained YouTube import implementation.


🧠 Learnings used
Learnt from: CR
Repo: seonghobae/bandscope PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-25T12:08:41.134Z
Learning: Applies to **/*youtube*{.ts,.tsx,.js,.jsx,.py} : Treat YouTube import as policy-constrained and fallback-friendly
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
services/analysis-engine/src/bandscope_analysis/youtube.py (1)

68-84: 🧹 Nitpick | 🔵 Trivial

다운로드 전 duration 검사로 대역폭 낭비를 방지할 수 있습니다.

현재 구현은 download=True로 먼저 다운로드한 후 duration을 검사합니다. yt-dlp는 download=False로 메타데이터만 먼저 추출할 수 있어, 15분 초과 영상의 불필요한 다운로드를 방지할 수 있습니다.

♻️ 메타데이터 사전 검사 제안
     try:
         with yt_dlp.YoutubeDL(ydl_opts) as ydl:
-            info = ydl.extract_info(url, download=True)
+            # First extract metadata without downloading
+            info = ydl.extract_info(url, download=False)
             if info is None:
                 raise Exception("Failed to extract info")
-            actual_filepath = ydl.prepare_filename(info)
             duration = info.get("duration")
             if duration is not None and duration > 15 * 60:
-                if os.path.exists(actual_filepath):
-                    os.remove(actual_filepath)
                 return {
                     "ok": False,
                     "error": {
                         "code": "duration_exceeded",
                         "message": "Video exceeds the 15-minute limit.",
                     },
                 }
+            
+            # Now download after validation
+            info = ydl.extract_info(url, download=True)
+            if info is None:
+                raise Exception("Failed to extract info")
+            actual_filepath = ydl.prepare_filename(info)

             if (
                 os.path.exists(actual_filepath)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/analysis-engine/src/bandscope_analysis/youtube.py` around lines 68 -
84, The current code calls ydl.extract_info(url, download=True) which downloads
before checking duration; change to first call ydl.extract_info(url,
download=False) (using the existing ydl_opts and ydl/YoutubeDL context) to
obtain info and check info.get("duration") against 15*60, and only if within
limit perform the actual download (e.g., call ydl.download([url]) or re-run
extract_info with download=True) to avoid wasted bandwidth; preserve the
existing handling of actual_filepath (derived from ydl.prepare_filename(info)),
removal on duration exceedance, and existing error/return shapes.
apps/desktop/src-tauri/src/main.rs (1)

775-783: ⚠️ Potential issue | 🟠 Major

YouTube 다운로드에 타임아웃이 없습니다.

run_analysis_engineANALYSIS_PROCESS_TIMEOUT (30초)을 사용하지만, YouTube 다운로드는 네트워크 상태에 따라 훨씬 오래 걸릴 수 있습니다. 현재 구현은 무한 대기할 수 있어 리소스가 고갈될 위험이 있습니다.

🛡️ 타임아웃 추가 제안
+const YOUTUBE_IMPORT_TIMEOUT: Duration = Duration::from_secs(120); // 2분
+
 #[tauri::command]
 async fn import_youtube_url(
     // ...
 ) -> Result<ProjectBootstrapSummaryPayload, String> {
     // ... validation code ...

-    let output = tauri::async_runtime::spawn_blocking(move || {
+    let output = tokio::time::timeout(
+        YOUTUBE_IMPORT_TIMEOUT,
+        tauri::async_runtime::spawn_blocking(move || {
         Command::new(program)
             .args(args)
             .current_dir(working_dir)
             .output()
-    })
+        })
+    )
     .await
+    .map_err(|_| "YouTube import timed out. Please try again or use a local audio file.".to_string())?
     .map_err(|_| "Failed to execute YouTube import process.".to_string())?
     .map_err(|_| "Failed to start YouTube import process.".to_string())?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src-tauri/src/main.rs` around lines 775 - 783, The YouTube
download command spawned in run_analysis_engine currently blocks indefinitely
(the closure with
Command::new(program).args(args).current_dir(working_dir).output()), so wrap the
process execution with a timeout using ANALYSIS_PROCESS_TIMEOUT: convert the
blocking .output() invocation into a cancellable flow (use
tokio::time::timeout(Duration::from_secs(ANALYSIS_PROCESS_TIMEOUT), ...) or
spawn the child and await it with timeout), and on timeout ensure you kill the
spawned child process and return a clear timeout error (map_err) instead of
hanging; update the error mapping that currently follows the
tauri::async_runtime::spawn_blocking call to handle the timeout case and include
proper cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src-tauri/src/main.rs`:
- Around line 797-802: The file_name is built directly from title via
format!("{}.{}", title, extension) which allows directory traversal and invalid
characters; update the code that constructs LocalAudioSourcePayload
(specifically the file_name field creation) to sanitize the title: remove or
replace path separators (/, \), dots that form parent references, and other
filesystem-invalid/control characters, truncate to a safe max length, and fall
back to a deterministic safe name if empty; then compose file_name from the
sanitized title + extension and use that value in LocalAudioSourcePayload so
source_path and file_name cannot be manipulated by unsafe title content.

In `@services/analysis-engine/tests/test_youtube.py`:
- Around line 13-20: Add test cases in test_validate_url to cover YouTube
subdomains: call validate_url with URLs like "https://m.youtube.com/watch?v=123"
and "https://music.youtube.com/watch?v=123" and assert they return True (and
optionally a non-YouTube subdomain like "https://evil.youtube.com/watch?v=123"
if your validation should reject unknown subdomains); update the test function
test_validate_url to include these assertions so the validate_url behavior for
*.youtube.com subdomains is covered.

---

Duplicate comments:
In `@apps/desktop/src-tauri/src/main.rs`:
- Around line 775-783: The YouTube download command spawned in
run_analysis_engine currently blocks indefinitely (the closure with
Command::new(program).args(args).current_dir(working_dir).output()), so wrap the
process execution with a timeout using ANALYSIS_PROCESS_TIMEOUT: convert the
blocking .output() invocation into a cancellable flow (use
tokio::time::timeout(Duration::from_secs(ANALYSIS_PROCESS_TIMEOUT), ...) or
spawn the child and await it with timeout), and on timeout ensure you kill the
spawned child process and return a clear timeout error (map_err) instead of
hanging; update the error mapping that currently follows the
tauri::async_runtime::spawn_blocking call to handle the timeout case and include
proper cleanup.

In `@services/analysis-engine/src/bandscope_analysis/youtube.py`:
- Around line 68-84: The current code calls ydl.extract_info(url, download=True)
which downloads before checking duration; change to first call
ydl.extract_info(url, download=False) (using the existing ydl_opts and
ydl/YoutubeDL context) to obtain info and check info.get("duration") against
15*60, and only if within limit perform the actual download (e.g., call
ydl.download([url]) or re-run extract_info with download=True) to avoid wasted
bandwidth; preserve the existing handling of actual_filepath (derived from
ydl.prepare_filename(info)), removal on duration exceedance, and existing
error/return shapes.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8d8f52d7-78f9-4d3b-a5a8-d628c2a16817

📥 Commits

Reviewing files that changed from the base of the PR and between 73ab892 and 0b105f8.

⛔ Files ignored due to path filters (1)
  • apps/desktop/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • apps/desktop/src-tauri/Cargo.toml
  • apps/desktop/src-tauri/src/main.rs
  • apps/desktop/src/App.tsx
  • apps/desktop/src/locales/en/common.json
  • apps/desktop/src/locales/ko/common.json
  • services/analysis-engine/src/bandscope_analysis/youtube.py
  • services/analysis-engine/tests/test_youtube.py

Comment thread apps/desktop/src-tauri/src/main.rs
Adds 120s timeout and filename sanitization to rust main.rs. Fixes youtube.py duration checking by using two-pass extract_info logic. Adds missing test cases for 100% test coverage.
@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
apps/desktop/src-tauri/src/main.rs (1)

803-812: ⚠️ Potential issue | 🟡 Minor

파일명 새니타이징이 Windows 불법 문자를 완전히 처리하지 않습니다.

현재 구현은 control chars, /, \, .만 필터링합니다. Windows에서 불법인 :, *, ?, ", <, >, | 문자가 누락되어 Windows 환경에서 파일 생성 오류가 발생할 수 있습니다.

🛡️ Windows 호환 새니타이징 제안
 let safe_title: String = title
     .chars()
-    .filter(|c| !c.is_control() && *c != '/' && *c != '\\' && *c != '.')
+    .map(|c| match c {
+        '/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' => '_',
+        '.' => '_',  // prevent path-like patterns
+        c if c.is_control() => '_',
+        c => c,
+    })
     .take(100)
     .collect();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src-tauri/src/main.rs` around lines 803 - 812, The filename
sanitization building safe_title currently only filters control chars plus '/',
'\', and '.'; update the sanitization logic that creates safe_title (the block
using title.chars().filter(...).take(100).collect()) to also remove the
Windows-reserved characters ':', '*', '?', '"', '<', '>', and '|' (or replace
them with a safe character like '_'), and keep the existing max-length and
empty-string fallback ("youtube_audio") behavior; ensure the variable name
safe_title and the same take(100) truncation are preserved so callers remain
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@services/analysis-engine/src/bandscope_analysis/youtube.py`:
- Around line 88-99: The current branch only handles the case where
actual_filepath exists and is too large but does not handle the case where the
file is missing after download; update the post-download logic in the function
that writes/returns actual_filepath (look for the block using actual_filepath
and os.path.getsize) to explicitly check that os.path.exists(actual_filepath) is
True before returning success and, if not, return a failure dict (e.g., code
"missing_file" with a clear message) so the caller/Rust side (which uses
std::fs::metadata) sees a consistent error; ensure this defensive existence
check occurs prior to any size checks or successful return.
- Around line 57-66: ydl_opts currently contains the unsupported key
"extract_audio": True; replace this with a "postprocessors" entry that uses the
FFmpegExtractAudio postprocessor (configure "key": "FFmpegExtractAudio", and set
"preferredcodec" and "preferredquality") so yt_dlp's Python API will perform
audio extraction; update the ydl_opts dictionary used in download logic
(reference: ydl_opts) and ensure ffmpeg is available at runtime.

In `@services/analysis-engine/tests/test_youtube.py`:
- Around line 31-51: The test test_download_youtube_audio_success currently
doesn't mock filesystem checks so the file-size branch inside
download_youtube_audio isn't exercised; update the test to patch os.path.exists
and os.path.getsize in the module where download_youtube_audio is defined (e.g.,
patch "bandscope_analysis.youtube.os.path.exists" and
"bandscope_analysis.youtube.os.path.getsize") to return True and a size under
the max threshold (e.g., <50MB) respectively, so the code path that checks file
existence and size is executed and the metadata["filepath"]/size logic is
validated.

---

Duplicate comments:
In `@apps/desktop/src-tauri/src/main.rs`:
- Around line 803-812: The filename sanitization building safe_title currently
only filters control chars plus '/', '\', and '.'; update the sanitization logic
that creates safe_title (the block using
title.chars().filter(...).take(100).collect()) to also remove the
Windows-reserved characters ':', '*', '?', '"', '<', '>', and '|' (or replace
them with a safe character like '_'), and keep the existing max-length and
empty-string fallback ("youtube_audio") behavior; ensure the variable name
safe_title and the same take(100) truncation are preserved so callers remain
unchanged.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d51ba850-3ce2-434b-a4db-6f972111beab

📥 Commits

Reviewing files that changed from the base of the PR and between 0b105f8 and 9598b4d.

📒 Files selected for processing (3)
  • apps/desktop/src-tauri/src/main.rs
  • services/analysis-engine/src/bandscope_analysis/youtube.py
  • services/analysis-engine/tests/test_youtube.py

Comment thread services/analysis-engine/src/bandscope_analysis/youtube.py Outdated
Comment thread services/analysis-engine/src/bandscope_analysis/youtube.py
Comment thread services/analysis-engine/tests/test_youtube.py Outdated
- Replace extract_audio with postprocessors.FFmpegExtractAudio
- Add fallback extension checking when yt-dlp transcodes audio
- Add 100% test coverage for file not found and extension changes
@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@services/analysis-engine/tests/test_youtube.py`:
- Around line 177-194: The test test_download_youtube_audio_duration_exceeded
patches three filesystem-related mocks (mock_remove, mock_exists, mock_getsize)
that are never used because download_youtube_audio rejects by duration before
touching the filesystem; remove the unused `@patch` decorators and the
corresponding parameters from the test signature so only
`@patch`("bandscope_analysis.youtube.yt_dlp.YoutubeDL") remains and the test
function only accepts mock_ydl_class (and keep the existing setup of
mock_ydl_class.return_value.__enter__.return_value and extract_info return
value).
- Around line 243-268: The test_module_execution test patches os.path on the
bandscope_analysis.youtube module which won't affect runpy.run_path because
runpy creates a fresh module namespace; instead, mock the os module at the
sys.modules level or patch the global os.path functions: replace the current
patch("bandscope_analysis.youtube.os.path.exists") /
patch("bandscope_analysis.youtube.os.path.getsize") approach by either inserting
a mock_os into sys.modules via monkeypatch.setitem(sys.modules, "os", mock_os)
(ensuring mock_os.path.exists and mock_os.path.getsize return the desired
values) or patching the global functions with patch("os.path.exists") and
patch("os.path.getsize") before calling runpy.run_path in test_module_execution
so the run-time import inside runpy sees the mocked functions.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e0f76bba-a1dc-41b8-aead-c8d69cc30581

📥 Commits

Reviewing files that changed from the base of the PR and between 9598b4d and c2bf52a.

📒 Files selected for processing (2)
  • services/analysis-engine/src/bandscope_analysis/youtube.py
  • services/analysis-engine/tests/test_youtube.py

Comment thread services/analysis-engine/tests/test_youtube.py Outdated
Comment thread services/analysis-engine/tests/test_youtube.py Outdated
@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@apps/desktop/src-tauri/src/main.rs`:
- Around line 795-836: When parsed.get("ok") is Some(true) but metadata is
missing, return a clear, specific error instead of falling through; update the
block that checks parsed.get("ok") and then attempts to unwrap metadata (the
branch that currently looks up metadata, filepath, title, builds
LocalAudioSourcePayload and ProjectBootstrapSummaryPayload and calls
store_bootstrap_source) to detect metadata.is_none() and immediately return
Err(...) with a descriptive message like "YouTube import reported ok but missing
metadata" (include any available context such as parsed value), so callers don’t
hit the generic "YouTube import failed with an unknown error." later.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 78001590-9f88-452f-bddc-c121b0a2de17

📥 Commits

Reviewing files that changed from the base of the PR and between c2bf52a and 07bcfa5.

📒 Files selected for processing (2)
  • apps/desktop/src-tauri/src/main.rs
  • services/analysis-engine/tests/test_youtube.py

Comment thread apps/desktop/src-tauri/src/main.rs
@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
apps/desktop/src-tauri/src/main.rs (2)

776-789: ⚠️ Potential issue | 🟠 Major

타임아웃이 자식 프로세스를 실제로 멈추지 않습니다.

Line 776의 timeout(...)JoinHandle 대기만 끊습니다. 이미 시작된 spawn_blocking 내부의 Command::output()는 계속 돌기 때문에, 호출자는 타임아웃을 받았어도 yt-dlp 다운로드와 파일 쓰기가 백그라운드에 남습니다. Child를 직접 spawn()해서 deadline 초과 시 kill()/wait()까지 보장해야 합니다.

⏱️ 제안
-    let spawn_result = tokio::time::timeout(
-        YOUTUBE_IMPORT_TIMEOUT,
-        tauri::async_runtime::spawn_blocking(move || {
-            Command::new(program)
-                .args(args)
-                .current_dir(working_dir)
-                .output()
-        })
-    ).await;
-
-    let output = spawn_result
-        .map_err(|_| "YouTube import timed out.".to_string())?
-        .map_err(|_| "Failed to execute YouTube import process.".to_string())?
-        .map_err(|_| "Failed to start YouTube import process.".to_string())?;
+    let mut child = Command::new(program)
+        .args(args)
+        .current_dir(working_dir)
+        .stdout(Stdio::piped())
+        .stderr(Stdio::piped())
+        .spawn()
+        .map_err(|_| "Failed to start YouTube import process.".to_string())?;
+
+    let deadline = Instant::now() + YOUTUBE_IMPORT_TIMEOUT;
+    loop {
+        match child.try_wait() {
+            Ok(Some(_)) => break,
+            Ok(None) if Instant::now() >= deadline => {
+                let _ = child.kill();
+                let _ = child.wait();
+                return Err("YouTube import timed out.".to_string());
+            }
+            Ok(None) => tokio::time::sleep(ANALYSIS_WAIT_POLL).await,
+            Err(_) => {
+                let _ = child.kill();
+                let _ = child.wait();
+                return Err("Failed to execute YouTube import process.".to_string());
+            }
+        }
+    }
+
+    let output = child
+        .wait_with_output()
+        .map_err(|_| "Failed to execute YouTube import process.".to_string())?;
Does `tokio::time::timeout` cancel an already-started `spawn_blocking` task, and if not, does a `std::process::Command::output()` child process continue running after the timeout future resolves?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src-tauri/src/main.rs` around lines 776 - 789, The timeout
currently wraps tauri::async_runtime::spawn_blocking(...) which only cancels
waiting on the JoinHandle while the underlying Command::output() child keeps
running; change the logic in the YOUTUBE import sequence to spawn the child
process with Command::new(program).args(args).current_dir(working_dir).spawn()
so you obtain a std::process::Child, then await the child's completion with
tokio::time::timeout(YOUTUBE_IMPORT_TIMEOUT, async { child.wait_with_output() })
(or use a blocking-to-async bridge), and on timeout call child.kill() and
child.wait() to ensure the process is terminated and reaped; remove the current
use of spawn_blocking + Command::output() and update the error handling around
the former spawn_result/Command::... calls accordingly.

743-753: ⚠️ Potential issue | 🟠 Major

호스트만 검사하면 정책 allowlist가 너무 넓습니다.

Line 751은 https://www.youtube.com/, /feed/history, /account처럼 실제 import 대상이 아닌 페이지도 모두 통과시킵니다. 이 경계에서는 host뿐 아니라 지원하는 path/query 조합(watch?v=..., youtu.be/<id>, 필요 시 shorts/live)까지 명시적으로 좁혀서 fail-fast 해야 합니다.

🛡️ 제안
-    if host != "youtu.be" && host != "youtube.com" && !host.ends_with(".youtube.com") {
+    let supported = match host.as_str() {
+        "youtu.be" => parsed_url
+            .path_segments()
+            .and_then(|mut segments| segments.next())
+            .is_some_and(|id| !id.is_empty()),
+        "youtube.com" | "www.youtube.com" | "m.youtube.com" | "music.youtube.com" => {
+            match parsed_url.path() {
+                "/watch" => parsed_url.query_pairs().any(|(k, v)| k == "v" && !v.is_empty()),
+                path if path.starts_with("/shorts/") || path.starts_with("/live/") => true,
+                _ => false,
+            }
+        }
+        _ => false,
+    };
+    if !supported {
         return Err("Only standard YouTube URLs are supported.".to_string());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src-tauri/src/main.rs` around lines 743 - 753, The current URL
validation (using parsed_url, scheme, host) allows many non-video YouTube pages;
tighten validation by explicitly allowing only supported path/query patterns:
for youtube.com require path "/watch" with a non-empty "v" query parameter, or
paths starting with "/shorts/" or "/live" as needed; for youtu.be require a
single-segment path (the video id). Implement these checks after parsed_url and
host are validated (using parsed_url.path(), parsed_url.path_segments(), and
parsed_url.query_pairs()) and return the same Err("Only standard YouTube URLs
are supported.".to_string()) for any unsupported path/query combinations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src-tauri/src/main.rs`:
- Around line 795-823: The code trusts the raw stdout "filepath" and stores it
into LocalAudioSourcePayload.source_path, which allows referencing files outside
the intended workspace; fix by canonicalizing the reported filepath (use
Path::new(filepath).canonicalize()) and canonicalizing the application's
expected download root, then verify the canonicalized file path starts_with the
canonicalized download root (reject with an error if not). Use the canonicalized
path for subsequent std::fs::metadata calls and set
LocalAudioSourcePayload.source_path to the canonicalized string; keep
extension/safe_title logic unchanged but ensure you return an error instead of
accepting paths outside the confined root.

In `@services/analysis-engine/tests/test_youtube.py`:
- Around line 213-232: The test test_main_block in
services/analysis-engine/tests/test_youtube.py only asserts exit codes and
ignores main()'s stdout JSON contract; update the test to capture stdout via
capsys after calling bandscope_analysis.youtube.main(), parse the printed JSON
(e.g., json.loads(capsys.readouterr().out)), and assert the returned
structure/fields when mock_download returns {"ok": True, "metadata": {"id":
"123"}} and when it returns {"ok": False} (assert expected error shape); apply
the same stdout-capturing and JSON-parsing assertions to the other failing test
block referenced (lines ~235-260) so both tests validate printed JSON as well as
exit codes and reference the bandscope_analysis.youtube.main and
download_youtube_audio mocks when making assertions.
- Around line 13-22: Add negative test cases to ensure validate_url rejects
non-video YouTube paths: update test_validate_url to assert False for URLs like
"https://www.youtube.com/", "https://www.youtube.com/feed/history",
"https://www.youtube.com/watch" (no v param), and any other YouTube URLs lacking
a video id; ensure the validate_url implementation enforces allowed path/query
patterns (e.g., requires a valid "v" query or a youtu.be path segment) rather
than relying only on hostname checks so these cases fail as expected.

---

Duplicate comments:
In `@apps/desktop/src-tauri/src/main.rs`:
- Around line 776-789: The timeout currently wraps
tauri::async_runtime::spawn_blocking(...) which only cancels waiting on the
JoinHandle while the underlying Command::output() child keeps running; change
the logic in the YOUTUBE import sequence to spawn the child process with
Command::new(program).args(args).current_dir(working_dir).spawn() so you obtain
a std::process::Child, then await the child's completion with
tokio::time::timeout(YOUTUBE_IMPORT_TIMEOUT, async { child.wait_with_output() })
(or use a blocking-to-async bridge), and on timeout call child.kill() and
child.wait() to ensure the process is terminated and reaped; remove the current
use of spawn_blocking + Command::output() and update the error handling around
the former spawn_result/Command::... calls accordingly.
- Around line 743-753: The current URL validation (using parsed_url, scheme,
host) allows many non-video YouTube pages; tighten validation by explicitly
allowing only supported path/query patterns: for youtube.com require path
"/watch" with a non-empty "v" query parameter, or paths starting with "/shorts/"
or "/live" as needed; for youtu.be require a single-segment path (the video id).
Implement these checks after parsed_url and host are validated (using
parsed_url.path(), parsed_url.path_segments(), and parsed_url.query_pairs()) and
return the same Err("Only standard YouTube URLs are supported.".to_string()) for
any unsupported path/query combinations.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2c1bc002-3b94-4e32-883a-5315e7718b85

📥 Commits

Reviewing files that changed from the base of the PR and between c2bf52a and a3386df.

⛔ Files ignored due to path filters (1)
  • apps/desktop/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • apps/desktop/src-tauri/Cargo.toml
  • apps/desktop/src-tauri/src/main.rs
  • services/analysis-engine/tests/test_youtube.py

Comment thread apps/desktop/src-tauri/src/main.rs
Comment thread services/analysis-engine/tests/test_youtube.py
Comment thread services/analysis-engine/tests/test_youtube.py
…ube-import

# Conflicts:
#	apps/desktop/src-tauri/src/main.rs
#	apps/desktop/src/App.test.tsx
#	apps/desktop/src/App.tsx
#	apps/desktop/src/lib/analysis.ts
@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review I have addressed the review comments.

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

@seonghobae: Thank you for addressing the review comments! I'll kick off a fresh review of the PR now, covering the policy-constrained YouTube import implementation across all changed files.


🧠 Learnings used
Learnt from: CR
Repo: seonghobae/bandscope PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-25T12:08:41.134Z
Learning: Applies to **/*youtube*{.ts,.tsx,.js,.jsx,.py} : Treat YouTube import as policy-constrained and fallback-friendly
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resolve

@seonghobae

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

1 similar comment
@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Comments resolved and changes approved.

@seonghobae
seonghobae merged commit 51c63c0 into develop Mar 26, 2026
20 of 21 checks passed
@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Comments resolved and changes approved.

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Level 4] 정책 제약을 지키는 YouTube import 구현

1 participant