Nugget 的 Tendies 与 Batter 文件格式详解:PosterBoard 壁纸包与可定制模板的完整开发指南

发布时间:2026/10/15 20:04:38
Nugget 的 Tendies 与 Batter 文件格式详解:PosterBoard 壁纸包与可定制模板的完整开发指南
桌面应用移动开发音视频【免费下载链接】NuggetUnlock the fullest potential of your device项目地址https://gitcode.com/gh_mirrors/nug/Nugget点击查看免费下载导读本文以 Nugget 官方文档 documentation.md 为骨架系统讲解两类驱动 Nugget PosterBoard 壁纸功能的核心文件格式Tendies 文件PosterBoard 壁纸/描述符包.tendies与Batter 文件可定制模板包.batter。读者将掌握两种文件格式的内部结构、配置清单config.json的 Header 与 Option 完整写法、基于nuggetId的 CoreAnimation XML 标识机制以及 replace / set / remove / picker / bundle_id 五种选项类型的 JSON 规范与底层实现原理最终能够独立制作、发布属于自己的 Nugget 壁纸与模板文件。一、Tendies 文件PosterBoard 壁纸包Tendies 文件壁纸包本质上是存储将要恢复到 PosterBoard 的文件结构的压缩归档。Nugget 会读取包内的目录布局把文件原样还原到设备的 PosterBoard 容器中。依据包内顶层目录的命名Tendies 文件分为两种格式格式顶层文件夹名恢复行为UUID / 壁纸 ID容器格式Containercontainer直接恢复到/var/mobile/Containers/Applications/PosterBoard.app应用容器内并保持原有文件结构不会随机化描述符格式Descriptordescriptor或descriptors恢复到容器内的描述符descriptors目录会随机化防止冲突1.1 容器格式Container当包内出现名为container的文件夹时Nugget 将该文件夹下的内容直接映射到 PosterBoard 应用容器/var/mobile/Containers/Applications/PosterBoard.app中目录结构原样保留。由于不做任何改写包内descriptor的 UUID 与壁纸 ID 也会原封不动地落到设备上。需要特别警惕的是如果容器格式的包内包含PBFPosterExtensionDataStoreSQLiteDatabase.sqlite3这一数据库文件Nugget 会将其标记为unsafe_container并在导入时弹出警告提示可能需要先重置所有壁纸在设置中开启 Risky Options并重新应用该文件才能正常工作。这一检测逻辑位于 tendie_file.py扫描归档条目时若命中该文件名即置位危险标志。1.2 描述符格式Descriptor当包内顶层文件夹名为descriptor或descriptors时Nugget 将其内容恢复到设备上的描述符目录。恢复的目标位置与系统版本相关iOS 17.0 及以上恢复到61号目录iOS 16恢复到59号目录。这一版本差异在 posterboard_tweak.py 中体现apply_tweak会按version.startswith(16)决定structure_version 59其余情况为61。文档同时指出如果未来系统目录结构再次变化Nugget 的后续版本可能会自动处理这一兼容问题。描述符格式下每个描述符的UUID 与壁纸 ID 都会被随机化从而避免与设备上已存在的壁纸发生重叠冲突。随机化在recursive_add中实现描述符文件夹被重命名为str(uuid.uuid4()).upper()形式的新 UUID壁纸 ID 则通过randint(9999, 99999)生成同时会改写以下标识文件以保持一致性见 posterboard_tweak.pycom.apple.posterkit.provider.descriptor.identifier直接写入随机化 IDcom.apple.posterkit.provider.contents.userInfo设置wallpaperRepresentingIdentifier键Wallpaper.plist非递归地设置identifier键。1.2.1 有序描述符ordered如果希望在集合Collections中让壁纸按 ID 有序排列可以在文件夹名中加入ordered字样例如ordered-descriptors。此时 Nugget 会为同一目录下的所有描述符生成连续递减的 ID 序列recursive_add先取一个随机基数r_id生成sorted([r_id i for i in range(len(os.listdir(curr_path)))], reverseTrue)的列表逐个分配给每个描述符见 posterboard_tweak.py。源码注释解释了原因PosterBoard 以逆序按壁纸 ID 排序壁纸因此连续递减的 ID 能保证壁纸在集合中的展示顺序。1.2.2 按扩展Extension区分恢复目标Nugget v7.0Nugget v7.0 起描述符格式可以根据文件夹名中的关键词将壁纸恢复到不同的 PosterBoard 扩展extension下文件夹名包含目标扩展providerIdvideo或photosSuggested Photoscom.apple.PhotosUIPrivate.PhotosPosterProvidermercuryMercury Postercom.apple.MercuryPoster不加任何词Collectionscom.apple.WallpaperKit.CollectionsPoster——默认对应的扩展识别逻辑位于 posterboard_tweak.py文件夹名中命中video/photos或mercury时设置对应 provider否则回落为com.apple.WallpaperKit.CollectionsPoster。恢复路径形如/Library/Application Support/PRBPosterExtensionDataStore/{61|59}/Extensions/{extension}/descriptors文档建议如果目标是恢复到 Collections优先采用描述符格式因为随机化 ID 更安全且对未来版本更具前瞻性。1.3 Tendie 文件的识别与限制在 GUI 层tendie_file.py 通过扫描归档条目统计描述符数量descriptor_cnt并据此选择列表图标容器格式用shippingbox.svg单个描述符用photo.svg多个描述符用photo-stack.svg。同时 Nugget 对壁纸数量设有硬性限制verify_tendie要求descriptor_cnt 已有描述符数量 10见 posterboard_tweak.py而 Tendies 页面最多同时导入3个.tendies文件见 posterboard.py超出即弹出安全提示。二、Batter 文件可定制模板包Batter 文件模板与 Tendies 文件类似同样存储要恢复到某个 domain 的文件结构但多了一层用户可定制能力Nugget 会把包内Container文件夹中的文件结构恢复出去并依据一个config.json文件在界面上渲染出可交互的选项按钮、滑块、复选框、颜色选择器、下拉选择器等让用户无需手工改文件即可定制壁纸。对于 PosterBoard 模板Batter 文件沿用了与 Tendies 相同的container/descriptor/descriptors文件夹格式。除 PosterBoard 之外Batter 模板通过domain字段还可以作用于任意 AppDomain 甚至系统路径因此其通用性远超壁纸场景例如通过 sparserestore 写/var/Managed Preferences等系统位置。2.1 模板加载流程源码视角TemplateFile 在加载时会在归档中查找config.json排除位于 descriptor / container 目录内的同名文件校验options与domain字段是否存在缺失即抛出PBTemplateException兼容 v5.2 beta 时期的旧写法若domain com.apple.PosterBoard自动升级为AppDomain-com.apple.PosterBoard校验format_version若大于当前格式版本CURRENT_FORMAT 2见 template_file.py提示该配置需要更新版本的 Nugget按min_version/max_version与当前设备系统版本比对版本不符时拒绝加载解析resources、previews、banner_text、banner_stylesheet等展示信息逐个实例化options中的选项对象ReplaceOption/RemoveOption/SetOption/PickerOption/bundle_id。三、Batter 文件 Headerconfig.json 头部信息config.json的头部携带模板的基础信息分为必填与可选两类。3.1 必填字段字段说明title操作模板的标题显示在界面上author作者姓名domain恢复目标 domain。PosterBoard 模板固定填AppDomain-com.apple.PosterBoard若要使用 sparserestore 能力填Sparserestore-FILEPATH例如Sparserestore-/var/Managed Preferencesformat_version配置格式的最低版本要求当前 Nugget 最新版本对应的值为format_version: 2options用户可配置选项列表格式见下文 Option Format 一节不需要任何可配置项时写options: []其中domain与format_version的强校验逻辑见 template_file.pySparserestore-域在 templates_tweak.py 中被特殊处理恢复路径直接拼接Sparserestore-前缀后的绝对路径且不再使用 AppDomain 恢复方式。3.2 可选字段字段说明description显示在标题下方的说明文字min_version模板支持的最低 iOS 版本如min_version: 18.0max_version模板支持的最高 iOS 版本如max_version: 18.0resources内嵌资源文件列表如 banner / preview 图片等支持通配符previews预览图片文件名列表这些文件的路径必须同时出现在resources列表中才会被展示preview_layout预览图的排布方式默认horizontal合法值horizontal横向排列、stacked叠放多个预览图叠在一起展示banner_textbanner 横幅的文本内容banner_stylesheetbanner 的 Qt 样式表style sheetmin_version/max_version可只写其一、都不写或都写。加载时 Nugget 会与设备系统版本做Version()比较不符合则拒绝应用见 template_file.py。resources的落地逻辑在 template_file.py使用fnmatch.filter支持通配符匹配将资源解压到临时目录如果设置了banner_stylesheet其中的url(resource)引用会被改写为临时文件的实际路径previews中列出的资源则被登记为预览图。preview_layout为stacked时界面使用QStackedLayout叠放预览图见 template_file.py。四、Option Format选项通用格式options数组中每个选项都必须包含三个必备属性属性说明type选项类型各类型的取值见下文选项类型一节label在 Nugget 界面中显示的文字files相对于config.json所在目录其父目录的文件路径列表associated_preview(可选)与该选项关联的预览图名称。对部分选项会产生特殊效果set如颜色会应用视觉效果remove/picker会隐藏图片对picker类型每个子选项都必须带此标签associated_preview的绑定逻辑见 template_options.py存在associated_preview且与预览图名称匹配时该选项获得对应QLabel的引用remove选项通过显隐预览图实时反馈效果见 remove_option.py。4.1 标识符Identifiers用 nuggetId 精确改写 .caml为了告诉 Nugget 需要修改.caml文件中的哪些属性你可以在目标 XML 标签上添加一个特殊的nuggetId属性赋值为任意合法字符串不一定是数字例如CALayer nuggetId1 id#1 allowsEdgeAntialiasing1 allowsGroupOpacity1 bounds0 -28 520 563 contentsFormatRGBA8 cornerCurvecircular hidden0 nameThing position195 365 transform zPosition442 sublayers/ /CALayer注意添加nuggetId需要你用文本编辑器手动修改.caml文件。随后在config.json的选项里通过identifier标签指定这个字符串值即可定位到该 XML 节点。files中的路径应直接指向.caml文件而不是.ca文件。如果你希望使用 CoreAnimation 原生的id属性即 XML 中的id标签作为定位依据可以在 JSON 中设置use_ca_id为true。底层实现位于 xml_handler.pyset_xml_values会根据use_ca_id选择idKey为id或nuggetId然后通过 XPath//*[idKeyid]找到所有匹配节点并写入属性值当use_ca_id为true时还会额外处理targetId指向的节点。布尔值会被自动转为0/1整数写入。删除节点时则使用delete_xml_value见 xml_handler.py。4.1.1 偏移方程nuggetOffset你还可以在.caml属性上添加nuggetOffset标签使 Nugget 在应用时将用户的输入值按数学方程偏移后写入。该标签的格式类似数学表达式其中x代表用户输入的单值对于多值属性如位置可用x、y、z、a分别代表各分量。例如CALayer nuggetId1 nuggetOffset(2 * x 10), (3 * y 15) id#1 allowsEdgeAntialiasing1 allowsGroupOpacity1 bounds0 -28 520 563 contentsFormatRGBA8 cornerCurvecircular hidden0 nameThing position195 365 transform zPosition442 sublayers/ /CALayer规则要点每个分量之间用逗号分隔使用nuggetOffset必须同时有nuggetId即该标签依赖nuggetId定位底层parse_equation见 xml_handler.py会把用户输入按空格拆分为x/y/z/a数组对每个逗号分隔的方程做求值再以空格连接回写。注意从源码结构看偏移方程目前仅在第一个 key上生效xml_handler.py中留有 TODO: Allow offsets for more than just the first value 注释。五、选项类型Option Types5.1 Replace Files替换文件type: replace属性说明button_label(可选)导入按钮上显示的文字allowed_files允许导入的文件类型与扩展名遵循 QT 过滤器格式如Image Files (*.png)required是否为必选项。若为true而用户未选择文件应用模板时会直接报错界面表现为导入按钮 已选文件路径见 replace_option.py默认按钮文字为Import {allowed_files}点击后弹出QFileDialog选择文件选择后按钮变为 Remove Selected File 并可撤销。应用时见 replace_option.py用户所选文件的内容会被逐字节写入files列表中的每一个目标文件支持通配符通过glob.glob(..., recursiveTrue)展开若required为真且未选文件则抛出NuggetException。5.2 Set Values设置属性值type: set属性说明identifierXML 文件中的标识对应nuggetId的值use_ca_id(可选)是否改用 CoreAnimation 的id而非nuggetIdkey要修改的 XML 属性名key/tagdefault_value(可选)输入的默认值value_type(可选)值的类型合法值integer、float、stringsetter_type用户看到的输入控件类型合法值textbox、slider、toggle、color_pickerSlider滑块专属选项属性说明min_value允许输入的最小值slider必填其他控件可选max_value允许输入的最大值slider必填其他控件可选step(可选)滑块每次变化的间隔Toggle开关专属选项属性说明inverted(可选)为true时写入文件的值取反toggle_off_value(可选)开关关闭时写入的值用于非布尔类型toggle_on_value(可选)开关打开时写入的值Color Picker颜色选择器专属选项属性说明sets_opacity(可选)是否同时写入透明度若default_value中提供了 4 个值RGBA会自动推断为true补充细节值在.caml中是什么格式JSON 里就应保持什么格式如果是浮点数在 Nugget 中仍需带引号即1.0nuggetOffset方程的用法见上文偏移方程一节identifier是必填属性见 set_option.py。实现要点set_option.pyslider的min_value/max_value为必填若value_type为float内部会按×1000的整数刻度驱动QSliderconvert_float并用correct_slider_pos把滑块位置校正到step的整数倍见 set_option.pytoggle默认值为false应用时若inverted为真则取反再按toggle_on_value/toggle_off_value映射见 set_option.pycolor_picker从default_value解析颜色支持r g b或r g b a四种分量自动识别 alpha界面使用透明棋盘底图叠上当前色get_stylesheet生成rgba(...)样式点击弹出QColorDialog应用时根据目标文件类型分支处理见 set_option.py.caml/.xml调用set_xml_value写入属性若sets_opacity且值为QColor则同时写入opacity键.plist调用write_plist_value写入键值其他纯文本文件直接对文件内容做字符串替换。5.3 Remove Files / Values删除文件或属性type: remove属性说明inverted(可选)为true时仅在复选框未勾选时才删除文件default_value(可选)复选框的初始状态默认falseidentifier(可选)XML 文件中的标识只有需要删除属性或图层时才填use_ca_id(可选)是否使用 CoreAnimationid而非nuggetIdlabel属性会直接作为复选框的文字显示见 remove_option.py。应用逻辑remove_option.py当(inverted 且未勾选)或(非 inverted 且勾选)时触发删除——若指定了identifier调用delete_xml_value删除 XML 中对应属性或节点否则删除files中的文件目录用rmtree文件用os.remove。5.4 Picker选择器type: picker属性说明options选择器中展示的子选项列表每个子选项包含一个label和一组filesallow_multiple_selection(可选)是否允许多选rename(可选)是否将用户选中的文件重命名allow_multiple_selection为true时不生效names(可选rename为true时必填)重命名后的新文件名列表顺序必须与options中files的顺序一致default_value(可选)默认选中的下标从 0 开始如default_value: 0多选时改用default_values: [0, 1, 2]行为约定当用户从选择器中选中某个选项后应用时其他所有未被选中的选项对应的文件都会被删除见 picker_option.py。因此 picker 适合多选一 / 多选若干的互斥场景例如在几套背景 / 前景素材中挑选一套。界面层单选使用QComboBox多选使用 Nugget 自带的MultiComboBox组件见 picker_option.py。应用时picker_option.py先删除未选中子选项的所有文件再按names列表将选中文件重命名目录用shutil.move文件用os.rename。若子选项带有associated_preview预览图会随选择实时显隐update_preview。5.5 Bundle Id应用包 IDtype: bundle_id属性无。该选项仅对AppDomain-前缀的模板生效它允许用户在界面中输入自定义的 App Bundle ID模板随后恢复到该应用容器中。默认值取自domain字段中AppDomain-之后的部分见 template_file.py 与 templates_tweak.py应用时若用户修改了 bundle iddomain 会被替换为AppDomain-{新ID}。注意修改 Bundle ID 后恢复时以相同名字开头的文件也会被一并改写文档原话强调即对路径中前缀匹配的应用容器文件生效使用前需确认影响范围。六、完整示例一个典型的 config.json以下为官方文档给出的完整config.json示例覆盖了 replace、remove普通 / 反选 / 删属性、picker带 rename与 setslider / toggle全部类型{ title: My Batter, description: I love Nugget, author: lemin, format_version: 2, domain: AppDomain-com.apple.PosterBoard, options: [ { type: replace, label: Background Image, button_label: Select Image, allowed_files: Image Files (*.png), required: true, files: [descriptors/UUID/version/1/bg/assets/image.png, descriptors/UUID/version/1/fg/assets/image2.png] }, { type: remove, label: File Thing Visible, files: [descriptors/UUID2/version/1/bg/assets/file1.png] }, { type: remove, label: Include collection item, files: [descriptors/UUID3], inverted: true, default_value: true }, { type: picker, label: Choose Things, rename: true, names: [descriptors/UUID/background.ca, descriptors/UUID/foreground.ca], options: [ { label: Option 1, files: [descriptors/UUID/bg1.ca, descriptors/UUID/fg1.ca] }, { label: Option 2, files: [descriptors/UUID/bg2.ca, descriptors/UUID/fg2.ca] }, { label: Option 3, files: [descriptors/UUID/bg3.ca, descriptors/UUID/fg3.ca] } ] }, { type: remove, label: Show Object, files: [descriptors/UUID/version/1/fg.ca/main.caml], inverted: true, default_value: true, identifier: 1 }, { type: set, label: Animation Duration, files: [descriptors/UUID/version/1/fg.ca/main.caml], setter_type: slider, min_value: 0.5, max_value: 3.0, step: 0.1, default_value: 1.0, key: duration, identifier: 2 }, { type: set, label: Flip Geometry, files: [descriptors/UUID/version/1/fg.ca/main.caml], setter_type: toggle, inverted: false, default_value: false, key: geometryFlipped, identifier: 3 } ] }示例解读format_version: 2与domain: AppDomain-com.apple.PosterBoard是当前版本的标准头部replace选项要求用户必须选择一张 PNG 图片required: true并同时替换背景与前景两张图片第二个remove为反选删除复选框默认勾选default_value: true勾选时不删除inverted: true取消勾选才删除整个descriptors/UUID3目录picker三选一选中的.ca文件会被重命名为background.ca/foreground.caset选项分别用 slider 修改duration属性、用 toggle 修改geometryFlipped属性均通过nuggetId2、3定位.caml中的节点。七、从文件到设备应用链路速览理解了两类文件的格式之后可以把打包 → 导入 → 应用的完整链路串起来均可在仓库源码中验证打包Tendies 包内按container/descriptor/descriptors组织目录Batter 包额外携带config.json以及可选的resources预览图 / banner 素材。导入解析TendieFile 识别格式与描述符数量TemplateFile 校验头部字段并实例化全部选项。界面交互TemplateFile.create_ui 依据resources/previews/banner_text/banner_stylesheet渲染横幅、预览图与各选项控件bundle_id类型额外生成 Bundle ID 输入框。应用生成PosterboardTweak.apply_tweak 先按系统版本选定structure_version61 / 59解压 tendies 与模板再通过recursive_add递归遍历容器格式原样映射描述符格式随机化 UUID 与 ID、按文件夹名路由到对应扩展set/remove选项在此过程中调用 xml_handler.py 完成.caml改写。恢复最终每个文件被包装为 FileToRestore由AppDomain-com.apple.PosterBoard或 sparserestore 路径恢复机制写入设备。Tendies 与 Batter 两类文件共同构成了 Nugget PosterBoard 生态的开放文件规范前者解决壁纸包怎么分发后者解决壁纸包怎么被用户定制。理解了这两套格式你就可以像社区壁纸作者一样产出既美观又可交互的 Nugget 壁纸与模板。附仓库相关文件索引官方格式文档documentation.mdTendie 解析src/tweaks/posterboard/tendie_file.py模板解析与 UIsrc/tweaks/posterboard/template_file.py选项基类与类型枚举src/tweaks/posterboard/template_options/template_options.py四种选项实现replace_option.py、set_option.py、remove_option.py、picker_option.pyPosterBoard 应用逻辑src/tweaks/posterboard/posterboard_tweak.pyXML.caml改写引擎src/controllers/xml_handler.pyPosterBoard 页面与导入限制src/gui/pages/tools/posterboard.py恢复文件模型src/restore/restore.py赞分享桌面应用移动开发音视频【免费下载链接】NuggetUnlock the fullest potential of your device项目地址https://gitcode.com/gh_mirrors/nug/Nugget点击查看免费下载相关推荐Nugget PosterBoard深度解析从视频到壁纸的完整转换流程想要为你的iOS设备添加独特的动态壁纸吗Nugget PosterBoard功能让你可以将任何视频文件转换为炫酷的动画壁纸 这个强大的工具不仅支持视频转桌面应用移动开发音视频AirCard-iOS .tendies壁纸刷写教程注入PosterBoard壁纸不重启iPhone即刻生效AirCard iOS .tendies壁纸刷写教程注入PosterBoard壁纸不重启iPhone即刻生效 AirCard iOS 是一款免越狱的 iOSNugget模板系统详解如何创建自定义操作和batter文件Nugget模板系统详解如何创建自定义操作和batter文件 Nugget是一款强大的iOS设备个性化工具其模板系统让用户能够轻松创建和部署自定义操作。通过桌面应用移动开发音视频创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考