Skip to content

feat(#292): YouTube wallpaper を最高画質DL + HEVCトランスコードで全Mac再生対応#293

Merged
GeneralD merged 5 commits into
mainfrom
feat/292/youtube-highest-quality
Jun 16, 2026
Merged

feat(#292): YouTube wallpaper を最高画質DL + HEVCトランスコードで全Mac再生対応#293
GeneralD merged 5 commits into
mainfrom
feat/292/youtube-highest-quality

Conversation

@GeneralD

@GeneralD GeneralD commented Jun 16, 2026

Copy link
Copy Markdown
Owner

type scope breaking diff files tests review

Closes #292

背景

YouTube wallpaper の画質がときどき極端に悪い(360p になる)バグの修正。原因は yt-dlp の player_client=android が YouTube の SABR ストリーミングで video-only フォーマットを全スキップされ、結合済みの format 18(360p)しか取得できなくなっていたこと。実機調査で player_client の前提が逆転していたことを確定させた(→ 詳細な実機検証は #292 のfindings コメント)。

変更内容

  • player_client を androiddefault(web)へ切替 — PO Token 不要で全解像度・全コーデックの https DASH を取得できる唯一のクライアント。
  • 最高画質の取得とコーデック互換の両立 — 1080p 超は VP9/AV1 でしか配信されないが、AVFoundation は AV1 を M3 未満の Apple Silicon・Intel で再生できず、VP9/WebM は恒久非対応。そこで取得は「トランスコード可能か」でゲートする:
    • ffmpeg かつ ffprobe がある → 最高解像度(4K VP9/AV1)を取得 → ffprobe でコーデック判定 → AVC/HEVC は -c copy(安価)AV1/VP9 は hevc_videotoolbox で HEVC へ HW トランスコード(hvc1 タグ)。変換は wallpaper ごと初回1回のみ(SHA256 キャッシュ後は不要)。
    • ツールチェーン未導入 → 再生可能な AVC(1080p 天井) へフォールバック(従来挙動)。
  • processRunner(status, stdout, stderr) に拡張(コーデック判定に ffprobe の stdout が必要)。
  • 既存の --no-progress 追加でダウンロードログのノイズを抑制。

実機検証エビデンス

コーデック互換パイプラインは実機(macOS 26.4.1 / Apple M1 Max)で検証済み。

AVFoundation 再生可否マトリクス(M1 Max)
コーデック HW デコード AVFoundation デコード 結論
avc1 (H.264) 1080p ✅ DECODE OK / playable=true 再生可
av01 (AV1) 4K ❌ AV1=false ❌ "Cannot Open" / playable=false 再生不可
vp9 (VP9) 4K ❌ "Cannot Open" 再生不可(WebM/VP9 恒久非対応)
av01 4K → HEVC 変換後 ✅ DECODE OK / playable=true / 3840x2160 再生可

av01 4K → hevc_videotoolbox 変換は 3:39 の動画を約53秒(4.16x速)。全 Mac で本物の 4K wallpaper が再生できる。

テスト

WallpaperDataSourceTests を新ロジックに合わせて更新・拡充(全972テスト green):

  • AV1 検出 → HEVC トランスコード経路、H.264 検出 → stream-copy 経路
  • 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)で確認:

対象 decode-test 結果
transcode 後 HEVC(lyra がキャッシュする物) DECODE OK / codec=hvc1 / 3840×2160 / playable=true
生 AV1 4K(transcode しない場合の M1/M2/Intel) DECODE FAIL / playable=false / "Cannot Open"

DL 成功(4K AV1 取得)→ ffprobe 判定(av1)→ HEVC 変換 → AVFoundation 再生可、を一気通貫で実証。transcode を挟まないと AV1 は再生不可なので変換が必須であることも裏取り済み。

動作要件

Tool 用途
yt-dlp / uvx ダウンロード(必須)
ffmpeg + ffprobe 4K 解放・HEVC トランスコード(無い場合は AVC 1080p 天井)

Summary by CodeRabbit

  • New Features
    • Enhanced YouTube playback with codec-aware stream selection, including optional HEVC transcoding for better compatibility and improved 4K support when the required tools are available
    • Added playback-focused normalization so downloaded wallpapers are more reliably playable
  • Bug Fixes
    • Improved external process handling by capturing both standard output and error output, improving robustness and diagnostics
  • Documentation
    • Updated YouTube installation and playback capability requirements, including remux/transcode guidance and clearer fallback behavior
  • Chores
    • Version bumped to 2.16.0

GeneralD added 3 commits June 17, 2026 00:36
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 が必要)。
Copilot AI review requested due to automatic review settings June 16, 2026 17:10
@GeneralD GeneralD self-assigned this Jun 16, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2131d55-7806-4793-b969-c43a8d8c0c40

📥 Commits

Reviewing files that changed from the base of the PR and between 4dd029b and 9ef432f.

📒 Files selected for processing (3)
  • README.md
  • Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift
  • Tests/WallpaperDataSourceTests/YouTubeWallpaperResolveTests.swift
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • Tests/WallpaperDataSourceTests/YouTubeWallpaperResolveTests.swift
  • Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift

📝 Walkthrough

Walkthrough

YouTubeWallpaperDataSourceImpl is extended with runtime ffmpeg/ffprobe detection. When tools are present, it selects the highest-quality stream and normalizes the downloaded file via ffprobe codec detection followed by either stream-copy remux (AVC/HEVC) or hardware HEVC transcode (AV1/VP9). Without tools, it falls back to an AVC-limited 1080p selector. The processRunner signature gains a stdout field, and a new transcodeFailed error case is added.

Changes

YouTube Codec-Aware Download Pipeline

Layer / File(s) Summary
processRunner signature and error type
Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift
processRunner closure return type gains stdout; initializer parameter type updated; YouTubeDownloadError adds transcodeFailed(stderr:) case with updated description.
allowAnyCodec flag and format selector logic
Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift, Tests/WallpaperDataSourceTests/YouTubeToolDetectionTests.swift
buildArgs gains allowAnyCodec parameter; formatSelector branches between codec-flexible bestvideo and AVC-restricted 1080p selectors; tool detection tests updated with the new flag.
resolve(_:) tool detection and download orchestration
Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift
resolve(_:) probes for ffmpeg/ffprobe, sets allowAnyCodec, destructures the three-field process result, and dispatches to normalizeForPlayback instead of the prior remux call.
normalizeForPlayback: codec detection and remux/transcode
Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift
New normalizeForPlayback uses ffprobe to detect codec, stream-copies AVC/HEVC to faststart MP4, hardware-transcodes AV1/VP9 to HEVC, and throws typed errors on failure.
executeProcess stdout and stderr capture
Sources/WallpaperDataSource/YouTubeWallpaperDataSourceImpl.swift
executeProcess reads and trims both stdout and stderr concurrently via a new shared trimmedString(from:) helper and returns the three-field tuple.
Resolve, format, and process tests
Tests/WallpaperDataSourceTests/YouTubeWallpaperResolveTests.swift
Existing tests updated to stub (status, stdout, stderr); new tests cover AV1→HEVC transcode, stream-copy, transcode failure, remux failure, executeProcess stdout capture, transcodeFailed error description, and a new YouTubeWallpaperFormatTests suite.
README, CLAUDE.md, and version
README.md, .claude/CLAUDE.md, Sources/VersionHandler/Resources/version.txt
README updated with ffmpeg+ffprobe requirement, 4K detail, and fallback/looping behavior; CLAUDE.md updated to describe codec-aware pipeline; version bumped to 2.16.0.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐇 Hop, hop! No more 360p shame,
The rabbit sniffs out ffprobe by name.
AV1? VP9? Transcode to HEVC!
Or stream-copy swift—no fuss, no fee.
4K wallpaper, crisp as morning dew,
Lyra leaps higher, frame by glorious frame! 🎬

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: enabling highest-quality YouTube wallpaper downloads with HEVC transcoding for universal Mac playback by defeating codec restrictions.
Linked Issues check ✅ Passed All acceptance criteria from issue #292 are met: downloads exceed 1080p by removing H.264 codec restriction [#292], fallback avoids 360p by switching to web player client [#292], HEVC transcode ensures AVPlayer loop compatibility [#292], and PO Token/player_client behavior verified via real hardware testing [#292].
Out of Scope Changes check ✅ Passed All code changes align with issue #292 objectives: format selector enhancement, codec detection via ffprobe, HEVC transcode pipeline, processRunner signature update for stdout capture, and corresponding test coverage—no out-of-scope modifications detected.

✏️ 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/292/youtube-highest-quality

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.

❤️ Share

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

@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copilot AI 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.

Pull request overview

YouTube wallpaper の「ときどき 360p になる」問題を解消しつつ、VP9/AV1 の高解像度ソースを HEVC へ変換して全 Mac で再生可能にするための、YouTube ダウンロード〜再生正規化パイプライン改善です(WallpaperDataSource 層の責務として、取得した動画を AVFoundation で再生可能な形式へ寄せる変更)。

Changes:

  • yt-dlp の player_clientandroiddefault に切り替え、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.

Comment on lines +223 to 227
let stdoutPipe = Pipe()
let stderrPipe = Pipe()
process.standardOutput = stdoutPipe
process.standardError = stderrPipe

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

action

5ad7bac修正しました。stdout/stderr の両パイプを DispatchGroup 配下の別スレッドで並行ドレインするよう書き直し、OS パイプバッファ(約 64 KB)の枯渇によるデッドロックを解消しています。group.notify 内で process.waitUntilExit() を先に呼ぶことで、NSTask の SIGCHLD 処理が完了してから terminationStatus を読むようにしました。

Comment on lines +242 to +245
private static func trimmedString(from pipe: Pipe) -> String {
let data = pipe.fileHandleForReading.readDataToEndOfFile()
return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

action

Finding #1executeProcess 書き直しに伴い trimmedString(from: Pipe)削除しました。trim ロジックは新しい PipeBuffer クラスの trimmed(_:) メソッドに移動しています。

Comment thread README.md Outdated
Comment on lines +259 to +263
| `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

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

action

5ad7bac修正しました。requirements テーブルを ffmpegffprobe の 2 行に分離し、それぞれの役割を明確に記載しています: ffmpeg 単体は DASH→MP4 リムックスでループ再生を有効化(H.264 / 1080p 上限)、ffprobebrew install ffmpeg に同梱)を加えると 4K コーデック判定と HEVC トランスコードが使えます。

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment on lines +162 to +164
let codec = await videoCodec(at: path, ffprobe: ffprobe)
let needsTranscode = codec.map(Self.requiresTranscode) ?? false
let tmpPath = path + ".normalized.mp4"

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

action

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 単体)も追加済みです。

@GeneralD GeneralD merged commit d9869b8 into main Jun 16, 2026
3 checks passed
@GeneralD GeneralD deleted the feat/292/youtube-highest-quality branch June 16, 2026 18:48
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.

YouTube wallpaper を最高画質でダウンロード(AVC縛り・360pフォールバック解消)

2 participants