feat(#292): YouTube wallpaper を最高画質DL + HEVCトランスコードで全Mac再生対応#293
Conversation
player_client を android→default(web) に切替え、SABR で 360p に潰されていた 動画取得を全解像度の https DASH に戻す。ffmpeg+ffprobe があれば最高画質 (4K VP9/AV1) を取得し、ffprobe でコーデック判定して AVC/HEVC は stream-copy、 AVFoundation が再生できない AV1/VP9 は hevc_videotoolbox で HEVC へ ハードウェアトランスコード。ツールチェーン未導入時は再生可能な AVC(1080p) 天井へフォールバック。processRunner を (status, stdout, stderr) に拡張 (コーデック判定に ffprobe の stdout が必要)。
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthrough
ChangesYouTube Codec-Aware Download Pipeline
Sequence Diagram(s)sequenceDiagram
participant resolve as resolve(_:)
participant tools as Tool Detection
participant yt as yt-dlp (processRunner)
participant ffprobe as ffprobe (processRunner)
participant ffmpeg as ffmpeg (processRunner)
resolve->>tools: which ffmpeg / ffprobe
tools-->>resolve: paths or nil
alt ffmpeg + ffprobe available
resolve->>yt: buildArgs(allowAnyCodec: true)
yt-->>resolve: (status, stdout, stderr)
resolve->>ffprobe: probe codec of downloaded file
ffprobe-->>resolve: codec name (e.g. av1, vp9, h264, hevc)
alt codec requires transcode (AV1 / VP9)
resolve->>ffmpeg: transcodeArguments → HEVC
ffmpeg-->>resolve: (status, stdout, stderr)
else codec is copy-compatible (AVC / HEVC)
resolve->>ffmpeg: remuxArguments → faststart MP4
ffmpeg-->>resolve: (status, stdout, stderr)
end
else tools absent
resolve->>yt: buildArgs(allowAnyCodec: false, maxHeight: 1080)
yt-->>resolve: (status, stdout, stderr)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
YouTube wallpaper の「ときどき 360p になる」問題を解消しつつ、VP9/AV1 の高解像度ソースを HEVC へ変換して全 Mac で再生可能にするための、YouTube ダウンロード〜再生正規化パイプライン改善です(WallpaperDataSource 層の責務として、取得した動画を AVFoundation で再生可能な形式へ寄せる変更)。
Changes:
- yt-dlp の
player_clientをandroid→defaultに切り替え、SABR 由来の video-only 全スキップ(360p 固定化)を回避 ffprobeでコーデック判定し、AV1/VP9 はhevc_videotoolboxで HEVC へ HW トランスコード、AVC/HEVC は stream-copy(remux)で正規化processRunnerを(status, stdout, stderr)に拡張し、テスト・README・設計ドキュメントを更新、バージョンを 2.16.0 に bump
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift |
player_client 切替、format selector の分岐、ffprobe 判定 + HEVC 正規化、process 実行の stdout/stderr キャプチャ対応 |
Tests/WallpaperDataSourceTests/YouTubeWallpaperResolveTests.swift |
AV1→HEVC transcode / AVC copy / transcode 失敗など新フローの分岐テストを追加・更新 |
Tests/WallpaperDataSourceTests/YouTubeToolDetectionTests.swift |
buildArgs(..., allowAnyCodec:) 追加に伴うテスト更新 |
README.md |
YouTube 要件(yt-dlp/uvx/ffmpeg/ffprobe)と挙動の説明を更新 |
Sources/VersionHandler/Resources/version.txt |
2.15.1 → 2.16.0(feat による minor bump) |
.claude/CLAUDE.md |
YouTube 最高画質 + 互換性の設計判断を「Key Design Decisions」へ追記 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let stdoutPipe = Pipe() | ||
| let stderrPipe = Pipe() | ||
| process.standardOutput = stdoutPipe | ||
| process.standardError = stderrPipe | ||
|
|
There was a problem hiding this comment.
5ad7bac でしました。stdout/stderr の両パイプを
DispatchGroup 配下の別スレッドで並行ドレインするよう書き直し、OS パイプバッファ(約 64 KB)の枯渇によるデッドロックをしています。
group.notify 内で process.waitUntilExit() を先に呼ぶことで、NSTask の SIGCHLD 処理が完了してから terminationStatus を読むようにしました。
| private static func trimmedString(from pipe: Pipe) -> String { | ||
| let data = pipe.fileHandleForReading.readDataToEndOfFile() | ||
| return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" | ||
| } |
There was a problem hiding this comment.
Finding #1 の executeProcess 書き直しに伴い trimmedString(from: Pipe) をしました。trim ロジックは新しい
PipeBuffer クラスの trimmed(_:) メソッドに移動しています。
| | `yt-dlp` | `brew install yt-dlp` | Preferred. Downloads the highest-quality video-only stream, up to 4K | | ||
| | `uvx` | `brew install uv` | Zero-install alternative — runs `uvx yt-dlp` without global install | | ||
| | `ffmpeg` | `brew install ffmpeg` | Required for auto-loop. Remuxes DASH container to standard MP4 | | ||
|
|
||
| If neither `yt-dlp` nor `uvx` is found, lyra will show an error. If `ffmpeg` is not found, the video plays but may not loop automatically. | ||
| | `ffmpeg` + `ffprobe` | `brew install ffmpeg` | Unlocks 4K. Remuxes DASH to MP4 and transcodes AV1/VP9 to HEVC so any Mac can play it | | ||
|
|
||
| If neither `yt-dlp` nor `uvx` is found, lyra will show an error. The maximum |
There was a problem hiding this comment.
5ad7bac でしました。requirements テーブルを
ffmpeg と ffprobe の 2 行に分離し、それぞれの役割をに記載しています:
ffmpeg 単体は DASH→MP4 リムックスでループ再生を有効化(H.264 / 1080p 上限)、ffprobe(brew install ffmpeg に同梱)を加えると 4K コーデック判定と HEVC トランスコードが使えます。
… README toolchain rows
| let codec = await videoCodec(at: path, ffprobe: ffprobe) | ||
| let needsTranscode = codec.map(Self.requiresTranscode) ?? false | ||
| let tmpPath = path + ".normalized.mp4" |
There was a problem hiding this comment.
9ef432f でしました。ご指摘の通り、
allowAnyCodec で
ffprobe が検出失敗すると AV1/VP9 が stream-copy のまま残り、pre-M3 / Intel で再生不可になる穴がありました。
判定を needsTranscode(at:ffprobe:) ヘルパーにし、
ffprobe が期待されていた(非 nil)のに probe が失敗した場合は保守的に transcode へフォールバックするようにしました(codec.map(requiresTranscode) ?? true)。ffprobe のパスは AVC のみ DL するので従来通り stream-copy のままです。
これにより無用な再生不可ファイルのを防止します。probe
時の挙動を直接カバーする単体テスト(
resolve レベル + needsTranscode 単体)も追加済みです。
Closes #292
背景
YouTube wallpaper の画質がときどき極端に悪い(360p になる)バグの修正。原因は yt-dlp の
player_client=androidが YouTube の SABR ストリーミングで video-only フォーマットを全スキップされ、結合済みの format 18(360p)しか取得できなくなっていたこと。実機調査で player_client の前提が逆転していたことを確定させた(→ 詳細な実機検証は #292 のfindings コメント)。変更内容
android→default(web)へ切替 — PO Token 不要で全解像度・全コーデックの https DASH を取得できる唯一のクライアント。ffmpegかつffprobeがある → 最高解像度(4K VP9/AV1)を取得 →ffprobeでコーデック判定 → AVC/HEVC は-c copy(安価)、AV1/VP9 はhevc_videotoolboxで HEVC へ HW トランスコード(hvc1 タグ)。変換は wallpaper ごと初回1回のみ(SHA256 キャッシュ後は不要)。processRunnerを(status, stdout, stderr)に拡張(コーデック判定にffprobeの stdout が必要)。--no-progress追加でダウンロードログのノイズを抑制。実機検証エビデンス
コーデック互換パイプラインは実機(macOS 26.4.1 / Apple M1 Max)で検証済み。
AVFoundation 再生可否マトリクス(M1 Max)
av01 4K → hevc_videotoolbox変換は 3:39 の動画を約53秒(4.16x速)。全 Mac で本物の 4K wallpaper が再生できる。テスト
WallpaperDataSourceTestsを新ロジックに合わせて更新・拡充(全972テスト green):transcodeFailedエラー、トランスコード可否によるフォーマットセレクタ分岐requiresTranscode/remuxArguments/transcodeArguments/videoCodec(ffprobe 不在時 nil)の単体テストexecuteProcessの stdout/stderr 同時キャプチャ純粋関数とインジェクト可能クロージャで構成し、実プロセスを起動せず分岐を網羅(カバレッジ維持)。
キャッシュサイズについて
HEVC トランスコードはあえて品質上限を設けず、
hevc_videotoolboxのデフォルト(4K で ~24 Mbps)に任せている。ウルトラワイドディスプレイでは wallpaper の中央のみが過度に拡大されるため、ソース品質が高いほど有利なケースがあり、issue の「最高画質」方針とも一致する。トレードオフとして 4K wallpaper 1 本あたり数百 MiB が永続キャッシュに残る。将来、transcode 品質を config で可変にする余地はあるが本 PR の範囲外。E2E 検証
コードが生成する正確な引数列を実 YouTube URL(4K AV1 ソース)に対して実行し、実機(M1 Max / macOS 26.4.1)で確認:
DL 成功(4K AV1 取得)→ ffprobe 判定(av1)→ HEVC 変換 → AVFoundation 再生可、を一気通貫で実証。transcode を挟まないと AV1 は再生不可なので変換が必須であることも裏取り済み。
動作要件
yt-dlp/uvxffmpeg+ffprobeSummary by CodeRabbit