YoutubeDownloader 实战指南:跨平台下载 YouTube 视频、播放列表与频道(附源码级解析)

原创2026-10-02 17:12:511,747 阅读
文章标签:桌面应用视频音视频

YoutubeDownloader 实战指南:跨平台下载 YouTube 视频、播放列表与频道(附源码级解析)

YoutubeDownloader 是一款开源的跨平台图形界面下载工具,支持通过 URL 直接下载 YouTube 视频、播放列表与频道内容,也支持按关键词搜索后下载。本篇指南以仓库根目录的 Readme.md 为主线,结合 YoutubeDownloader.Core 与 YoutubeDownloader 两个工程的真实源码,系统讲解它的功能清单、安装方式、查询解析机制、下载选项、FFmpeg 集成、媒体标签注入以及可配置项,帮助你从"会用"进阶到"懂原理"。

项目概览:YoutubeDownloader 是什么

根据 Readme.md 的官方描述,YoutubeDownloader 是一个让你从 YouTube 下载视频的应用程序:你可以粘贴任意视频、播放列表或频道的 URL,直接以你选择的格式下载;它同时支持按关键词搜索,方便快速查找并下载视频。

该应用底层使用 YoutubeExplode 库与 YouTube 交互(见 Readme.md)。这意味着下载的核心协议解析、流媒体清单获取等工作都由 YoutubeExplode 完成,本项目则在其上构建了图形界面与下载编排逻辑。

从仓库结构看,解决方案由两个工程组成(见 YoutubeDownloader.slnx):

  • YoutubeDownloader.Core:无界面核心库,负责查询解析(Resolving/)、下载编排(Downloading/)与媒体标签注入(Tagging/),并直接依赖 YoutubeDownloader.Core.csproj 中声明的 YoutubeExplode、YoutubeExplode.Converter、Gress(进度)、TagLibSharp(标签)等包;
  • YoutubeDownloader:Avalonia 图形界面工程,采用 MVVM 架构(ViewModels/ 与 Views/),依赖 YoutubeDownloader.csproj 中的 Avalonia、Material.Avalonia、CommunityToolkit.Mvvm 等 UI 相关包。

从源码结构看,界面层几乎不直接触碰 YouTube API,所有业务能力都收敛在 Core 库中,这也是本项目"核心库 + UI 壳"分层清晰的设计特点。

核心功能清单

Readme.md 的 Features 列表完整给出了这款工具的能力边界,下面逐条结合源码展开:

  1. 跨平台图形界面:基于 Avalonia 构建(Windows / macOS / Linux),相关视图与主题管理见 Views 与 Framework/ThemeVariant.cs;
  2. 按 URL 下载视频:粘贴单个视频链接即可进入下载配置流程,对应 QueryResolver 中的 TryResolveVideoAsync;
  3. 下载播放列表或频道视频:支持播放列表 ID、频道 ID、频道 Handle、用户名、频道 Slug 等多种形式,对应同一文件中的 TryResolvePlaylistAsync 与 TryResolveChannelAsync;
  4. 按搜索关键词下载:关键词走 ResolveSearchAsync,一次抓取 20 条结果(见 QueryResolver.cs);
  5. 可选的视频质量与格式:UI 上可从多种质量档位与容器格式中选择,底层逻辑见下文"下载选项"一节;
  6. 自动嵌入其他语言的音轨:若视频存在多语言音频流,可一并封装进输出文件,对应 VideoDownloadOption.ResolveAll 中的语言音轨选择逻辑;
  7. 自动嵌入字幕:下载时把字幕轨一并写入容器,对应 VideoDownloader.cs 的字幕清单获取;
  8. 自动注入媒体标签:下载完成后用 MusicBrainz 数据库匹配歌手/专辑等元数据并写回文件,见 MediaTagInjector.cs;
  9. 登录 YouTube 账号访问私有内容:通过 Cookie 认证,登录态可持久化(加密保存),详见下文"登录与私有内容"一节。

获取与安装

Readme.md 给出了四类获取途径:

途径 说明 命令
🟢 Stable release 官方稳定版发布页 从 releases 页面下载对应平台安装包
🟠 CI build 持续集成构建产物 从 GitHub Actions 工作流产物中下载
📦 Scoop(社区维护) Windows 包管理器 scoop install extras/youtubedownloader
📦 AUR(社区维护) Arch Linux 用户仓库 yay -S youtubedownloader

社区维护的软件包独立于本仓库发布,可能与最新 Release 存在版本滞后(见 Readme.md)。若不确定该选哪种构建,官方建议先通过 useragent.cc 类站点确认自己的操作系统与 CPU 架构(见 Readme.md)。

