Files
bili-sync/design.html

26 lines
24 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-Hans" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>工作原理 | bili-sync</title>
<meta name="description" content="由 Rust & Tokio 驱动的哔哩哔哩同步工具">
<meta name="generator" content="VitePress v1.2.3">
<link rel="preload stylesheet" href="/assets/style.C1nXIPB-.css" as="style">
<script type="module" src="/assets/chunks/metadata.ae73bf22.js"></script>
<script type="module" src="/assets/app.D7044GBf.js"></script>
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="modulepreload" href="/assets/chunks/framework.Bsyxd66g.js">
<link rel="modulepreload" href="/assets/chunks/theme.CXiSjCOs.js">
<link rel="modulepreload" href="/assets/design.md.90q2hBns.lean.js">
<link rel="icon" type="image/svg+xml" href="/icon.svg">
<link rel="icon" type="image/png" href="/icon.png">
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
</head>
<body>
<div id="app"><div class="Layout" data-v-d8b57b2d><!--[--><!--]--><!--[--><span tabindex="-1" data-v-c8291ffa></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-c8291ffa> Skip to content </a><!--]--><!----><header class="VPNav" data-v-d8b57b2d data-v-7ad780c2><div class="VPNavBar has-sidebar top" data-v-7ad780c2 data-v-844edcde><div class="wrapper" data-v-844edcde><div class="container" data-v-844edcde><div class="title" data-v-844edcde><div class="VPNavBarTitle has-sidebar" data-v-844edcde data-v-0ad69264><a class="title" href="/" data-v-0ad69264><!--[--><!--]--><!----><span data-v-0ad69264>bili-sync</span><!--[--><!--]--></a></div></div><div class="content" data-v-844edcde><div class="content-body" data-v-844edcde><!--[--><!--]--><div class="VPNavBarSearch search" data-v-844edcde><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-844edcde data-v-f732b5d0><span id="main-nav-aria-label" class="visually-hidden" data-v-f732b5d0>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-f732b5d0 data-v-08fbf4b6><!--[--><span data-v-08fbf4b6>主页</span><!--]--></a><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-f732b5d0 data-v-af5898d3><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-af5898d3><span class="text" data-v-af5898d3><!----><span data-v-af5898d3>v2.11.1</span><span class="vpi-chevron-down text-icon" data-v-af5898d3></span></span></button><div class="menu" data-v-af5898d3><div class="VPMenu" data-v-af5898d3 data-v-e42ed9b3><div class="items" data-v-e42ed9b3><!--[--><!--[--><div class="VPMenuLink" data-v-e42ed9b3 data-v-f51f088d><a class="VPLink link vp-external-link-icon" href="https://github.com/amtoaer/bili-sync/releases" target="_blank" rel="noreferrer" data-v-f51f088d><!--[-->程序更新<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e42ed9b3 data-v-f51f088d><a class="VPLink link vp-external-link-icon" href="https://github.com/search?q=repo:amtoaer/bili-sync+docs&amp;type=commits" target="_blank" rel="noreferrer" data-v-f51f088d><!--[-->文档更新<!--]--></a></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-844edcde data-v-283b26e9><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="切换到深色模式" aria-checked="false" data-v-283b26e9 data-v-7df97737 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-7df97737></span><span class="vpi-moon moon" data-v-7df97737></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-844edcde data-v-ef6192dc data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/amtoaer/bili-sync" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-358b6670><span class="vpi-social-github" /></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-844edcde data-v-8e87c032 data-v-af5898d3><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-af5898d3><span class="vpi-more-horizontal icon" data-v-af5898d3></span></button><div class="menu" data-v-af5898d3><div class="VPMenu" data-v-af5898d3 data-v-e42ed9b3><!----><!--[--><!--[--><!----><div class="group" data-v-8e87c032><div class="item appearance" data-v-8e87c032><p class="label" data-v-8e87c032>主题</p><div class="appearance-action" data-v-8e87c032><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="切换到深色模式" aria-checked="false" data-v-8e87c032 data-v-7df97737 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-7df97737></span><span class="vpi-moon moon" data-v-7df97737></span><!--]--></span></span></button></div></div></div><div class="group" data-v-8e87c032><div class="item social-links" data-v-8e87c032><div class="VPSocialLinks social-links-list" data-v-8e87c032 data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/amtoaer/bili-sync" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-358b6670><span class="vpi-social-github" /></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-844edcde data-v-6bee1efd><span class="container" data-v-6bee1efd><span class="top" data-v-6bee1efd></span><span class="middle" data-v-6bee1efd></span><span class="bottom" data-v-6bee1efd></span></span></button></div></div></div></div><div class="divider" data-v-844edcde><div class="divider-line" data-v-844edcde></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-d8b57b2d data-v-2488c25a><div class="container" data-v-2488c25a><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-2488c25a><span class="vpi-align-left menu-icon" data-v-2488c25a></span><span class="menu-text" data-v-2488c25a>菜单</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-2488c25a data-v-883964e0><button data-v-883964e0>回到顶部</button><!----></div></div></div><aside class="VPSidebar" data-v-d8b57b2d data-v-4871f9f5><div class="curtain" data-v-4871f9f5></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-4871f9f5><span class="visually-hidden" id="sidebar-aria-label" data-v-4871f9f5> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-4871f9f5><section class="VPSidebarItem level-0" data-v-4871f9f5 data-v-c24f735a><div class="item" role="button" tabindex="0" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><h2 class="text" data-v-c24f735a>简介</h2><!----></div><div class="items" data-v-c24f735a><!--[--><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/introduction" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>什么是 bili-sync</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/quick-start" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>快速开始</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-4871f9f5><section class="VPSidebarItem level-0 has-active" data-v-4871f9f5 data-v-c24f735a><div class="item" role="button" tabindex="0" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><h2 class="text" data-v-c24f735a>细节</h2><!----></div><div class="items" data-v-c24f735a><!--[--><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/configuration" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>配置说明</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/args" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>命令行参数</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/design" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>工作原理</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-4871f9f5><section class="VPSidebarItem level-0" data-v-4871f9f5 data-v-c24f735a><div class="item" role="button" tabindex="0" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><h2 class="text" data-v-c24f735a>参考</h2><!----></div><div class="items" data-v-c24f735a><!--[--><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/favorite" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>获取收藏夹信息</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/collection" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>获取合集/列表信息</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/submission" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>获取用户投稿信息</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-4871f9f5><section class="VPSidebarItem level-0" data-v-4871f9f5 data-v-c24f735a><div class="item" role="button" tabindex="0" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><h2 class="text" data-v-c24f735a>其它</h2><!----></div><div class="items" data-v-c24f735a><!--[--><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/question" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>常见问题</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-c24f735a data-v-c24f735a><div class="item" data-v-c24f735a><div class="indicator" data-v-c24f735a></div><a class="VPLink link link" href="/frontend" data-v-c24f735a><!--[--><p class="text" data-v-c24f735a>管理页</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-d8b57b2d data-v-9a6c75ad><div class="VPDoc has-sidebar has-aside" data-v-9a6c75ad data-v-e6f2a212><!--[--><!--]--><div class="container" data-v-e6f2a212><div class="aside" data-v-e6f2a212><div class="aside-curtain" data-v-e6f2a212></div><div class="aside-container" data-v-e6f2a212><div class="aside-content" data-v-e6f2a212><div class="VPDocAside" data-v-e6f2a212 data-v-cb998dce><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-cb998dce data-v-f610f197><div class="content" data-v-f610f197><div class="outline-marker" data-v-f610f197></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-f610f197>页面导航</div><ul class="VPDocOutlineItem root" data-v-f610f197 data-v-53c99d69><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-cb998dce></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-e6f2a212><div class="content-container" data-v-e6f2a212><!--[--><!--]--><main class="main" data-v-e6f2a212><div style="position:relative;" class="vp-doc _design" data-v-e6f2a212><div><h1 id="工作原理" tabindex="-1">工作原理 <a class="header-anchor" href="#工作原理" aria-label="Permalink to &quot;工作原理&quot;"></a></h1><p>本节会尽可能简单明了地介绍 <code>bili-sync</code> 的工作原理,让用户了解程序的整体执行过程。</p><h2 id="b-站的视频结构" tabindex="-1">b 站的视频结构 <a class="header-anchor" href="#b-站的视频结构" aria-label="Permalink to &quot;b 站的视频结构&quot;"></a></h2><p>在了解程序工作原理之前,我们需要先对 b 站视频的组织结构有一个大概的了解。简单来说:</p><ul><li>收藏夹、稍后再看、视频合集、视频列表等结构都是由一系列视频构成的列表;</li><li>每个视频都有唯一的 bvid包含了封面、描述和标签信息并包含了一个或多个分页</li><li>每个分页都有一个唯一的 cid包含了封面、视频、音频、弹幕。</li></ul><p>为了描述方便,在后文会将收藏夹、稍后再看这类结构统称为 video source将视频称为 video将分页称为 page。不难看出这三者有着很明显的层级关系<strong>video source 包含若干 videovideo 包含若干 page</strong></p><p>一个非常容易混淆的点是视频合集/视频列表与多页视频的区别:</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p><img src="/assets/bili_collection.DiWdQ_1C.webp" alt="bili_collection"></p><p><img src="/assets/bili_video.AE-Vm7po.webp" alt="bili_video"></p></div><p>这两张图中,上图是视频合集,下图是多页视频。这两者在展示上区别较小,但在结构上有相当大的不同。结合上面对 b 站视频结构的介绍,这个区别可以简单总结为:</p><ul><li><p><strong>视频合集是由多个仅包含单个 page 的 video 组成的 video source</strong></p></li><li><p><strong>多页视频是由多个 page 组成的 video</strong></p></li></ul><p>这说明它们是处于两个不同层级的结构,因此程序对其的处理方式也有着相当大的不同。</p><h2 id="与-emby-媒体库的对应关系" tabindex="-1">与 EMBY 媒体库的对应关系 <a class="header-anchor" href="#与-emby-媒体库的对应关系" aria-label="Permalink to &quot;与 EMBY 媒体库的对应关系&quot;"></a></h2><p>EMBY 的一般结构是: <code>媒体库 - 文件夹 - 电影/电视剧 - 分季/分集</code>,方便起见,我采用了如下的对应关系:</p><ol><li><strong>文件夹</strong>:对应 b 站的 video source</li><li><strong>电视剧</strong>:对应 b 站的 video</li><li><strong>第一季的所有分集</strong>:对应 b 站的 page。</li></ol><p>特别的,当 video 仅有一个 page 时为了避免过多的层级bili-sync 会将 page 展开到第二层级,变成与电视剧同级的电影。</p><p>因此,<strong>需要将媒体库类型设置为“混合内容”以支持在同个媒体库中同时显示电视剧与电影</strong></p><h3 id="单-page-的-video" tabindex="-1">单 page 的 video <a class="header-anchor" href="#单-page-的-video" aria-label="Permalink to &quot;单 page 的 video&quot;"></a></h3><p><img src="/assets/single_page.jTeO_DvF.webp" alt="single_page"></p><h3 id="多-page-的-video" tabindex="-1">多 page 的 video <a class="header-anchor" href="#多-page-的-video" aria-label="Permalink to &quot;多 page 的 video&quot;"></a></h3><p><img src="/assets/multi_page.tN1txa1M.webp" alt="multi_page"></p><p><img src="/assets/multi_page_detail.BYT8HIYs.webp" alt="multi_page_detail"></p><h2 id="数据库设计" tabindex="-1">数据库设计 <a class="header-anchor" href="#数据库设计" aria-label="Permalink to &quot;数据库设计&quot;"></a></h2><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>可以<a href="https://github.com/amtoaer/bili-sync/tree/main/crates/bili_sync_entity/src/entities" target="_blank" rel="noreferrer">前往此处</a>实时查看当前版本的数据库表结构。</p></div><p>既然拥有着明显的层级关系,那数据库表就很容易设计了。为了简化实现,程序没有额外考虑单个 video 被多个 video source 引用的情况(如一个视频同时在收藏夹和稍后再看中)。而是简单的将其设计为了不交叉的层级结构。</p><h3 id="video-source-表" tabindex="-1">video source 表 <a class="header-anchor" href="#video-source-表" aria-label="Permalink to &quot;video source 表&quot;"></a></h3><p>从上面的介绍可以看出video source 并不是一个具体的结构,而是拥有多种实现的抽象概念。我选择将其特化实现为多张表:</p><ol><li>favorite收藏夹</li><li>watch_later稍后再看</li><li>collection: 视频合集/视频列表;</li><li>....</li></ol><h3 id="video-表" tabindex="-1">video 表 <a class="header-anchor" href="#video-表" aria-label="Permalink to &quot;video 表&quot;"></a></h3><p>video 表包含了 video 的基本信息,如 bvid、标题、封面、描述、标签等。此外video 表还包含了与 video source 的关联。</p><p>具体来说,每一种 video source 都在 video 表中有一个对应的列,指向 video source 表中的 id如 favorite_id、collection_id 等。接下来将这些键与 video 的 bvid 绑在一起建立唯一索引,就可以保证在同一个 video source 中不会有重复的 video。</p><h3 id="page-表" tabindex="-1">page 表 <a class="header-anchor" href="#page-表" aria-label="Permalink to &quot;page 表&quot;"></a></h3><p>page 表包含了 page 的基本信息,如 cid、标题、封面等。与 video 类似但更简单page 表仅包含了与 video 的关联。</p><h2 id="执行过程" tabindex="-1">执行过程 <a class="header-anchor" href="#执行过程" aria-label="Permalink to &quot;执行过程&quot;"></a></h2><h3 id="初始化" tabindex="-1">初始化 <a class="header-anchor" href="#初始化" aria-label="Permalink to &quot;初始化&quot;"></a></h3><p>程序启动时会读取配置文件、迁移数据库、初始化日志等操作。如果发现需要的文件不存在,程序会自动创建。</p><h3 id="扫描-video-source-获取新视频" tabindex="-1">扫描 video source 获取新视频 <a class="header-anchor" href="#扫描-video-source-获取新视频" aria-label="Permalink to &quot;扫描 video source 获取新视频&quot;"></a></h3><div class="warning custom-block github-alert"><p class="custom-block-title">WARNING</p><p>b 站实现接口时为了节省资源,通过 video source 获取到的 video 列表通常是分页且不包含详细信息的。</p></div><p>程序会扫描所有配置文件中包含的 video source获取其中包含的 video 的简单信息并填充到数据库。在实现时需要避免频繁的全量扫描。</p><p>具体到 bili-sync 的实现中,每个 video source 都有一个 <code>latest_row_at</code> 列,用于记录处理过的最新视频时间。程序在请求接口时会设置按时间排序,确保新视频位于前面。排序依据的时间根据 video source 的类型而定:收藏夹按收藏时间,投稿按照投稿时间...</p><p>拉取过程会逐页请求,程序会不断将获取到的视频保存到数据库中,直到发现第一个小于等于 <code>latest_row_at</code> 的视频时停止。接着将 <code>latest_row_at</code> 更新为最新的视频时间。</p><h3 id="填充-video-详情" tabindex="-1">填充 video 详情 <a class="header-anchor" href="#填充-video-详情" aria-label="Permalink to &quot;填充 video 详情&quot;"></a></h3><p>将新增视频的简单信息写入数据库后,下一步会填充 video 详情。正如上文所述:<strong>通过 video source 获取到的 video 列表通常是不包含详细信息的</strong>,因此需要额外的请求来填充这些信息。</p><p>这一步会筛选出所有未完全填充信息的 video逐个获取 video 的详细信息(如标签、包含的 page 等)并填充到数据库中。</p><p>在这个过程中,如果遇到 -404 错误码则说明视频无法被正常访问,程序会将该视频标记为无效并跳过。</p><h3 id="下载未处理的视频" tabindex="-1">下载未处理的视频 <a class="header-anchor" href="#下载未处理的视频" aria-label="Permalink to &quot;下载未处理的视频&quot;"></a></h3><p>经过上面处理后,数据库中已经包含了所有需要的 video 和 page 信息,接下来只需要筛选其中“未完全下载”、“成功填充详细信息”的所有视频,并发下载即可。程序在 video 层级最多允许 3 个任务同时下载page 层级最多允许 2 个任务同时下载。</p><p>数据库中的 status 字段用于标记 video 和 page 的下载状态视频的各个部分封面、视频、nfo 等)包含在 status 的不同位中。程序会根据 status 的不同位来判断视频的下载状态,以此来决定是否需要下载。</p><p>如果某些部分下载失败status 字段会记录这些部分的失败次数,程序会在下次下载时重试。如果重试次数超过了设定的阈值,那么视频会被标记为下载失败,后续直接忽略。</p><p>此处程序对风控做了额外的处理,一般风控发生时接下来的所有请求都会失败,因此程序检测到风控时不会认为是某个视频下载失败,而是直接终止 video source 的全部下载任务,等待下次扫描时重试。</p></div></div></main><footer class="VPDocFooter" data-v-e6f2a212 data-v-5941af80><!--[--><!--]--><div class="edit-info" data-v-5941af80><!----><div class="last-updated" data-v-5941af80><p class="VPLastUpdated" data-v-5941af80 data-v-19a7ae4e>上次更新于: <time datetime="2026-05-07T06:47:45.000Z" data-v-19a7ae4e></time></p></div></div><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-5941af80><span class="visually-hidden" id="doc-footer-aria-label" data-v-5941af80>Pager</span><div class="pager" data-v-5941af80><a class="VPLink link pager-link prev" href="/args" data-v-5941af80><!--[--><span class="desc" data-v-5941af80>上一页</span><span class="title" data-v-5941af80>命令行参数</span><!--]--></a></div><div class="pager" data-v-5941af80><a class="VPLink link pager-link next" href="/favorite" data-v-5941af80><!--[--><span class="desc" data-v-5941af80>下一页</span><span class="title" data-v-5941af80>获取收藏夹信息</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
</body>
</html>