关于 recipe-cleanup
用于清理并规范化食谱文件的技能,可从本地文件、网页、YouTube 视频说明或内联文本提取内容。它按食材、步骤、备料和来源等规则整理格式,并处理图片、创作者信息及食材链接。
主要功能
- 01读取本地文件、网页或内联食谱
- 02从 YouTube 说明提取食谱
- 03规范食材清单与烹饪步骤
- 04整理备料和烹饪阶段
- 05生成创作者元数据并查找图片
- 06解析食材笔记链接
适用场景
- 01将网页食谱整理为统一文件
- 02从 YouTube 视频说明制作食谱笔记
- 03将食材链接对应到现有产品笔记
- 04按备料和烹饪阶段重排步骤
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gkwa/ volcanicviper --skill recipe-cleanup安装后在 Claude Code 中输入/recipe-cleanup确认已加载。
附带文件tile.json
详细介绍
食谱整理
解析输入:第一个参数可以是本地食谱文件路径,也可以是食谱页面的 URL。可选的第二个参数是原食谱的来源 URL。URL 或文件路径之后的所有文本都视为内联食谱内容(食材、做法、备注)。
根据第一个参数确定输入模式:
- 如果以
http://或https://开头,则视为 URL — 获取页面并提取食谱内容;该 URL 也会作为隐式来源 URL(除非第二个参数覆盖它)- YouTube 观看 URL(
youtube.com/watch?v=VIDEO_ID):提取视频 ID,并将缩略图设为https://i.ytimg.com/vi/VIDEO_ID/hq720.jpg,然后尝试按以下备用顺序获取视频简介:- 先对 YouTube URL 使用 WebFetch — 如果返回了可用的视频简介内容,则使用这些内容
- 如果 WebFetch 失败或未返回视频简介,则改用 Playwright:访问页面,等待 3 秒使页面加载,然后点击 "...more" 按钮展开完整简介
- 获取视频简介后(无论通过哪种方式),扫描其中是否包含食谱内容:
- 简介中包含食谱:如果简介直接包含食材或做法,则将其用作食谱内容
- 简介中包含链接:如果简介包含指向外部食谱页面的链接(例如博客文章或专门的食谱 URL),则使用 WebFetch 获取该 URL,并从中提取食谱;记录 YouTube URL 和食谱页面 URL — 两者都会出现在输出文件中
- 内联文本备用方案:如果简介中两者都没有,则使用参数中提供的任何内联文本
- 使用频道名称和视频标题识别创作者
- YouTube 观看 URL(
- 否则,将其视为本地文件路径 — 读取该文件
根据频道 URL 识别创作者:
- 如果任一参数是 YouTube 频道 URL(例如
youtube.com/@Handle或youtube.com/c/name),尝试获取该页面以提取创作者的真实姓名 - 如果获取失败,则通过拆分句首大写的部分,从账号名称推导姓名(例如
@FastEasyRecipes→Fast Easy Recipes) - 在
creatorfrontmatter 字段中使用该姓名
根据以下格式规则处理食谱内容。
Frontmatter:
- 在文件顶部(来源 URL 行之前)生成 YAML frontmatter,并包含以下字段:
--- creator: '[[Author Name]]' pic: <hero-image-url> tags: - recipe - <creator tag> --- - 创作者标签为解析出的页面名称转为小写,并移除所有空格和分隔符(例如,
Andrea Geary→andreageary、budgetbytes→budgetbytes、Fast Easy Recipes→fasteasyrecipes) - 从食谱页面提取作者/创作者姓名(例如,从食谱卡片的作者字段或博客的关于页面中提取)
- 按以下优先顺序确定创作者的页面名称:
- 如果食谱来自作者自己的个人域名,则使用不含 TLD 的域名作为页面名称(例如,
budgetbytes.com→budgetbytes、halfbakedharvest.com→halfbakedharvest)。这是作者选定的品牌名称。 - 如果食谱来自大型出版物(例如,Bon Appétit、Serious Eats、NYT Cooking、Food Network、Epicurious、Tasty),则使用该出版物署名的作者姓名 — 内容属于出版物,而非个人品牌。
- 如果没有个人域名(例如,仅在 YouTube 上发布内容的创作者),则使用作者的真实姓名。
- 如果食谱来自作者自己的个人域名,则使用不含 TLD 的域名作为页面名称(例如,
- 在
creatorfrontmatter wikilink 中使用解析出的页面名称(例如,[[budgetbytes]]) - 写入文件后,检查与食谱文件相同目录下是否存在名为
<page name>.md的页面 — 如果不存在,则创建一个最简存根文件:包含# <page name>,后接一个空行和作者的来源 URL(例如,其 YouTube 频道 URL、网站,或食谱的来源页面)
格式规则
食材:
- 将每行食材格式化为已勾选的复选框:
- [x] quantity [[ingredient name]] - 使用 Obsidian 双链包裹食材名称(不包括数量或描述):
[[ingredient name]] - 确保双链涵盖完整的产品名称,以链接到仓库中对应的产品页面
- 示例:
1 cup julienned carrots→- [x] 1 cup julienned [[carrots]] - 示例:
2 teaspoons sesame oil→- [x] 2 teaspoons [[sesame oil]](而非sesame [[oil]])
- 示例:
- 如果食材已有双链,则保持原样
- 写入文件后,解决每个未指向现有笔记的食材双链——请参阅下方的“解决食材双链”部分
解决食材双链:
- 清理后的文件写入后,每个食材名称都应使用
[[wikilink]]包裹,但其中许多双链可能不会指向知识库中现有的产品笔记 - 不要自行编造或手动构造目标;请委派给
resolve-recipe-ingredient-link技能,该技能是唯一的规范解析器 - 对于每个目标笔记在知识库中不存在的食材双链(按 Obsidian 解析链接时使用的方式,通过基本名称判定),调用
resolve-recipe-ingredient-link,查找最匹配的产品笔记并将链接重写为解析后的形式 - 将完整的食材行作为上下文传给解析器,而不只是单独的括号文本——链接词语周围的描述性文字有助于区分匹配项
- 只枚举一次候选产品笔记,并基于同一列表解析该食谱中所有未解析的链接,而不是针对每种食材重新枚举
- 已解析到现有笔记的双链保持不变
- 如果解析器仅凭食材行无法确定链接目标,请遵循其三级规则——列出候选项并询问用户,不要强行选择错误的匹配项
说明:
-
指令行的首字母绝不大写(以小写字母开头)
-
指令末尾不要加句号
-
每个句子单独成段——将包含多个句子的指令拆分为独立句子,并以空行分隔
-
删除所有粗体、斜体或强调格式,仅保留纯文本
-
指令始终使用普通段落——如果原文使用编号列表或有序列表,则将每个项目转换为普通段落
-
如果某个步骤以逗号分隔的序列列出多个项目(例如“add onions, oil, butter, and salt”),请将该列表展开为 Markdown 项目符号子列表,同时保留周围句子的原样:
输入示例:
add onions, oil, remaining butter, salt and pepper to pan输出示例:
add - onions - oil - remaining butter - salt - pepper to pan
备料:
- 备料(Mise en place)意为“一切各就其位”,指在开始烹饪前完成所有准备工作,从而让烹饪过程不中断
- 原则:将准备工作与烹饪过程分开;锅已经烧热时还在切菜的厨师,既会烧焦食物,也会分散注意力
- 从说明中提取所有准备类步骤,并将其放在紧挨第一段烹饪内容上方的
## Mise en Place部分;如果已有## Prep部分,则将其重命名为## Mise en Place - 准备步骤包括所有可在开始加热或计时前完成的任务:切碎、切丁、剁碎、切片、去皮、称量、浸泡、清洗、醒面、将食材恢复至室温、预热等
- 如果食谱包含不同的烹饪阶段(例如熬制高汤、制作酱汁、准备馅料),请为每个阶段设置单独的命名部分(例如
## Broth、## Sauce),而不是将所有内容合并到一个## Instructions部分中——这会在阶段层面应用备料原则,而不仅仅是在食材层面应用 - 仅将最终组装步骤放在
## Instructions中 - 每个准备步骤都使用普通段落格式(不加项目符号),段落之间以空行分隔——与说明部分的结构相同
- 如果准备步骤涉及多个组成项目,将操作写成普通段落,并在其下方用项目符号子列表列出这些项目(遵循与说明部分相同的逗号展开规则)
- 遵循相同的说明格式规则:以小写字母开头,末尾不加句号
- 从烹饪部分中移除已提取的步骤——不要重复
- 如果已存在
## Mise en Place部分,则将新提取的步骤合并到其中
菜品图片:
- 查找食谱的主图(例如,来自 og:image 元标签或主要食谱照片)
- 将其作为内嵌 Markdown 图片放在 frontmatter 块之后、来源 URL 之前:
 - 如果该位置已有图片,则保持原样
营养部分:
- 如果存在 Nutrition 部分,请将其整个移除
YouTube 链接:
- 将所有较长的 YouTube URL 转换为短格式:
https://youtu.be/VIDEO_ID- 示例:
https://www.youtube.com/watch?v=abc123→https://youtu.be/abc123 - 示例:
https://www.youtube.com/watch?v=abc123&t=45s→https://youtu.be/abc123?t=45
- 示例:
- 已有的短
youtu.beURL 保持原样 - 如果文件中存在 YouTube 视频 ID 和
picfrontmatter 字段,请在使用缩略图 URL 前验证缩略图:先对hq720.jpg运行curl -s -o /dev/null -w "%{http_code}" URL;如果返回 200,则使用该图片;如果返回 404,则改用hqdefault.jpg
时间戳:
- 如果某条说明引用了
[MM:SS]格式的视频时间戳,并且有对应的 YouTube URL,请将其转换为使用?t=参数的绝对 Markdown 链接,参数值为秒数- 示例:视频为
https://youtu.be/Wnt90dkEwFs,[00:17]→[00:17](https://youtu.be/Wnt90dkEwFs?t=17) - 示例:
[01:05]→?t=65
- 示例:视频为
- 链接文本应为
[MM:SS]格式的时间戳 - 如果时间戳没有对应的 YouTube URL,则保持为纯文本
输出示例
crack the eggs into a bowl, see [00:05](https://youtu.be/Wnt90dkEwFs?t=5)
whisk until fully combined
pour into a heated, greased pan, see [00:17](https://youtu.be/Wnt90dkEwFs?t=17)
cook until edges begin to set
步骤
- 根据第一个参数判断输入模式(URL 还是文件路径)
- 据此获取或读取食谱内容;提取作者姓名和主图 URL
- 将所有格式规则应用于食谱内容——将说明中的有序列表转换为普通段落;仅对说明行应用说明专属规则(小写、不加句末句号、拆分句子)
- 在文件最顶部添加 YAML frontmatter,包含
creator、pic和tags - 将主图紧接在 frontmatter 区块之后,再在下一行或数行放置来源 URL,随后空一行——如果已有来源 URL,则替换它;如果食谱来自 YouTube 描述中链接的外部页面,则先放 YouTube URL,再在下一行放食谱页面 URL
- 不要添加
# Title标题——在 Obsidian 中,文件名就是页面标题 - 对于文件模式:用清理后的内容覆盖文件并确认成功——不要在聊天中输出完整食谱内容
- 对于 URL 模式:在聊天中显示格式化后的食谱,然后立即写入文件,无需询问——使用食谱标题作为文件名,以空格(而非连字符)分隔,并添加
.md扩展名;如果作者已知,则在文件名前加上作者姓名,避免同名食谱冲突(例如,Fast Easy Recipes crepes.md);保存到知识库中其他食谱文件所在的目录 - 写入文件后,检查作者页面是否存在于同一目录;如果不存在,则创建名为
<page name>.md的空白页面,仅包含# <page name>(使用上述 frontmatter 规则确定的页面名称) - 写入文件后,委派给
resolve-recipe-ingredient-link技能以解决未解析的食材 wikilink——参见上文“解决食材 wikilink”格式规则 - 对写入的笔记调用
outline-headings技能——这是必需步骤,不可省略,否则来源 URL、份量、说明文字和主图会位于第一个标题上方,且不会显示在大纲中 - 确认 frontmatter 中的
pic值非空,并且同一张图片直接嵌入在第一个标题下方——没有主图的食谱笔记是不完整的
作者命名空间
根据作者的账号名或个人域名生成命名空间,仅保留小写字母和数字。
在以下三个位置统一使用这个单一标记:creator frontmatter wikilink、tags 条目和空白页面文件名。
Instagram 账号名 chef.sam.black 在所有位置都转换为 chefsamblack——[[chefsamblack]]、标签 chefsamblack、文件 chefsamblack.md。个人域名 halfbakedharvest.com 转换为 halfbakedharvest。
标签不能包含句点、空格或其他分隔符,而且真实姓名可能重复,因此,将账号名合并后得到的标记既能作为标签、wikilink,也能作为文件名,并且不会包含分隔符。
只有在没有账号名或域名时,才使用合并后的真实姓名。
知识库中已有的先例:pantrymama、thesaltymarshmallow、ediblealchemyacademy、allrecipes。