macOS 特别提醒:首次启动前可能需要解除下载文件的隔离属性,在终端执行:

xattr -rd com.apple.quarantine YoutubeDownloader.app

(见 Readme.md。)这条命令会把隔离标记从整个 app 包中递归移除,避免 Gatekeeper 阻止运行。

如需从源码自行构建,可先克隆仓库并还原依赖:

git clone https://gitcode.com/gh_mirrors/yo/YoutubeDownloader
cd YoutubeDownloader
dotnet restore
dotnet build

构建所需 SDK 版本由仓库根目录的 global.json 约束,包版本统一收敛在 Directory.Packages.props 中。

查询解析机制:URL、搜索与多行输入的底层逻辑

这是理解本工具行为最重要的入口。用户在界面输入框(DashboardViewModel.Query)中粘贴的内容会被 QueryResolver.cs 按以下顺序尝试解析(见 ResolveAsync,L110-L123):

  1. 播放列表(PlaylistId.TryParse);
  2. 单个视频(VideoId.TryParse);
  3. 频道(依次尝试 ChannelId、ChannelHandle、UserName、ChannelSlug 四种形式,见 L54-L96);
  4. 兜底:关键词搜索(上述全部失败时)。

三个值得注意的细节:

  • ? 前缀强制搜索:如果输入以 ? 开头,则无论内容多像 URL,都会直接按搜索处理(query<a href="https://link.gitcode.com/i/6fe25ca03bf90566376e1b7fbfb13b61" target="_blank">1..] 去掉前缀后执行搜索,见 [L115-L117)。这为"明确想搜索、但关键词恰好长得像链接"的场景提供了逃生通道;
  • 私人系统播放列表需要登录:WL(Watch Later)、LL(Liked)、LM 这类个人播放列表,只有在携带登录 Cookie 时才会解析(见 L29-L34);
  • 搜索只取前 20 条:Search.GetVideosAsync(query).CollectAsync(20)(见 L103-L105),即单次搜索最多呈现 20 个结果。

在界面层,输入框支持多行粘贴:ProcessQueryAsync 会把 Query 按换行符拆成多个独立查询逐个解析(见 DashboardViewModel.cs),再用 QueryResult.Aggregate 合并结果:单条查询保留原始类型与标题,多条查询则归为 Aggregate 类型、标题显示为"N queries",并按视频 ID 去重。如果某一行解析失败且总查询数大于 1,不会中断整体流程,而是通过 Snackbar 异步提示错误(见 DashboardViewModel.cs)。

解析结果类型由 QueryResultKind.cs 定义,共五种:Video、Playlist、Channel、Search、Aggregate。界面会据此决定后续交互:单视频弹出单文件下载对话框,多视频弹出批量下载对话框——且只有来自"非搜索、非聚合"的结果才会在批量对话框中默认预选全部视频(见 DashboardViewModel.cs),避免搜索结果被无脑全选。

下载选项:格式、画质与多语言音轨的选择逻辑

下载选项如何生成

当解析出一个视频后,界面会调用 VideoDownloader.GetDownloadOptionsAsync 拉取流媒体清单(manifest),并交给 VideoDownloadOption.ResolveAll 生成全部可选方案。其核心逻辑为:

  • 混流方案:如果视频本身提供音视频打包在一起的 MuxedStreamInfo,直接生成单流方案;
  • 分离流方案:对每个视频流,按"同容器优先 → 独立音频流优先 → 码率优先"的顺序挑选音频流;若视频存在多语言音频流(且设置允许),则把所有语言轨一并打包,并让默认语言排在最前(见 L57-L75),这也是"自动嵌入其他语言音轨"的落点;
  • 纯音频方案:分别以 WebM / MP3 / OGG / MP4 四种容器输出纯音频(见 GetAudioOnlyOptions,L97-L142);
  • 去重:以"画质 + 是否超采样(upscaled)+ 容器"为键去重(见 L144-L158)。

每个选项还暴露两个派生属性:VideoQuality(取所有视频流中的最高画质)与 IsVideoUpscaled(是否存在超采样流,见 L15-L19)。

画质偏好档位

设置页可选的画质偏好由 VideoQualityPreference.cs 定义,共 6 档,显示名见下表:

枚举值 显示名(界面) 含义
Lowest Lowest quality 最低画质
UpTo360p ≤ 360p 不超过 360p
UpTo480p ≤ 480p 不超过 480p
UpTo720p ≤ 720p 不超过 720p
UpTo1080p ≤ 1080p 不超过 1080p
Highest Highest quality 最高画质

这些偏好由 VideoDownloadPreference.TryGetBestOption 落地为具体选项:音频专用容器直接短路匹配;视频类容器则把选项按画质升序排列后,按档位截取(MaxHeight <= 1080/720/480/360)取最后一个同容器选项;Highest 取同容器中画质最高的,Lowest 取同容器中画质最低的;若档位内找不到同容器选项,则回退到同容器中的第一个可用项(见 L52-L53)。因此"UpTo"类偏好并不会真正转码降级,而是"只选取不超过该高度的流",这是理解画质行为的关键。

下载流程与 FFmpeg 集成

下载链路

VideoDownloader.DownloadVideoAsync(见 VideoDownloader.cs)是单文件下载的核心方法,执行顺序为:

  1. 若需要字幕且目标容器非纯音频,先拉取字幕轨清单(ClosedCaptions.GetManifestAsync)并把全部轨道加入 trackInfos;
  2. 自动创建目标目录;
  3. 调用 YoutubeExplode 的 Videos.DownloadAsync,把"选中的流清单 + 字幕轨 + 转换请求"一并提交,转换请求以 ConversionPreset.Medium 预设交给 FFmpeg 封装(见 L73-L83);
  4. 进度通过 Gress 库以 Percentage 形式向上层 UI 上报。

在 DashboardViewModel.cs 的 EnqueueDownload 中,每个下载任务会:先按设置决定是否调用 GetBestDownloadOptionAsync(未手动指定选项时自动挑选最佳画质);下载完成后若开启标签注入则调用 MediaTagInjector.InjectTagsAsync;失败时删除不完整的文件并标记状态(Completed / Failed / Canceled);YouTube 类异常只展示短消息,其他异常展示完整堆栈(见 L218-L222)。

FFmpeg 的定位、探测与自动下载

FFmpeg 是"分离流合并、字幕封装、容器转换"的执行者。工具对它的处理集中体现在 FFmpeg.cs:

  • 版本与文件名:内置目标版本常量 8.1;Windows 下 CLI 文件名为 ffmpeg.exe,其余平台为 ffmpeg(见 L17-L20);
  • 探测路径顺序:依次检查应用基目录、当前工作目录、进程 PATH、Windows 的用户与系统注册表 PATH(见 GetProbeDirectoryPaths,L22-L67);
  • 按需下载:若探测不到,工具会自动从 Tyrrrz/FFmpegBin 的 8.1 Release 下载对应 系统-架构 的 zip(Windows/Linux/macOS × x64/x86/arm64),解压出 CLI 文件,并在 Unix 上补上可执行位(见 L75-L154)。

也就是说:不装 FFmpeg 也能跑,工具会在首次使用时自动补齐;当然也可以在图界面设置中手动指定 FFmpeg 可执行文件路径(对应设置项 FFmpegFilePath)。

自动注入媒体标签与缩略图

下载完成后,MediaTagInjector.InjectTagsAsync(见 MediaTagInjector.cs)会对文件做三类元数据加工:

  1. 基础信息(InjectMiscMetadata):写入视频描述,以及一条固定格式注释,包含"使用 YoutubeDownloader 下载"、视频标题、视频 URL、频道名与频道 URL(见 L16-L31);
  2. 音乐元数据(InjectMusicMetadataAsync):以视频标题查询 MusicBrainz,要求"录音标题是视频标题的子串,且歌手名出现在视频标题或频道名中"才算命中(见 L41-L49);命中后写入歌手、标题、歌手排序名与专辑字段,数据模型见 MusicBrainzRecording.cs;
  3. 封面缩略图(InjectThumbnailAsync):优先挑选分辨率最高且格式为 jpg 的官方缩略图,否则回退到 i.ytimg.com 的 hqdefault.jpg(见 L64-L83)。

媒体文件的读写封装在 MediaFile.cs(基于 TagLibSharp)。在界面流程中,标签注入被放在下载成功之后、且被 try/catch 包裹——"媒体标签不是关键步骤",即使 MusicBrainz 或缩略图请求失败也不会导致下载任务被判定失败(见 DashboardViewModel.cs)。

设置项、环境变量与文件名模板

设置项与默认值

应用设置由 SettingsService.cs 管理(基于 Cogwheel 的 SettingsBase,JSON 序列化,持久化到 Settings.dat),下表汇总了可配置项与默认值:

设置项 默认值 说明
Theme — 界面主题(亮/暗/跟随系统,见 ThemeVariant.cs)
Language — 界面语言(仓库内置英语、简体中文、法语、德语、匈牙利语、西班牙语、乌克兰语,见 Localization)
IsAutoUpdateEnabled true 是否启用自动更新(由 Onova 库驱动)
IsAuthPersisted true 登录 Cookie 是否持久化保存
FFmpegFilePath null 自定义 FFmpeg 路径
ShouldInjectLanguageSpecificAudioStreams true 是否嵌入多语言音轨
ShouldInjectSubtitles true 是否嵌入字幕
ShouldInjectTags true 是否注入媒体标签
ShouldSkipExistingFiles false 是否跳过已存在的文件
FileNameTemplate $title 输出文件名模板(见下文)
ParallelLimit 2 最大并发下载数
LastContainer Mp4 上次使用的容器格式
LastVideoQualityPreference Highest 上次使用的画质偏好

环境变量

启动阶段(StartOptions.cs)支持两个环境变量:

  • YOUTUBEDOWNLOADER_SETTINGS_PATH:自定义设置文件位置。若值指向已存在的目录或以路径分隔符结尾,则视为目录并在其中使用 Settings.dat;否则视为完整文件路径;未设置时使用程序基目录下的 Settings.dat(见 L15-L24);
  • YOUTUBEDOWNLOADER_ALLOW_AUTO_UPDATE:设为 false(不区分大小写)可禁用自动更新(见 L25-L28)。

文件名模板

文件名由 FileNameTemplate.cs 中的 Apply 渲染,支持以下占位符(结果会自动追加容器扩展名并转义非法字符):

占位符 含义 示例
$numc 序号(不带括号) 5
$num 序号(带方括号) [5]
$id 视频 ID dQw4w9WgXcQ
$title 视频标题 Rick Astley - Never Gonna Give You Up
$author 频道名 Rick Astley
$uploadDate 上传日期(yyyy-MM-dd) 2009-10-25

例如模板 $num - $title 配合序号 3 会渲染为 3 - 视频标题.mp4。多视频下载时由界面层为每个任务传入序号。

登录与私有内容

要下载"仅登录可见"的私有内容,可通过 Auth 设置对话框登录 YouTube 账号(入口见 DashboardViewModel.cs 的 ShowAuthSetupAsync)。认证本质是保存会话 Cookie:

  • QueryResolver 与 VideoDownloader 的构造函数都接收 IReadOnlyList<Cookie>? 并注入到 YoutubeClient(见 QueryResolver.cs 与 VideoDownloader.cs),后续所有请求都带上登录态;
  • Cookie 通过 LastAuthCookies 设置项保存,序列化时使用 AuthCookiesEncryptionConverter 加密,且保存时若 IsAuthPersisted 为假则先置空再写盘(见 SettingsService.cs),避免把凭据明文留在磁盘;
  • 工程文件中的 EncryptionSalt 属性(见 YoutubeDownloader.csproj)作为加盐参数暴露给代码使用。

下载队列与并发控制

主界面维护一个下载队列(Downloads 集合),其并发行为值得注意:所有下载任务共享一个信号量(semaphore),并发上限即设置中的 ParallelLimit(默认 2),任务只有获取到许可证后才真正进入下载(见 DashboardViewModel.cs 的 _downloadSemaphore.AcquireAsync)。队列操作还包括:

  • 取消/移除:CancelAllDownloads、RemoveDownload、RemoveSuccessfulDownloads、RemoveInactiveDownloads(见 L354-L384);
  • 失败重试:RestartDownload 按原下载选项或偏好重新入队,RestartFailedDownloads 批量重试失败任务(见 L386-L415);
  • 清理:视图模型释放时会取消全部下载并释放信号量(见 L424-L435)。

另外,应用启动时会先执行 EnsureFFmpegAsync(见 L140),确保 FFmpeg 就绪后才开放下载能力。

附带说明:项目使用条款

Readme.md 同时声明了项目的使用条款:使用本项目或源码即视为默认同意其政治立场声明(谴责俄罗斯对乌克兰的军事侵略、承认乌克兰领土完整等)。该条款属于项目文档的一部分,在决定分发、二次开发或企业内部使用时需一并知悉。开发者若对本仓库进行镜像或再分发,也应保留这一条款文本。

总结

YoutubeDownloader 的价值在于把"解析 → 选流 → 合并 → 打标签"整条链路做成了开箱即用的跨平台 GUI:粘贴链接或关键词即可下载,画质/格式可调,字幕与多语言音轨自动封装,媒体标签自动补齐,还能登录账号抓取私有内容。如果你想深入源码,建议按以下路径阅读:

需要说明的是,本仓库镜像未包含 README 中引用的 .assets/ 截图目录;文中所有结论均来自 Readme.md 与仓库源码,未引入任何外部性能或排名数据。请以你实际使用的 Release 版本为准,社区维护的 Scoop/AUR 包可能与最新版存在差异。

登录后查看全文
YoutubeDownloader