diff --git a/.github/workflows/lint-and-fmt.yml b/.github/workflows/lint-and-fmt.yml index 421b95359..fc10a93b9 100644 --- a/.github/workflows/lint-and-fmt.yml +++ b/.github/workflows/lint-and-fmt.yml @@ -74,3 +74,31 @@ jobs: working-directory: packages/biliass/rust run: | cargo clippy --all-targets --all-features -- -D warnings + + lint-and-fmt-docs: + name: Lint and Format (Docs) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + package_json_file: "docs/package.json" + version: "latest" + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "22" + cache: "pnpm" + cache-dependency-path: "docs/pnpm-lock.yaml" + + - name: Install dependencies + working-directory: ./docs + run: pnpm i --frozen-lockfile + + - name: Format check + working-directory: ./docs + run: pnpm fmt:check diff --git a/.github/workflows/vitepress-deploy.yml b/.github/workflows/vitepress-deploy.yml new file mode 100644 index 000000000..2214032b9 --- /dev/null +++ b/.github/workflows/vitepress-deploy.yml @@ -0,0 +1,53 @@ +name: VitePress Deploy + +on: + push: + branches: [main] + pull_request: + merge_group: + workflow_dispatch: + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: true + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + package_json_file: "docs/package.json" + version: "latest" + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "22" + cache: "pnpm" + cache-dependency-path: "docs/pnpm-lock.yaml" + + - name: Install dependencies + working-directory: ./docs + run: pnpm i --frozen-lockfile + + - name: Build VitePress site + working-directory: ./docs + run: pnpm build + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: github.ref == 'refs/heads/main' + with: + personal_token: ${{ secrets.PERSONAL_TOKEN }} + publish_dir: docs/.vitepress/dist + external_repository: SigureMo/docs + publish_branch: yutto + force_orphan: true + commit_message: ":rocket: deploy: " + user_name: "github-actions[bot]" + user_email: "github-actions[bot]@users.noreply.github.com" diff --git a/README.md b/README.md index 40b22dd26..1f4f3df3c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# yutto2.0.0 +# yutto

- +

@@ -16,15 +16,48 @@ discord chat

-

🧊 yutto,一个可爱且任性的 B 站下载器(CLI)

+

🧊 yutto,一个可爱且任性的 B 站视频下载器(CLI)

+ +**完整静态文档在这里喔 → [yutto](https://yutto.nyakku.moe/)** > [!TIP] > > 如果在使用过程中遇到问题,请通过 [Issues](https://github.com/yutto-dev/yutto/issues) 反馈功能正确性问题和功能请求,其他问题请通过 [Discussions](https://github.com/yutto-dev/yutto/discussions) 反馈~ -## 版本号为什么是 2.0 - -因为 yutto 是 [bilili](https://github.com/yutto-dev/bilili) 的後輩呀~ +## 什么是 yutto? + +yutto 是一个 B 站视频下载器,它可以帮助你下载 B 站上的投稿视频、番剧、课程等资源,支持单个视频下载、批量下载等功能,就像这样: + +```bash +❯ yutto https://www.bilibili.com/video/BV1ZEf9YiE2h/ + INFO 发现配置文件 yutto.toml,加载中…… + 大会员 成功以大会员身份登录~ + 投稿视频 植物大战僵尸融合版2.2正式版宣传片 + INFO 开始处理视频 植物大战僵尸融合版2.2正式版宣传片 + INFO 共包含以下 15 个视频流: + INFO * 0 [AVC ] [1920x1080] <1080P 60帧> #3 + INFO 1 [HEVC] [1920x1080] <1080P 60帧> #3 + INFO 2 [AV1 ] [1920x1080] <1080P 60帧> #3 + INFO 3 [AVC ] [1920x1080] <1080P 高清> #3 + INFO 4 [HEVC] [1920x1080] <1080P 高清> #3 + INFO 5 [AV1 ] [1920x1080] <1080P 高清> #3 + INFO 6 [AVC ] [1280x720 ] <720P 高清 > #3 + INFO 7 [HEVC] [1280x720 ] <720P 高清 > #3 + INFO 8 [AV1 ] [1280x720 ] <720P 高清 > #3 + INFO 9 [AVC ] [ 852x480 ] <480P 清晰 > #3 + INFO 10 [HEVC] [ 852x480 ] <480P 清晰 > #3 + INFO 11 [AV1 ] [ 852x480 ] <480P 清晰 > #3 + INFO 12 [AVC ] [ 640x360 ] <360P 流畅 > #3 + INFO 13 [HEVC] [ 640x360 ] <360P 流畅 > #3 + INFO 14 [AV1 ] [ 640x360 ] <360P 流畅 > #3 + INFO 共包含以下 3 个音频流: + INFO * 0 [MP4A] <320kbps > + INFO 1 [MP4A] < 64kbps > + INFO 2 [MP4A] <128kbps > + 弹幕 ASS 弹幕已生成 + INFO 开始下载…… +━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━━━━━━━━━━━━━━━━━━ 39.05 MiB/ 72.13 MiB 32.22 MiB/⚡ +``` ## 从安装开始~ @@ -90,28 +123,6 @@ uv tool install git+https://github.com/yutto-dev/yutto.git@main # 通过 ## 主要功能 -### 已支持的下载类型 - - -| 类型 | 是否支持选集 | 示例链接 | 默认路径模板 | -| - | - | - | - | -| 投稿视频 | - | `https://www.bilibili.com/video/BV1vZ4y1M7mQ`
`https://www.bilibili.com/video/av371660125`
`https://www.bilibili.com/video/BV1vZ4y1M7mQ?p=1`
`av371660125`
`BV1vZ4y1M7mQ` | `{title}` | -| 投稿视频 批量 | :white_check_mark: | `https://www.bilibili.com/video/BV1vZ4y1M7mQ`
`https://www.bilibili.com/video/av371660125`
`av371660125`
`BV1vZ4y1M7mQ` | `{title}/{name}` | -| 番剧 | - | `https://www.bilibili.com/bangumi/play/ep395211`
`ep395211` | `{name}` | -| 番剧 批量 | :white_check_mark: | `https://www.bilibili.com/bangumi/play/ep395211`
`https://www.bilibili.com/bangumi/play/ss38221`
`https://www.bilibili.com/bangumi/media/md28233903`
`ep395211`
`ss38221`
`md28233903` | `{title}/{name}` | -| 课程 | - | `https://www.bilibili.com/cheese/play/ep6902` | `{name}` | -| 课程 批量 | :white_check_mark: | `https://www.bilibili.com/cheese/play/ep6902`
`https://www.bilibili.com/cheese/play/ss298` | `{title}/{name}` | -| 用户指定收藏夹 批量 | :x: | `https://space.bilibili.com/100969474/favlist?fid=1306978874&ftype=create` | `{username}的收藏夹/{series_title}/{title}/{name}` | -| 当前用户稍后再看 批量 | :x: | `https://www.bilibili.com/watchlater` | `稍后再看/{title}/{name}` | -| 用户全部收藏夹 批量 | :x: | `https://space.bilibili.com/100969474/favlist` | `{username}的收藏夹/{series_title}/{title}/{name}` | -| UP 主个人空间 批量 | :x: | `https://space.bilibili.com/100969474/video` | `{username}的全部投稿视频/{title}/{name}` | -| 合集 批量 | :white_check_mark: | `https://space.bilibili.com/3546619314178489/lists?sid=3221717?type=season`
`https://space.bilibili.com/3546619314178489/channel/collectiondetail?sid=3221717`旧版页面
`https://space.bilibili.com/100969474/favlist?fid=3221717&ftype=collect&ctype=21` | `{series_title}/{title}` | -| 视频列表 批量 | :x: | `https://space.bilibili.com/100969474/lists/1947439?type=series`
`https://space.bilibili.com/100969474/channel/seriesdetail?sid=1947439`旧版页面
`https://www.bilibili.com/list/100969474?sid=1947439` | `{series_title}/{title}/{name}` | - -> [!NOTE] -> -> 标记「批量」的视频都必须通过 `-b/--batch` 参数来下载,否则会按照单个视频来解析下载 - ### 基本命令 yutto 的基本命令如下: @@ -144,764 +155,9 @@ yutto ep395211 yutto --batch https://www.bilibili.com/bangumi/play/ep395211 ``` -### 基础参数 - -yutto 支持一些基础参数,无论是批量下载还是单视频下载都适用。 - -
-点击展开详细参数 - -#### 最大并行 worker 数量 - -- 参数 `-n` 或 `--num-workers` -- 默认值 `8` - -与 bilili 不同的是,yutto 并不是使用多线程实现并行下载,而是使用协程实现的,本参数限制的是最大的并行 Worker 数量。 - -#### 指定视频清晰度等级 - -- 参数 `-q` 或 `--video-quality` -- 可选值 `127 | 126 | 125 | 120 | 116 | 112 | 100 | 80 | 74 | 64 | 32 | 16` -- 默认值 `127` - -清晰度对应关系如下 - - -| code | 清晰度 | -| :-: | :-: | -| 127 | 8K 超高清 | -| 126 | 杜比视界 | -| 125 | HDR 真彩 | -| 120 | 4K 超清 | -| 116 | 1080P 60帧 | -| 112 | 1080P 高码率 | -| 100 | 智能修复 | -| 80 | 1080P 高清 | -| 74 | 720P 60帧 | -| 64 | 720P 高清 | -| 32 | 480P 清晰 | -| 16 | 360P 流畅 | - -并不是说指定某个清晰度就一定会下载该清晰度的视频,yutto 只会尽可能满足你的要求,如果不存在指定的清晰度,yutto 就会按照默认的清晰度搜索机制进行调节,比如指定清晰度为 `80`,**首先会依次降清晰度搜索** `74`、`64`、`32`、`16`,如果依然找不到合适的则**继续升清晰度搜索** `100`、`112`、`116`、`120`、`125`、`126`、`127`。 - -值得注意的是,目前杜比视界视频只能简单下载音视频流并合并,合并后并不能达到在线观看的效果。 - -#### 指定音频码率等级 - -- 参数 `-aq` 或 `--audio-quality` -- 可选值 `30251 | 30255 | 30250 | 30280 | 30232 | 30216` -- 默认值 `30251` - -码率对应关系如下 - - -| code | 码率 | -| :-: | :-: | -| 30251 | - (Hi-Res) | -| 30255 | - (杜比音效) | -| 30250 | - (杜比全景声) | -| 30280 | 320kbps | -| 30232 | 128kbps | -| 30216 | 64kbps | - -码率自动调节机制与视频清晰度一致,也采用先降后升的匹配机制。 - -#### 指定视频编码 - -- 参数 `--vcodec` -- 下载编码可选值 `"av1" | "hevc" | "avc"` -- 保存编码可选值 FFmpeg 所有可用的视频编码器 -- 默认值 `"avc:copy"` - -该参数略微复杂,前半部分表示在下载时**优先**选择哪一种编码的视频流,后半部分则表示在合并时如何编码视频流,两者使用 `:` 分隔。 - -值得注意的是,前半的下载编码只是优先下载的编码而已,如果不存在该编码,则会根据 `--download-vcodec-priority` 自动选择其余编码,如未设置 `--download-vcodec-priority`,则会类似视频清晰度调节机制先降序后升序的方式来选择。 - -而后半部分的参数如果设置成非 `copy` 的值则可以确保在下载完成后对其进行重新编码,而且不止支持 `av1`、`hevc` 与 `avc`,只要你的 FFmpeg 支持的视频编码器,它都可以完成。 - -#### 指定音频编码 - -- 参数 `--acodec` -- 下载编码可选值 `"mp4a"` -- 保存编码可选值 FFmpeg 所有可用的音频编码器 -- 默认值 `"mp4a:copy"` - -详情同视频编码。 - -#### 指定视频下载编码优先级 - -- 参数 `--download-vcodec-priority` -- 默认值 `"auto"` -- 可选值 `"auto"` 或者使用 `,` 分隔的下载编码列表,如 `"hevc,avc,av1"` - -当使用默认值 `"auto"` 时,yutto 会类似视频清晰度调节机制先降序后升序的方式来选择。 - -当使用自定义下载编码列表时,yutto 会严格按照列表中的顺序进行选择,如果不存在则会认为该视频无视频流。 - - - -> **Warning** -> -> 如若设置本参数,请总是将 `--download-vcode-priority` 首选编码作为 `--vcodec` 的前半部分,否则可能会导致下载失败。 - -#### 指定输出格式 - -- 参数 `--output-format` -- 可选值 `"infer" | "mp4" | "mkv" | "mov"` -- 默认值 `"infer"` - -在至少包含视频流时所使用的输出格式,默认选值 `"infer"` 表示自动根据情况进行推导以保证输出的可用,推导规则如下: - -- 如果输出包含音频流且音频流编码为 `"fLaC"`,则输出格式为 `"mkv"`,因为 `"mp4"` 尚不支持 `"fLaC"` 编码 -- 否则为 `"mp4"` - -#### 指定在仅包含音频流时的输出格式 - -- 参数 `--output-format-audio-only` -- 可选值 `"infer" | "m4a" | "aac" | "mp3" | "flac" | "mp4" | "mkv" | "mov"` -- 默认值 `"infer"` - -在仅包含音频流时所使用的输出格式,默认选值 `"infer"` 表示自动根据情况进行推导以保证输出的可用,推导规则如下: - -- 如果音频流编码为 `"fLaC"`,则输出格式为 `"flac"` -- 否则为 `"m4a"` - -> **Note** -> -> 并不是仅仅在指定 `--audio-only` 时才会仅仅包含视频流,有些视频是仅包含音频流的,此时即便不指定 `--audio-only` 选项也会按照本选项的格式进行输出。 - -#### 弹幕格式选择 - -- 参数 `-df` 或 `--danmaku-format` -- 可选值 `"ass" | "xml" | "protobuf"` -- 默认值 `"ass"` - -B 站提供了 `xml` 与 `protobuf` 两种弹幕数据接口,`xml` 接口为旧接口,弹幕数上限较低,`protobuf` 接口相对较高,但不登录情况下只能获取很少的弹幕 - -为了确保无论是否登录都能获取最多的弹幕,yutto 在登录时会下载 `protobuf` 源数据,在未登录时会下载 `xml` 源数据,并将其转换为主流播放器支持的 `ass` 格式 - -如果你不喜欢 yutto 自动转换的效果,可以选择输出格式为 `xml` 或 `protobuf`,手动通过一些工具进行转换,比如 yutto 和 bilili 所使用的 [biliass](https://github.com/yutto-dev/yutto/tree/main/packages/biliass),或者使用 [us-danmaku](https://tiansh.github.io/us-danmaku/bilibili/) 进行在线转换。 - -如果你不想下载弹幕,只需要使用参数 `--no-danmaku` 即可。 - -#### 下载块大小 - -- 参数 `-bs` 或 `--block-size` -- 默认值 `0.5` - -以 MiB 为单位,为分块下载时各块大小,不建议更改。 - -#### 强制覆盖已下载文件 - -- 参数 `-w` 或 `--overwrite` -- 默认值 `False` - -#### 代理设置 - -- 参数 `-x` 或 `--proxy` -- 可选值 `"auto" | "no" | ` -- 默认值 `"auto"` - -设置代理服务器,默认是从环境变量读取,`no` 则为不设置代理,设置其它 http/https url 则将其作为代理服务器。 - -#### 存放根目录 - -- 参数 `-d` 或 `--dir` -- 默认值 `"./"` - -#### 临时文件目录 - -- 参数 `--tmp-dir` -- 默认值是“存放根目录”即 `-d, --dir` 的值 - -#### Cookies 设置 - -- 参数 `-c` 或 `--sessdata` -- 默认值 `""` - -设置 Cookies 后你才可以下载更高清晰度以及更多的剧集,当你传入你的大会员 `SESSDATA` 时(当然前提是你是大会员),你就可以下载大会员可访问的资源咯。 - -
SESSDATA 获取方式 - -这里用 Chrome 作为示例,其它浏览器请尝试类似方法。 - -首先,用你的帐号登录 B 站,然后随便打开一个 B 站网页,比如[首页](https://www.bilibili.com/)。 - -按 F12 打开开发者工具,切换到 Network 栏,刷新页面,此时第一个加载的资源应该就是当前页面的 html,选中该资源,在右侧 「Request Headers」 中找到 「cookie」,在其中找到类似于 `SESSDATA=d8bc7493%2C2843925707%2C08c3e*81;` 的一串字符串,复制这里的 `d8bc7493%2C2843925707%2C08c3e*81`,这就是你需要的 `SESSDATA`。 - -
- -另外,由于 SESSDATA 中可能有特殊符号,所以传入时你可能需要使用双引号来包裹 - -```bash -yutto -c "d8bc7493%2C2843925707%2C08c3e*81" -``` - -当然,示例里的 SESSDATA 是无效的,请使用自己的 SESSDATA。 - -#### 存放子路径模板 - -- 参数 `-tp` 或 `--subpath-template` -- 可选参数变量 `title | id | name | username | series_title | pubdate | download_date | owner_uid` (以后可能会有更多) -- 默认值 `"{auto}"` - -通过配置子路径模板可以灵活地控制视频存放位置。 - -默认情况是由 yutto 自动控制存放位置的。比如下载单个视频时默认就是直接存放在设定的根目录,不会创建一层容器目录,此时自动选择了 `{name}` 作为模板;而批量下载时则会根据视频层级生成多级目录,比如番剧会是 `{title}/{name}`,首先会在设定根目录里生成一个番剧名的目录,其内才会存放各个番剧剧集视频,这样方便了多个不同番剧的管理。当然,如果你仍希望将番剧直接存放在设定根目录下的话,可以修改该参数值为 `{name}`即可。 - -另外,该功能语法由 Python format 函数模板语法提供,所以也支持一些高级的用法,比如 `{id:0>3}{name}`,此外还专门为时间变量 🕛 增加了自定义时间模板的语法 `{pubdate@%Y-%m-%d %H:%M:%S}`,默认时间模板为 `%Y-%m-%d`。 - -值得注意的是,并不是所有变量在各种场合下都会提供,比如 `username`, `owner_uid` 变量当前仅在 UP 主全部投稿视频/收藏夹/稍后再看才提供,在其它情况下不应使用它。各变量详细作用域描述见下表: - - -| Variable | Description | Scope | -| - | - | - | -| title | 系列视频总标题(番剧名/投稿视频标题) | 全部 | -| id | 系列视频单 p 顺序标号 | 全部 | -| aid | 视频 AV 号,早期使用的视频 ID,不建议使用,详见 [AV 号全面升级公告](https://www.bilibili.com/blackboard/activity-BV-PC.html) | 全部 | -| bvid | 视频 BV 号,即视频 ID | 全部 | -| name | 系列视频单 p 标题 | 全部 | -| username | UP 主用户名 | 个人空间、收藏夹、稍后再看、合集、视频列表下载 | -| series_title | 合集标题 | 收藏夹、视频合集、视频列表下载 | -| pubdate🕛 | 投稿日期 | 仅投稿视频 | -| download_date🕛 | 下载日期 | 全部 | -| owner_uid | UP 主 UID | 个人空间、收藏夹、稍后再看、合集、视频列表下载 | - -> **Note** -> -> 未来可能会对路径变量及默认路径模板进行调整 - -#### url 别名文件路径 - -- 参数 `-af` 或 `--alias-file` -- 默认值 `None` - -指定别名文件路径,别名文件中存放一个别名与其对应的 url,使用空格或者 `=` 分隔,示例如下: - -``` -tensura1=https://www.bilibili.com/bangumi/play/ss25739/ -tensura2=https://www.bilibili.com/bangumi/play/ss36170/ -tensura-nikki=https://www.bilibili.com/bangumi/play/ss38221/ -``` - -比如将上述内容存储到 `~/.yutto_alias`,则通过以下命令即可解析该文件: - -```bash -yutto tensura1 --batch --alias-file='~/.yutto_alias' -``` - -当参数值为 `-` 时,会从标准输入中读取: - -```bash -cat ~/.yutto_alias | yutto tensura-nikki --batch --alias-file - -``` - -#### 指定媒体元数据值的格式 - -当前仅支持 `premiered` - -- 参数 `--metadata-format-premiered` -- 默认值 `"%Y-%m-%d"` -- 常用值 `"%Y-%m-%d %H:%M:%S"` - -#### 严格校验大会员状态有效 - -- 参数 `--vip-strict` -- 默认值 `False` - -#### 严格校验登录状态有效 - -- 参数 `--login-strict` -- 默认值 `False` - -#### 设置下载间隔 - -- 参数 `--download-interval` -- 默认值 `0` - -设置两话之间的下载间隔(单位为秒),避免短时间內下载大量视频导致账号被封禁 - -#### 禁用下载镜像 - -- 参数 `--banned-mirrors-pattern` -- 默认值 `None` - -使用正则禁用特定镜像,比如 `--banned-mirrors-pattern "mirrorali"` 将禁用 url 中包含 `mirrorali` 的镜像 - -#### 不显示颜色 - -- 参数 `--no-color` -- 默认值 `False` - -#### 不显示进度条 - -- 参数 `--no-progress` -- 默认值 `False` - -#### 启用 Debug 模式 - -- 参数 `--debug` -- 默认值 `False` - -
- -### 资源选择参数 - -此外有一些参数专用于资源选择,比如选择是否下载弹幕、音频、视频等等。 - -
-点击展开详细参数 - -#### 仅下载视频流 - -- 参数 `--video-only` -- 默认值 `False` - -> **Note** -> -> 这里「仅下载视频流」是指视频中音视频流仅选择视频流,而不是仅仅下载视频而不下载弹幕字幕等资源,如果需要取消字幕等资源下载,请额外使用 `--no-danmaku` 等参数。 -> -> 「仅下载音频流」也是同样的。 - -#### 仅下载音频流 - -- 参数 `--audio-only` -- 默认值 `False` - -仅下载其中的音频流,保存为 `.m4a` 文件。 - -#### 不生成弹幕文件 - -- 参数 `--no-danmaku` -- 默认值 `False` - -#### 仅生成弹幕文件 - -- 参数 `--danmaku-only` -- 默认值 `False` - -#### 不生成字幕文件 - -- 参数 `--no-subtitle` -- 默认值 `False` - -#### 仅生成字幕文件 - -- 参数 `--subtitle-only` -- 默认值 `False` - -#### 生成媒体元数据文件 - -- 参数 `--with-metadata` -- 默认值 `False` - -目前媒体元数据生成尚在试验阶段,可能提取出的信息并不完整。 - -#### 仅生成媒体元数据文件 - -- 参数 `--metadata-only` -- 默认值 `False` - -#### 不生成视频封面 - -- 参数 `--no-cover` -- 默认值 `False` - -> [!NOTE] -> -> 当前仅支持为包含视频流的视频生成封面。 - -#### 生成视频流封面时单独保存封面 - -- 参数 `--save-cover` -- 默认值 `False` - -#### 仅生成视频封面 - -- 参数 `--cover-only` -- 默认值 `False` - -#### 不生成章节信息 - -- 参数 `--no-chapter-info` -- 默认值 `False` - -不生成章节信息,包含 MetaData 和嵌入视频流的章节信息。 - -
- -### 弹幕设置参数Experimental - -yutto 通过与 biliass 的集成,提供了一些 ASS 弹幕选项,包括字号、字体、速度等~ - -
-点击展开详细参数 - -#### 弹幕字体大小 - -- 参数 `--danmaku-font-size` -- 默认值 `video_width / 40` - -#### 弹幕字体 - -- 参数 `--danmaku-font` -- 默认值 `"SimHei"` - -#### 弹幕不透明度 - -- 参数 `--danmaku-opacity` -- 默认值 `0.8` - -#### 弹幕显示区域与视频高度的比例 - -- 参数 `--danmaku-display-region-ratio` -- 默认值 `1.0` - -#### 弹幕速度 - -- 参数 `--danmaku-speed` -- 默认值 `1.0` - -#### 屏蔽顶部弹幕 - -- 参数 `--danmaku-block-top` -- 默认值 `False` - -#### 屏蔽底部弹幕 - -- 参数 `--danmaku-block-bottom` -- 默认值 `False` - -#### 屏蔽滚动弹幕 - -- 参数 `--danmaku-block-scroll` -- 默认值 `False` - -#### 屏蔽逆向弹幕 - -- 参数 `--danmaku-block-reverse` -- 默认值 `False` - -#### 屏蔽固定弹幕(顶部、底部) - -- 参数 `--danmaku-block-fixed` -- 默认值 `False` - -#### 屏蔽高级弹幕 - -- 参数 `--danmaku-block-special` -- 默认值 `False` - -#### 屏蔽彩色弹幕 - -- 参数 `--danmaku-block-colorful` -- 默认值 `False` - -#### 屏蔽关键词 - -- 参数 `--danmaku-block-keyword-patterns` -- 默认值 `None` - -按关键词屏蔽,支持正则,使用 `,` 分隔 - -
- -### 批量参数 - -有些参数是只有批量下载时才可以使用的 - -
-点击展开详细参数 - -#### 启用批量下载 - -- 参数 `-b` 或 `--batch` -- 默认值 `False` - -只需要 `yutto --batch ` 即可启用批量下载功能。 - -#### 选集 - -- 参数 `-p` 或 `--episodes` -- 默认值 `1~-1`(也即全选) - -也就是选集咯,其语法是这样的 - -- `` 单独下某一剧集 - - 支持负数来选择倒数第几话 - - 此外还可以使用 `$` 来代表 `-1` -- `~` 使用 `~` 可以连续选取(如果起始为 1,或者终止为 -1 则可以省略) -- `,,,...,` 使用 `,` 可以不连续选取 - -emmm,直接看的话大概并不能知道我在说什么,所以我们通过几个小例子来了解其语法 - -```bash -# 假设要下载一个具有 24 话的番剧 -# 如果我们只想下载第 3 话,只需要这样 -yutto -b -p 3 -# 那如果我想下载第 5 话到第 7 话呢,使用 `~` 可以连续选中 -yutto -b -p 5~7 -# 那我想下载第 12 话和第 17 话又要怎么办?此时只需要 `,` 就可以将多个不连续的选集一起选中 -yutto -b -p 12,17 -# 那我突然又想将刚才那些都选中了呢?还是使用 `,` 呀,将它们连在一起即可 -yutto -b -p 3,5~7,12,17 -# 嗯,你已经把基本用法都了解过了,很简单吧~ -# 下面是一些语法糖,不了解也完全不会影响任何功能哒~ -# 那如果我只知道我想下载倒数第 3 话,而不想算倒数第三话是第几话应该怎么办? -# 此时可以用负数哒~不过要注意的是,这种参数以 `-` 开头参数需要使用 `=` 来连接选项和参数 -yutto -b -p=-3 -# 那么如果想下载最后一话你可能会想到 `-p=-1` 对吧?不过我内置了符号 $ 用于代表最后一话 -# 像下面这样就可以直接下载最后一话啦~ -yutto -b -p $ -# 为了进一步方便表示一个范围选取,在从第一话开始选取或者以最后一话为终止时可以省略它们 -# 这样就是前三话啦(这里与以 `-` 开头类似,以 `~` 开头可能被识别为 $HOME,因此最好也用等号,或者使用引号包裹) -yutto -b -p=~3 -# 这样就是后四话啦 -yutto -b -p=-4~ -# 所有语法都了解完啦,我们看一个稍微复杂的例子 -yutto -b -p "~3,10,12~14,16,-4~" -# 很明显,上面的例子就是下载前 3 话、第 10 话、第 12 到 14 话、第 16 话以及后 4 话 -``` - -下面是一些要注意的问题 - -1. 这里使用的序号是视频的顺序序号,而不是番剧所标注的`第 n 话`,因为有可能会出现 `第 x.5 话` 等等的特殊情况,此时一定要按照顺序自行计数。 -2. 参数值里一定不要加空格 -3. 参数值开头为特殊符号时最好使用 `=` 来连接选项和参数,或者尝试使用引号包裹参数 -4. 个人空间、视频列表、收藏夹等批量下载暂不支持选集操作 - -#### 同时下载附加剧集 - -- 参数 `-s` 或 `--with-section` -- 默认值 `False` - -#### 指定稿件发布时间范围 - -- 参数 `--batch-filter-start-time` 和 `--batch-filter-end-time` 分别表示`开始`和`结束`时间,该区间**左闭右开** -- 默认 `不限制` -- 支持的格式 - - - `%Y-%m-%d` - - `%Y-%m-%d %H:%M:%S` - - 例如仅下载 2020 年投稿的视频,可以这样: - - `--batch-filter-start-time=2020-01-01 --batch-filter-end-time=2021-01-01` - -
- -### 配置文件Experimental - -yutto 自 `2.0.0-rc.3` 起增加了实验性的配置文件功能,你可以通过 `--config` 选项来指定配置文件路径,比如 - -```bash -yutto --config /path/to/config.toml -``` - -如果不指定配置文件路径,yutto 也支持配置自动发现,根据优先级,搜索路径如下: - -- 当前目录下的 `yutto.toml` -- 搜索 [`XDG_CONFIG_HOME`](https://specifications.freedesktop.org/basedir-spec/latest/) 下的 `yutto/yutto.toml` 文件 -- 非 Windows 系统下的 `~/.config/yutto/yutto.toml`,Windows 系统下的 `~/AppData/Roaming/yutto/yutto.toml` - -你可以通过配置文件来设置一些默认参数,整体上与命令行参数基本一致,下面以一些示例来展示配置文件的写法: - -```toml -# yutto.toml -#:schema https://raw.githubusercontent.com/yutto-dev/yutto/refs/heads/main/schemas/config.json -[basic] -# 设置下载目录 -dir = "/path/to/download" -# 设置临时文件目录 -tmp_dir = "/path/to/tmp" -# 设置 SESSDATA -sessdata = "***************" -# 设置大会员严格校验 -vip_strict = true -# 设置登录严格校验 -login_strict = true - -[resource] -# 不下载字幕 -require_subtitle = false - -[danmaku] -# 设置弹幕速度 -speed = 2.0 -# 设置弹幕屏蔽关键词 -block_keyword_patterns = [ - ".*keyword1.*", - ".*keyword2.*", -] - -[batch] -# 下载额外剧集 -with_section = true -``` - -如果你使用 VS Code 对配置文件编辑,强烈建议使用 [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) 扩展,配合 yutto 提供的 schema,可以获得最佳的提示体验。 +### 更多功能 -## 从 bilili1.x 迁移 - -### 取消的功能 - -- `- bilibili` 目录的生成 -- 播放列表生成 -- 源格式修改功能(不再支持 flv 源视频下载,如果仍有视频不支持 dash 源,请继续使用 bilili) -- 对 Python3.8 的支持,最低支持 Python3.9 -- 下载前询问 -- 弃用选集语法糖开始符号 `^`,直接使用明确的剧集号 `1` 即可 - -### 默认行为的修改 - -- 使用协程而非多线程进行下载 -- 默认生成弹幕为 ASS -- 默认启用从多镜像源下载的特性 -- 不仅可以控制是否使用系统代理,还能配置特定的代理服务器 - -### 新增的特性 - -- 单视频下载与批量下载命令分离(`bilili` 命令与 `yutto --batch` 相类似) -- 音频/视频编码选择 -- 可选仅下载音频/视频 -- 存放子路径的自由定制 -- 支持 url 别名 -- 支持文件列表 -- 更多的批下载支持(现已支持 UP 主全部投稿视频、视频合集、收藏夹等) -- 更加完善的 warning 与 error 提示 -- 支持仅输入 id 即可下载(aid、bvid、episode_id 等) -- 支持描述文件生成 -- 将链接解析延迟到下载前一刻,避免短时间大量请求导致 IP 被封的问题 - -## 小技巧 - -### 作为 log 输出到文件 - -虽说 yutto 不像 bilili 那样会全屏刷新,但进度条还是会一直刷新占据多行,可能影响 log 的阅读,另外颜色码也是难以阅读的,因此我们可以通过选项禁用他们: - -```bash -yutto --no-color --no-progress > log -``` - -### 使用配置自定义默认参数 - -如果你希望修改 yutto 的部分参数,那么可能每次运行都需要在后面加上长长一串选项,为了避免这个问题,你可以尝试使用配置文件 - -```toml -# ~/.config/yutto/yutto.toml -#:schema https://raw.githubusercontent.com/yutto-dev/yutto/refs/heads/main/schemas/config.json -[basic] -dir = "~/Movies/yutto" -sessdata = "***************" -num_workers = 16 -vcodec = "av1:copy" -``` - -当然,请手动修改 `sessdata` 内容为自己的 `SESSDATA` 哦~ - -> [!TIP] -> -> 本方案可替代原有的「自定义命令别名」方式~ -> ->
-> 原「自定义命令别名」方案 -> -> 在 `~/.zshrc` / `~/.bashrc` 中自定义一条 alias,像这样 -> -> ```bash -> alias ytt='yutto -d ~/Movies/yutto/ -c `cat ~/.sessdata` -n 16 --vcodec="av1:copy"' -> ``` -> -> 这样我每次只需要 `ytt ` 就可以直接使用这些参数进行下载啦~ -> -> 由于我提前在 `~/.sessdata` 存储了我的 `SESSDATA`,所以避免每次都要手动输入 cookie 的问题。 -> ->
- -### 使用 url alias - -yutto 新增的 url alias 可以让你下载正在追的番剧时不必每次都打开浏览器复制 url,只需要将追番列表存储在一个文件中,并为这些 url 起一个别名即可 - -``` -tensura-nikki=https://www.bilibili.com/bangumi/play/ss38221/ -``` - -之后下载最新话只需要 - -``` -yutto --batch tensura-nikki --alias-file=/path/to/alias-file -``` - -你同样可以通过配置文件来实现这一点(推荐) - -```toml -# ~/.config/yutto/yutto.toml -#:schema https://raw.githubusercontent.com/yutto-dev/yutto/refs/heads/main/schemas/config.json -[basic.aliases] -tensura-nikki = "https://www.bilibili.com/bangumi/play/ss38221/" -``` - -### 使用任务列表 - -现在 url 不仅支持 http/https 链接与裸 id,还支持使用文件路径与 file scheme 来用于表示文件列表,文件列表以行分隔,每行写一次命令的参数,该参数会覆盖掉主程序中所使用的参数,示例如下: - -首先将下面的文件存储到一个地方 - -``` -https://www.bilibili.com/bangumi/play/ss38221/ --batch -p $ -https://www.bilibili.com/bangumi/play/ss38260/ --batch -p $ -``` - -然后运行 - -``` -yutto file:///path/to/list -``` - -即可分别下载这两个番剧的最新一话 - -或者直接使用相对或者绝对路径也是可以的 - -``` -yutto ./path/to/list -``` - -值得注意的是,在文件列表各项里的参数优先级是高于命令里的优先级的,比如文件中使用: - -``` -tensura1 --batch -p $ --no-danmaku --vcodec="hevc:copy" -tensura2 --batch -p $ -``` - -而命令中则使用 - -``` -yutto file:///path/to/list --vcodec="avc:copy" -``` - -最终下载的 tensura1 会是 "hevc:copy",而 tensura2 则会是 "avc:copy" - -另外,文件列表也是支持 alias 的,你完全可以为该列表起一个别名,一个比较特别的用例是将你所有追番的内容放在一个文件里,然后为该文件起一个别名(比如 `subscription`),这样只需要 `yutto subscription --alias-file path/to/alias/file` 就可以达到追番效果啦~ - -最后,列表也是支持嵌套的哦(虽然没什么用 2333) - -## FAQ - -### 名字的由来 - -[《転スラ日記》第一话 00:24](https://www.bilibili.com/bangumi/play/ep395211?t=24) - -### 何谓「任性」? - -yutto 添加任何特性都需要以保证可维护性为前提,因此 yutto 不会添加过于复杂的特性,只需要满足够用即可。 - -### yutto 会替代 bilili 吗 - -yutto 自诞生以来已经过去三年多了,功能上基本可以替代 bilili 了,由于 B 站接口的不断变化,bilili 也不再适用于现在的环境,因此请 bilili 用户尽快迁移到 yutto ~ +yutto 还支持很多功能,限于篇幅不在 `README` 中展示,你可以前往 [yutto 文档](https://yutto.nyakku.moe/) 查看更多详细内容~ ## 其他应用 @@ -921,7 +177,7 @@ yutto 自诞生以来已经过去三年多了,功能上基本可以替代 bili ### future -- [ ] docs: 可爱的静态文档(WIP in [#86](https://github.com/yutto-dev/yutto/pull/86)) +- [x] docs: 可爱的静态文档(WIP in [#86](https://github.com/yutto-dev/yutto/pull/86)) - [ ] feat: 新的基于 toml 的任务列表 - [ ] refactor: 配置参数复用 pydantic 验证 - [ ] refactor: 针对视频合集优化路径变量 @@ -930,13 +186,6 @@ yutto 自诞生以来已经过去三年多了,功能上基本可以替代 bili - [ ] feat: 更多批下载支持 - [ ] feat: 以及更加可爱~ -## 参考 - -- 基本结构: -- 协程下载: -- 弹幕转换: -- 样式设计: - ## 参与贡献 请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..99e395a90 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,8 @@ +node_modules +.temp +.cache +dist/ +lib/ +*.tsbuildinfo +.DS_Store +.vitepress/cache diff --git a/docs/.prettierignore b/docs/.prettierignore new file mode 100644 index 000000000..6bc17221f --- /dev/null +++ b/docs/.prettierignore @@ -0,0 +1,6 @@ +*.html +dist/ +node_modules/ +*.min.js +lib/* +pnpm-lock.yaml diff --git a/docs/.prettierrc b/docs/.prettierrc new file mode 100644 index 000000000..80a3b854b --- /dev/null +++ b/docs/.prettierrc @@ -0,0 +1,22 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "singleQuote": true, + "trailingComma": "es5", + "semi": false, + "arrowParens": "always", + "overrides": [ + { + "files": "*.md", + "options": { + "tabWidth": 3 + } + }, + { + "files": "*.json5", + "options": { + "singleQuote": false + } + } + ] +} diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 000000000..83eceb9f2 --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,133 @@ +import { defineConfig } from 'vitepress' +import { + groupIconMdPlugin, + groupIconVitePlugin, + localIconLoader, +} from 'vitepress-plugin-group-icons' + +export default defineConfig({ + lang: 'zh-CN', + title: 'yutto', + description: '🧊 yutto,一个可爱且任性的 B 站视频下载器(CLI)', + cleanUrls: true, + head: [['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo-mini.svg' }]], + themeConfig: { + logo: '/logo-mini.svg', + nav: [ + { text: '首页', link: '/' }, + { text: '指南', link: '/guide/quick-start' }, + { text: '迁移', link: '/migration/' }, + { + text: '支持我', + items: [ + { text: '赞助', link: '/sponsor' }, + { + text: '参与贡献', + link: 'https://github.com/yutto-dev/yutto/blob/main/CONTRIBUTING.md', + }, + ], + }, + ], + + sidebar: { + '/guide': [ + { + text: '开始', + items: [ + { + text: '快速开始', + link: '/guide/quick-start', + }, + { + text: '支持的链接', + link: '/guide/supported-links', + }, + { + text: '命令行参数', + collapsed: false, + items: [ + { + text: '介绍', + link: '/guide/cli/introduction', + }, + { + text: '基础参数', + link: '/guide/cli/basic', + }, + { + text: '资源选择参数', + link: '/guide/cli/resource', + }, + { + text: '弹幕设置参数', + link: '/guide/cli/danmaku', + }, + { + text: '批量下载参数', + link: '/guide/cli/batch', + }, + ], + }, + ], + }, + { + text: '小技巧', + link: '/guide/tips', + }, + { + text: 'FAQ', + link: '/guide/faq', + }, + { + text: '交流和反馈', + link: '/guide/feedback', + }, + { + text: '注意事项', + link: '/guide/notice', + }, + { + text: '特别感谢', + link: '/guide/thanks', + }, + ], + }, + + footer: { + message: 'Released under the GPL3.0 License.', + copyright: 'Copyright © 2025-present Nyakku Shigure', + }, + + editLink: { + pattern: 'https://github.com/yutto-dev/yutto/edit/main/docs/:path', + text: '欸?我刚刚哪里说错了?你可以帮我改正一下哦~', + }, + + socialLinks: [ + { icon: 'github', link: 'https://github.com/yutto-dev/yutto' }, + { icon: 'discord', link: 'https://discord.gg/5cQGyFwsqC' }, + ], + + search: { + provider: 'local', + }, + }, + + markdown: { + image: { + lazyLoading: true, + }, + config(md) { + md.use(groupIconMdPlugin) + }, + }, + vite: { + plugins: [ + groupIconVitePlugin({ + customIcon: { + yutto: localIconLoader(import.meta.url, '../public/logo-mini.svg'), + }, + }) as any, + ], + }, +}) diff --git a/docs/.vitepress/env.d.ts b/docs/.vitepress/env.d.ts new file mode 100644 index 000000000..2f7d41bcf --- /dev/null +++ b/docs/.vitepress/env.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/docs/.vitepress/theme/Layout.vue b/docs/.vitepress/theme/Layout.vue new file mode 100644 index 000000000..617d4277a --- /dev/null +++ b/docs/.vitepress/theme/Layout.vue @@ -0,0 +1,11 @@ + + + diff --git a/docs/.vitepress/theme/components/Contributors.vue b/docs/.vitepress/theme/components/Contributors.vue new file mode 100644 index 000000000..699a502f2 --- /dev/null +++ b/docs/.vitepress/theme/components/Contributors.vue @@ -0,0 +1,42 @@ + + + diff --git a/docs/.vitepress/theme/components/GitHubUser.vue b/docs/.vitepress/theme/components/GitHubUser.vue new file mode 100644 index 000000000..85f3bf204 --- /dev/null +++ b/docs/.vitepress/theme/components/GitHubUser.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/docs/.vitepress/theme/components/Sparkler.vue b/docs/.vitepress/theme/components/Sparkler.vue new file mode 100644 index 000000000..540b2ad3d --- /dev/null +++ b/docs/.vitepress/theme/components/Sparkler.vue @@ -0,0 +1,28 @@ + + + diff --git a/docs/.vitepress/theme/index.css b/docs/.vitepress/theme/index.css new file mode 100644 index 000000000..41e0cd0b9 --- /dev/null +++ b/docs/.vitepress/theme/index.css @@ -0,0 +1,30 @@ +/** + * Colors + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-brand-1: #379c9c; + --vp-c-brand-2: #46bcc0; + --vp-c-brand-3: #67e8e2; + --vp-c-brand-soft: #29c9cf33; +} + +.dark { + --vp-c-brand-1: #32cbcb; + --vp-c-brand-2: #3dd6db; + --vp-c-brand-3: #67e8e2; + --vp-c-brand-soft: #29c9cf33; +} + +/** + * Component: Button + * -------------------------------------------------------------------------- */ + +:root { + --vp-button-brand-text: var(--vp-c-bg-soft); + --vp-button-brand-bg: var(--vp-c-brand-2); + --vp-button-brand-hover-text: var(--vp-c-bg-soft); + --vp-button-brand-hover-bg: var(--vp-c-brand-3); + --vp-button-brand-active-text: var(--vp-c-bg-soft); + --vp-button-brand-active-bg: var(--vp-c-brand-2); +} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 000000000..ee420afe0 --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,14 @@ +import { EnhanceAppContext } from 'vitepress' +import DefaultTheme from 'vitepress/theme' +import Layout from './Layout.vue' +import 'virtual:group-icons.css' +import './index.css' + +export default { + ...DefaultTheme, + Layout, + enhanceApp(ctx: EnhanceAppContext) { + // extend default theme custom behaviour. + DefaultTheme.enhanceApp(ctx) + }, +} diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/docs/env.d.ts b/docs/env.d.ts new file mode 100644 index 000000000..2f7d41bcf --- /dev/null +++ b/docs/env.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/docs/guide/cli/basic.md b/docs/guide/cli/basic.md new file mode 100644 index 000000000..1060a3f7f --- /dev/null +++ b/docs/guide/cli/basic.md @@ -0,0 +1,346 @@ +--- +aside: true +--- + +# 基础参数 + +我支持一些基础参数,无论是批量下载还是单视频下载都适用。 + +## 最大并行 worker 数量 + +- 参数 `-n` 或 `--num-workers` +- 配置项 `basic.num_workers` +- 默认值 `8` + +与 bilili 不同的是,我并不是使用多线程实现并行下载,而是使用协程实现的,本参数限制的是最大的并行 Worker 数量。 + +## 指定视频清晰度等级 + +- 参数 `-q` 或 `--video-quality` +- 配置项 `basic.video_quality` +- 可选值 `127 | 126 | 125 | 120 | 116 | 112 | 100 | 80 | 74 | 64 | 32 | 16` +- 默认值 `127` + +清晰度对应关系如下 + + +| code | 清晰度 | +| :-: | :-: | +| 127 | 8K 超高清 | +| 126 | 杜比视界 | +| 125 | HDR 真彩 | +| 120 | 4K 超清 | +| 116 | 1080P 60帧 | +| 112 | 1080P 高码率 | +| 100 | 智能修复 | +| 80 | 1080P 高清 | +| 74 | 720P 60帧 | +| 64 | 720P 高清 | +| 32 | 480P 清晰 | +| 16 | 360P 流畅 | + +并不是说指定某个清晰度就一定会下载该清晰度的视频,我只会尽可能满足你的要求,如果不存在指定的清晰度,我就会按照默认的清晰度搜索机制进行调节,比如指定清晰度为 `80`,**首先会依次降清晰度搜索** `74`、`64`、`32`、`16`,如果依然找不到合适的则**继续升清晰度搜索** `100`、`112`、`116`、`120`、`125`、`126`、`127`。 + +## 指定音频码率等级 + +- 参数 `-aq` 或 `--audio-quality` +- 配置项 `basic.audio_quality` +- 可选值 `30251 | 30255 | 30250 | 30280 | 30232 | 30216` +- 默认值 `30251` + +码率对应关系如下 + + +| code | 码率 | +| :-: | :-: | +| 30251 | - (Hi-Res) | +| 30255 | - (杜比音效) | +| 30250 | - (杜比全景声) | +| 30280 | 320kbps | +| 30232 | 128kbps | +| 30216 | 64kbps | + +码率自动调节机制与视频清晰度一致,也采用先降后升的匹配机制。 + +## 指定视频编码 + +- 参数 `--vcodec` +- 配置项 `basic.vcodec` +- 下载编码可选值 `"av1" | "hevc" | "avc"` +- 保存编码可选值 FFmpeg 所有可用的视频编码器 +- 默认值 `"avc:copy"` + +该参数略微复杂,前半部分表示在下载时**优先**选择哪一种编码的视频流,后半部分则表示在合并时如何编码视频流,两者使用 `:` 分隔。 + +值得注意的是,前半的下载编码只是优先下载的编码而已,如果不存在该编码,则会根据 `--download-vcodec-priority` 自动选择其余编码,如未设置 `--download-vcodec-priority`,则会类似视频清晰度调节机制先降序后升序的方式来选择。 + +而后半部分的参数如果设置成非 `copy` 的值则可以确保在下载完成后对其进行重新编码,而且不止支持 `av1`、`hevc` 与 `avc`,只要你的 FFmpeg 支持的视频编码器,它都可以完成。 + +## 指定音频编码 + +- 参数 `--acodec` +- 配置项 `basic.acodec` +- 下载编码可选值 `"mp4a"` +- 保存编码可选值 FFmpeg 所有可用的音频编码器 +- 默认值 `"mp4a:copy"` + +详情同视频编码。 + +## 指定视频下载编码优先级 + +- 参数 `--download-vcodec-priority` +- 配置项 `basic.download_vcodec_priority` +- 默认值 `"auto"` +- 可选值 `"auto"` 或者使用 `,` 分隔的下载编码列表,如 `"hevc,avc,av1"` + +当使用默认值 `"auto"` 时,我会类似视频清晰度调节机制先降序后升序的方式来选择。 + +当使用自定义下载编码列表时,我会严格按照列表中的顺序进行选择,如果不存在则会认为该视频无视频流。 + +::: warning + +如若设置本参数,请总是将 `--download-vcode-priority` 首选编码作为 `--vcodec` 的前半部分,否则可能会导致下载失败。 + +另外需要注意的是,作为配置项时,请直接使用列表形式,比如: + +```toml [yutto.toml] +[basic] +download_vcodec_priority = ["hevc", "avc", "av1"] +``` + +::: + +## 指定输出格式 + +- 参数 `--output-format` +- 配置项 `basic.output_format` +- 可选值 `"infer" | "mp4" | "mkv" | "mov"` +- 默认值 `"infer"` + +在至少包含视频流时所使用的输出格式,默认选值 `"infer"` 表示自动根据情况进行推导以保证输出的可用,推导规则如下: + +- 如果输出包含音频流且音频流编码为 `"fLaC"`,则输出格式为 `"mkv"`,因为 `"mp4"` 尚不支持 `"fLaC"` 编码 +- 否则为 `"mp4"` + +## 指定在仅包含音频流时的输出格式 + +- 参数 `--output-format-audio-only` +- 配置项 `basic.output_format_audio_only` +- 可选值 `"infer" | "m4a" | "aac" | "mp3" | "flac" | "mp4" | "mkv" | "mov"` +- 默认值 `"infer"` + +在仅包含音频流时所使用的输出格式,默认选值 `"infer"` 表示自动根据情况进行推导以保证输出的可用,推导规则如下: + +- 如果音频流编码为 `"fLaC"`,则输出格式为 `"flac"` +- 否则为 `"m4a"` + +::: tip + +并不是仅仅在指定 `--audio-only` 时才会仅仅包含视频流,有些视频是仅包含音频流的,此时即便不指定 `--audio-only` 选项也会按照本选项的格式进行输出。 + +::: + +## 弹幕格式选择 + +- 参数 `-df` 或 `--danmaku-format` +- 配置项 `basic.danmaku_format` +- 可选值 `"ass" | "xml" | "protobuf"` +- 默认值 `"ass"` + +B 站提供了 `xml` 与 `protobuf` 两种弹幕数据接口,`xml` 接口为旧接口,弹幕数上限较低,`protobuf` 接口相对较高,但不登录情况下只能获取很少的弹幕 + +为了确保无论是否登录都能获取最多的弹幕,我在登录时会下载 `protobuf` 源数据,在未登录时会下载 `xml` 源数据,并将其转换为主流播放器支持的 `ass` 格式 + +如果你不喜欢我自动转换的效果,可以选择输出格式为 `xml` 或 `protobuf`,手动通过一些工具进行转换,比如我和 bilili 所使用的 [biliass](https://github.com/yutto-dev/yutto/tree/main/packages/biliass),或者使用 [us-danmaku](https://tiansh.github.io/us-danmaku/bilibili/) 进行在线转换。 + +如果你不想下载弹幕,只需要使用参数 `--no-danmaku` 即可。 + +## 下载块大小 + +- 参数 `-bs` 或 `--block-size` +- 配置项 `basic.block_size` +- 默认值 `0.5` + +以 MiB 为单位,为分块下载时各块大小,不建议更改。 + +## 强制覆盖已下载文件 + +- 参数 `-w` 或 `--overwrite` +- 配置项 `basic.overwrite` +- 默认值 `False` + +## 代理设置 + +- 参数 `-x` 或 `--proxy` +- 配置项 `basic.proxy` +- 可选值 `"auto" | "no" | ` +- 默认值 `"auto"` + +设置代理服务器,默认是从环境变量读取,`no` 则为不设置代理,设置其它 http/https url 则将其作为代理服务器。 + +## 存放根目录 + +- 参数 `-d` 或 `--dir` +- 配置项 `basic.dir` +- 默认值 `"./"` + +## 临时文件目录 + +- 参数 `--tmp-dir` +- 配置项 `basic.tmp_dir` +- 默认值是“存放根目录”即 `-d, --dir` 的值 + +## Cookies 设置 + +- 参数 `-c` 或 `--sessdata` +- 配置项 `basic.sessdata` +- 默认值 `""` + +设置 Cookies 后你才可以下载更高清晰度以及更多的剧集,当你传入你的大会员 `SESSDATA` 时(当然前提是你是大会员),你就可以下载大会员可访问的资源咯。 + +::: details `SESSDATA` 获取方式 + +这里用 Chrome 作为示例,其它浏览器请尝试类似方法。 + +首先,用你的帐号登录 B 站,然后随便打开一个 B 站网页,比如[首页](https://www.bilibili.com/)。 + +按 F12 打开开发者工具,切换到 Network 栏,刷新页面,此时第一个加载的资源应该就是当前页面的 html,选中该资源,在右侧 「Request Headers」 中找到 「cookie」,在其中找到类似于 `SESSDATA=d8bc7493%2C2843925707%2C08c3e*81;` 的一串字符串,复制这里的 `d8bc7493%2C2843925707%2C08c3e*81`,这就是你需要的 `SESSDATA`。 + +::: + +另外,由于 `SESSDATA` 中可能有特殊符号,所以传入时你可能需要使用双引号来包裹 + +```bash +yutto -c "d8bc7493%2C2843925707%2C08c3e*81" +``` + +当然,示例里的 `SESSDATA` 是无效的,请使用自己的 `SESSDATA`。 + +## 存放子路径模板 + +- 参数 `-tp` 或 `--subpath-template` +- 配置项 `basic.subpath_template` +- 可选参数变量 `title | id | name | username | series_title | pubdate | download_date | owner_uid` (以后可能会有更多) +- 默认值 `"{auto}"` + +通过配置子路径模板可以灵活地控制视频存放位置。 + +默认情况是由我自动控制存放位置的。比如下载单个视频时默认就是直接存放在设定的根目录,不会创建一层容器目录,此时自动选择了 `{name}` 作为模板;而批量下载时则会根据视频层级生成多级目录,比如番剧会是 `{title}/{name}`,首先会在设定根目录里生成一个番剧名的目录,其内才会存放各个番剧剧集视频,这样方便了多个不同番剧的管理。当然,如果你仍希望将番剧直接存放在设定根目录下的话,可以修改该参数值为 `{name}`即可。 + +另外,该功能语法由 Python format 函数模板语法提供,所以也支持一些高级的用法,比如 `{id:0>3}{name}`,此外还专门为时间变量 🕛 增加了自定义时间模板的语法 `{pubdate@%Y-%m-%d %H:%M:%S}`,默认时间模板为 `%Y-%m-%d`。 + +值得注意的是,并不是所有变量在各种场合下都会提供,比如 `username`, `owner_uid` 变量当前仅在 UP 主全部投稿视频/收藏夹/稍后再看才提供,在其它情况下不应使用它。各变量详细作用域描述见下表: + + +| Variable | Description | Scope | +| - | - | - | +| title | 系列视频总标题(番剧名/投稿视频标题) | 全部 | +| id | 系列视频单 p 顺序标号 | 全部 | +| aid | 视频 AV 号,早期使用的视频 ID,不建议使用,详见 [AV 号全面升级公告](https://www.bilibili.com/blackboard/activity-BV-PC.html) | 全部 | +| bvid | 视频 BV 号,即视频 ID | 全部 | +| name | 系列视频单 p 标题 | 全部 | +| username | UP 主用户名 | 个人空间、收藏夹、稍后再看、合集、视频列表下载 | +| series_title | 合集标题 | 收藏夹、视频合集、视频列表下载 | +| pubdate🕛 | 投稿日期 | 仅投稿视频 | +| download_date🕛 | 下载日期 | 全部 | +| owner_uid | UP 主 UID | 个人空间、收藏夹、稍后再看、合集、视频列表下载 | + +> **Note** +> +> 未来可能会对路径变量及默认路径模板进行调整 + +## url 别名设置 + +- 参数 `-af` 或 `--alias-file` +- 配置项 `basic.aliases` +- 默认值 `None` + +由于 CLI 和配置文件两种界面是不一致的,别名的配置方式有着较大的区别,下面分别介绍两者。 + +### 别名文件路径设置(CLI) + +在 CLI,你需要通过 `-af`/`--alias-file` 来指定别名文件路径,并在该文件中写明别名的映射关系,使用空格或者 `=` 分隔,示例如下: + +``` +tensura1=https://www.bilibili.com/bangumi/play/ss25739/ +tensura2=https://www.bilibili.com/bangumi/play/ss36170/ +tensura-nikki=https://www.bilibili.com/bangumi/play/ss38221/ +``` + +比如将上述内容存储到 `~/.yutto_alias`,则通过以下命令即可解析该文件: + +```bash +yutto tensura1 --batch --alias-file='~/.yutto_alias' +``` + +当参数值为 `-` 时,会从标准输入中读取: + +```bash +cat ~/.yutto_alias | yutto tensura-nikki --batch --alias-file - +``` + +### 别名配置设置(配置文件) + +因为配置文件本身就是外部文件,因此可以直接通过结构化数据来表明别名映射关系,示例如下: + +```toml +[basic.aliases] +tensura1 = "https://www.bilibili.com/bangumi/play/ss25739/" +tensura2 = "https://www.bilibili.com/bangumi/play/ss36170/" +tensura-nikki = "https://www.bilibili.com/bangumi/play/ss38221/" +``` + +## 指定媒体元数据值的格式 + +当前仅支持 `premiered` + +- 参数 `--metadata-format-premiered` +- 配置项 `basic.metadata_format_premiered` +- 默认值 `"%Y-%m-%d"` +- 常用值 `"%Y-%m-%d %H:%M:%S"` + +## 设置下载间隔 + +- 参数 `--download-interval` +- 配置项 `basic.download_interval` +- 默认值 `0` + +设置两话之间的下载间隔(单位为秒),避免短时间內下载大量视频导致账号被封禁 + +## 禁用下载镜像 + +- 参数 `--banned-mirrors-pattern` +- 配置项 `basic.banned_mirrors_pattern` +- 默认值 `None` + +使用正则禁用特定镜像,比如 `--banned-mirrors-pattern "mirrorali"` 将禁用 url 中包含 `mirrorali` 的镜像 + +## 严格校验大会员状态有效 + +- 参数 `--vip-strict` +- 配置项 `basic.vip_strict` +- 默认值 `False` + +## 严格校验登录状态有效 + +- 参数 `--login-strict` +- 配置项 `basic.login_strict` +- 默认值 `False` + +## 不显示颜色 + +- 参数 `--no-color` +- 配置项 `basic.no_color` +- 默认值 `False` + +## 不显示进度条 + +- 参数 `--no-progress` +- 配置项 `basic.no_progress` +- 默认值 `False` + +## 启用 Debug 模式 + +- 参数 `--debug` +- 配置项 `basic.debug` +- 默认值 `False` diff --git a/docs/guide/cli/batch.md b/docs/guide/cli/batch.md new file mode 100644 index 000000000..8c2ad1f29 --- /dev/null +++ b/docs/guide/cli/batch.md @@ -0,0 +1,98 @@ +--- +aside: true +--- + +# 批量参数 + +有些参数是只有批量下载时才可以使用的 + +## 启用批量下载 + +- 参数 `-b` 或 `--batch` +- 配置项 「不支持」 +- 默认值 `False` + +只需要 `yutto --batch ` 即可启用批量下载功能。 + +::: warning 该选项不支持配置项 + +因为是否启用批量下载在大多数情况需要针对具体链接来选择,因此将其设置在配置项里基本是无意义的。 + +::: + +## 选集 + +- 参数 `-p` 或 `--episodes` +- 配置项 「不支持」 +- 默认值 `1~-1`(也即全选) + +也就是选集咯,其语法是这样的 + +- `` 单独下某一剧集 + - 支持负数来选择倒数第几话 + - 此外还可以使用 `$` 来代表 `-1` +- `~` 使用 `~` 可以连续选取(如果起始为 1,或者终止为 -1 则可以省略) +- `,,,...,` 使用 `,` 可以不连续选取 + +emmm,直接看的话大概并不能知道我在说什么,所以我们通过几个小例子来了解其语法 + +```bash +# 假设要下载一个具有 24 话的番剧 +# 如果我们只想下载第 3 话,只需要这样 +yutto -b -p 3 +# 那如果我想下载第 5 话到第 7 话呢,使用 `~` 可以连续选中 +yutto -b -p 5~7 +# 那我想下载第 12 话和第 17 话又要怎么办?此时只需要 `,` 就可以将多个不连续的选集一起选中 +yutto -b -p 12,17 +# 那我突然又想将刚才那些都选中了呢?还是使用 `,` 呀,将它们连在一起即可 +yutto -b -p 3,5~7,12,17 +# 嗯,你已经把基本用法都了解过了,很简单吧~ +# 下面是一些语法糖,不了解也完全不会影响任何功能哒~ +# 那如果我只知道我想下载倒数第 3 话,而不想算倒数第三话是第几话应该怎么办? +# 此时可以用负数哒~不过要注意的是,这种参数以 `-` 开头参数需要使用 `=` 来连接选项和参数 +yutto -b -p=-3 +# 那么如果想下载最后一话你可能会想到 `-p=-1` 对吧?不过我内置了符号 $ 用于代表最后一话 +# 像下面这样就可以直接下载最后一话啦~ +yutto -b -p $ +# 为了进一步方便表示一个范围选取,在从第一话开始选取或者以最后一话为终止时可以省略它们 +# 这样就是前三话啦(这里与以 `-` 开头类似,以 `~` 开头可能被识别为 $HOME,因此最好也用等号,或者使用引号包裹) +yutto -b -p=~3 +# 这样就是后四话啦 +yutto -b -p=-4~ +# 所有语法都了解完啦,我们看一个稍微复杂的例子 +yutto -b -p "~3,10,12~14,16,-4~" +# 很明显,上面的例子就是下载前 3 话、第 10 话、第 12 到 14 话、第 16 话以及后 4 话 +``` + +下面是一些要注意的问题 + +1. 这里使用的序号是视频的顺序序号,而不是番剧所标注的`第 n 话`,因为有可能会出现 `第 x.5 话` 等等的特殊情况,此时一定要按照顺序自行计数。 +2. 参数值里一定不要加空格 +3. 参数值开头为特殊符号时最好使用 `=` 来连接选项和参数,或者尝试使用引号包裹参数 +4. 个人空间、视频列表、收藏夹等批量下载暂不支持选集操作 + +::: warning 该选项不支持配置项 + +与「启用批量下载」相同,具体选集只有在具体链接时才能确定,为该选项指定配置项同样无意义。 + +::: + +## 同时下载附加剧集 + +- 参数 `-s` 或 `--with-section` +- 配置项 `batch.with_section` +- 默认值 `False` + +## 指定稿件发布时间范围 + +- 参数 `--batch-filter-start-time` 和 `--batch-filter-end-time` 分别表示`开始`和`结束`时间,该区间**左闭右开** +- 配置项 `batch.filter_start_time` 和 `batch.filter_end_time` +- 默认 `不限制` +- 支持的格式 + + - `%Y-%m-%d` + - `%Y-%m-%d %H:%M:%S` + + 例如仅下载 2020 年投稿的视频,可以这样: + + `--batch-filter-start-time=2020-01-01 --batch-filter-end-time=2021-01-01` diff --git a/docs/guide/cli/danmaku.md b/docs/guide/cli/danmaku.md new file mode 100644 index 000000000..80b8c6570 --- /dev/null +++ b/docs/guide/cli/danmaku.md @@ -0,0 +1,95 @@ +--- +aside: true +--- + +# 弹幕设置参数 + +通过与 biliass 的集成,我提供了一些 ASS 弹幕选项,包括字号、字体、速度等~ + +## 弹幕字体大小 + +- 参数 `--danmaku-font-size` +- 配置项 `danmaku.font_size` +- 默认值 `video_width / 40` + +## 弹幕字体 + +- 参数 `--danmaku-font` +- 配置项 `danmaku.font` +- 默认值 `"SimHei"` + +## 弹幕不透明度 + +- 参数 `--danmaku-opacity` +- 配置项 `danmaku.opacity` +- 默认值 `0.8` + +## 弹幕显示区域与视频高度的比例 + +- 参数 `--danmaku-display-region-ratio` +- 配置项 `danmaku.display_region_ratio` +- 默认值 `1.0` + +## 弹幕速度 + +- 参数 `--danmaku-speed` +- 配置项 `danmaku.speed` +- 默认值 `1.0` + +## 屏蔽顶部弹幕 + +- 参数 `--danmaku-block-top` +- 配置项 `danmaku.block_top` +- 默认值 `False` + +## 屏蔽底部弹幕 + +- 参数 `--danmaku-block-bottom` +- 配置项 `danmaku.block_bottom` +- 默认值 `False` + +## 屏蔽滚动弹幕 + +- 参数 `--danmaku-block-scroll` +- 配置项 `danmaku.block_scroll` +- 默认值 `False` + +## 屏蔽逆向弹幕 + +- 参数 `--danmaku-block-reverse` +- 配置项 `danmaku.block_reverse` +- 默认值 `False` + +## 屏蔽固定弹幕(顶部、底部) + +- 参数 `--danmaku-block-fixed` +- 配置项 `danmaku.block_fixed` +- 默认值 `False` + +## 屏蔽高级弹幕 + +- 参数 `--danmaku-block-special` +- 配置项 `danmaku.block_special` +- 默认值 `False` + +## 屏蔽彩色弹幕 + +- 参数 `--danmaku-block-colorful` +- 配置项 `danmaku.block_colorful` +- 默认值 `False` + +## 屏蔽关键词 + +- 参数 `--danmaku-block-keyword-patterns` +- 配置项 `danmaku.block_keyword_patterns` +- 默认值 `None` + +按关键词屏蔽,支持正则,作为 CLI 参数使用 `,` 分隔,作为配置项直接使用列表即可: + +```toml [yutto.toml] +[danmaku] +block_keyword_patterns = [ + ".*keyword1.*", + ".*keyword2.*", +] +``` diff --git a/docs/guide/cli/introduction.md b/docs/guide/cli/introduction.md new file mode 100644 index 000000000..03d8a9ea7 --- /dev/null +++ b/docs/guide/cli/introduction.md @@ -0,0 +1,106 @@ +# 命令行参数 + +既然我是基于 CLI(Command Line Interface)的工具,那么自然而然,我支持很多命令行参数来让你更好地调度我。 + +就比如说,你可以添加 `-d` 参数来指定下载的视频路径,或者使用 `-c` 参数来设置用于登录状态的 Cookie(准确来说是其中的 `SESSDATA`)。 + +## 参数的使用方式 + +如果你是第一次接触命令行,那么你可能会对这些参数的使用方式感到困惑。 + +不过,不用担心,我会在这里为你详细地介绍一下。 + +### 指定参数值 + +比如你需要修改下载路径到 `/path/to/videos`,只需要 + +```bash +yutto --dir /path/to/videos +# 或者使用短参数 -d +yutto -d /path/to/videos +# 你也可以使用 = 将参数 key 和 value 连接在一起 +yutto -d=/path/to/videos +yutto --dir=/path/to/videos +``` + +### 切换 `True` or `False` + +对于那些不需要指定具体值,只切换 `True` or `False` 的参数,你也不需要在命令中指定值,比如开启强制覆盖已下载视频选项 + +```bash +yutto --overwrite +# 或者 +yutto -w +``` + +### 多参数同时使用 + +当然,同时使用多个参数也是允许的,只需要写在一起即可,而且 `` 和其它参数都不强制要求顺序,比如下面这些命令都是合法的 + +```bash +yutto --overwrite --dir=/path/to/videos +yutto --overwrite -d /path/to/videos +yutto -w --d=/path/to/videos +``` + +## 更多参数 + +当然,这些只是冰山一角啦,我支持的参数远不止这些,你可以通过 `yutto --help` 来查看所有支持的参数。也可以前往以下页面查看具体介绍: + +- [基础参数](./basic) +- [资源选择参数](./resource) +- [弹幕设置参数](./danmaku) +- [批量下载参数](./batch) + +## 配置文件 + +当你熟悉 CLI 界面后,可能每次下载视频的时候都需要输入一长串的参数,你可能会希望有一种方式来保存常用的参数,下次下载时直接使用,这时候配置文件就派上用场啦~ + +你可以通过 `--config` 参数来指定配置文件的路径,比如 + +```bash +yutto --config /path/to/config.toml +``` + +我还支持配置自动发现,也就是说,如果不指定配置文件路径,我也会自动去以下路径查找配置文件的: + +- 当前目录下的 `yutto.toml` +- [`XDG_CONFIG_HOME`](https://specifications.freedesktop.org/basedir-spec/latest/) 下的 `yutto/yutto.toml` 文件 +- 非 Windows 系统下的 `~/.config/yutto/yutto.toml`,Windows 系统下的 `~/AppData/Roaming/yutto/yutto.toml` + +你可以通过配置文件来设置一些默认参数,整体上与命令行参数基本一致,下面以一些示例来展示配置文件的写法: + +```toml +# yutto.toml +#:schema https://raw.githubusercontent.com/yutto-dev/yutto/refs/heads/main/schemas/config.json +[basic] +# 设置下载目录 +dir = "/path/to/download" +# 设置临时文件目录 +tmp_dir = "/path/to/tmp" +# 设置 SESSDATA +sessdata = "***************" +# 设置大会员严格校验 +vip_strict = true +# 设置登录严格校验 +login_strict = true + +[resource] +# 不下载字幕 +require_subtitle = false + +[danmaku] +# 设置弹幕速度 +speed = 2.0 +# 设置弹幕屏蔽关键词 +block_keyword_patterns = [ + ".*keyword1.*", + ".*keyword2.*", +] + +[batch] +# 下载额外剧集 +with_section = true +``` + +如果你使用 VS Code 对配置文件编辑,强烈建议使用 [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) 扩展,配合我提供的 schema,可以获得最佳的提示体验。 diff --git a/docs/guide/cli/resource.md b/docs/guide/cli/resource.md new file mode 100644 index 000000000..17f5b2eb4 --- /dev/null +++ b/docs/guide/cli/resource.md @@ -0,0 +1,142 @@ +--- +aside: true +--- + +# 资源选择参数 + +这里有一些参数专用于资源选择,比如选择是否下载弹幕、音频、视频等等。 + +## 仅下载视频流 + +- 参数 `--video-only` +- 默认值 `False` + +::: tip + +这里「仅下载视频流」是指视频中音视频流仅选择视频流,而不是仅仅下载视频而不下载弹幕字幕等资源,如果需要取消字幕等资源下载,请额外使用 `--no-danmaku` 等参数。 + +「仅下载音频流」也是同样的。 + +::: + +## 仅下载音频流 + +- 参数 `--audio-only` +- 默认值 `False` + +仅下载其中的音频流,保存为 `.m4a` 文件。 + +## 不生成弹幕文件 + +- 参数 `--no-danmaku` +- 默认值 `False` + +## 仅生成弹幕文件 + +- 参数 `--danmaku-only` +- 默认值 `False` + +## 不生成字幕文件 + +- 参数 `--no-subtitle` +- 默认值 `False` + +## 仅生成字幕文件 + +- 参数 `--subtitle-only` +- 默认值 `False` + +## 生成媒体元数据文件 + +- 参数 `--with-metadata` +- 默认值 `False` + +目前媒体元数据生成尚在试验阶段,可能提取出的信息并不完整。 + +## 仅生成媒体元数据文件 + +- 参数 `--metadata-only` +- 默认值 `False` + +## 不生成视频封面 + +- 参数 `--no-cover` +- 默认值 `False` + +::: tip + +当前仅支持为包含视频流的视频生成封面。 + +::: + +## 生成视频流封面时单独保存封面 + +- 参数 `--save-cover` +- 默认值 `False` + +## 仅生成视频封面 + +- 参数 `--cover-only` +- 默认值 `False` + +## 不生成章节信息 + +- 参数 `--no-chapter-info` +- 默认值 `False` + +不生成章节信息,包含 MetaData 和嵌入视频流的章节信息。 + +## 配置项 + +与命令行界面完全不同,配置文件可以直接表明你要下载的资源类型,比如: + +```toml [yutto.toml] +[resource] +require_audio = false +require_subtitle = false +require_danmaku = false +``` + +如上配置表明了你不需要音频、字幕和弹幕资源。 + +具体配置项如下: + +### 是否需要视频流 + +- 配置项 `resource.video_only` +- 默认值 `True` + +### 是否需要音频流 + +- 配置项 `resource.require_audio` +- 默认值 `True` + +### 是否需要弹幕 + +- 配置项 `resource.require_danmaku` +- 默认值 `True` + +### 是否需要字幕 + +- 配置项 `resource.require_subtitle` +- 默认值 `True` + +### 是否需要媒体元数据 + +- 配置项 `resource.require_metadata` +- 默认值 `False` + +### 是否需要视频封面 + +- 配置项 `resource.require_cover` +- 默认值 `True` + +### 是否需要章节信息 + +- 配置项 `resource.require_chapter_info` +- 默认值 `True` + +### 生成视频流封面时单独保存封面 + +- 配置项 `resource.save_cover` +- 默认值 `False` diff --git a/docs/guide/faq.md b/docs/guide/faq.md new file mode 100644 index 000000000..73e159aff --- /dev/null +++ b/docs/guide/faq.md @@ -0,0 +1,17 @@ +# FAQ + +## 版本号为什么是 2.0 + +因为 yutto 是 [bilili](https://github.com/yutto-dev/bilili) 的後輩呀~ + +## 名字的由来 + +[《転スラ日記》第一话 00:24](https://www.bilibili.com/bangumi/play/ep395211?t=24) + +## 何谓「任性」? + +我不一定会满足你的所有需求哦,从设计者的角度,为我添加任何特性都需要以保证可维护性为前提,因此我不会支持过于复杂的特性,只需要满足大多数用户的需求即可~ + +## yutto 会替代 bilili 吗 + +我自诞生以来已经过去三年多了,功能上基本可以替代 bilili 了,由于 B 站接口的不断变化,bilili 也不再适用于现在的环境,因此请 bilili 用户尽快迁移到 yutto ~ diff --git a/docs/guide/feedback.md b/docs/guide/feedback.md new file mode 100644 index 000000000..0b2e8ca7e --- /dev/null +++ b/docs/guide/feedback.md @@ -0,0 +1,7 @@ +# 交流和反馈 + +在提出问题之前,请确定你已经大致地将本文档浏览过一遍,这样会节省彼此的一些时间。当然,如果你对文档的组织形式、内容等有任何问题的话,欢迎提出与贡献。 + +如果你有一些新的想法,可以在 GitHub 上[发起 discussion](https://github.com/yutto-dev/yutto/discussions),如果你确定你发现了一个 Bug,或者想请求添加新的功能,也可以直接[发起 Issue](https://github.com/yutto-dev/yutto/issues/)。 + +此外你也可以通过 [Discord](https://discord.com/invite/5cQGyFwsqC) 来与大家讨论,建议问题尽可能直奔主题,尽可能少询问不必要的信息。 diff --git a/docs/guide/notice.md b/docs/guide/notice.md new file mode 100644 index 000000000..be8e78b77 --- /dev/null +++ b/docs/guide/notice.md @@ -0,0 +1,9 @@ +# 注意事项 + +我的工作只是将 B 站的视频搬运到你的电脑上,**仅此而已**啦,但有些事情你可能需要知道。 + +我不会帮你下载你没有权限访问的东西,因此我不是什么破解程序,该开大会员还是去乖乖开大会员,这是对 B 站的一种支持,所以请不要对我提让我为难的要求哦,无论现在还是以后。 + +我所下载的东西只代表你有权限获取,请不要将我获取的东西随意分享破坏平台和创作者的权益,如果你这么做了的话,那就不要怪我绝情咯,只能一切后果自负哦。 + +另外我本身的开源协议是 GPL-3.0,一方面我需要 FFmpeg 前辈和 [biliass](https://github.com/yutto-dev/yutto/tree/main/packages/biliass) 的帮忙,另一方面也尽可能维护 B 站本身的权益。不过我的文档采用的是 CC0-1.0 协议。 diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md new file mode 100644 index 000000000..2f0b26cee --- /dev/null +++ b/docs/guide/quick-start.md @@ -0,0 +1,214 @@ +# 快速开始 + +嗨~你好呀~这里是 yutto,一个可爱的命令行驱动的 B 站视频下载工具~ + +我的工作方式非常简单快捷,你只需要提供给我 B 站的链接,我就可以帮你下载好视频啦~就像这样: + +
+
+
+ yutto https://www.bilibili.com/video/BV1ZEf9YiE2h/
+ INFO  发现配置文件 yutto.toml,加载中……
+ 大会员  成功以大会员身份登录~
+ 投稿视频  植物大战僵尸融合版2.2正式版宣传片
+ INFO  开始处理视频 植物大战僵尸融合版2.2正式版宣传片
+ INFO  共包含以下 15 个视频流:
+ INFO  * 0 [AVC ] [1920x1080] <1080P 60帧> #3
+ INFO    1 [HEVC] [1920x1080] <1080P 60帧> #3
+ INFO    2 [AV1 ] [1920x1080] <1080P 60帧> #3
+ INFO    3 [AVC ] [1920x1080] <1080P 高清> #3
+ INFO    4 [HEVC] [1920x1080] <1080P 高清> #3
+ INFO    5 [AV1 ] [1920x1080] <1080P 高清> #3
+ INFO    6 [AVC ] [1280x720 ] <720P 高清 > #3
+ INFO    7 [HEVC] [1280x720 ] <720P 高清 > #3
+ INFO    8 [AV1 ] [1280x720 ] <720P 高清 > #3
+ INFO    9 [AVC ] [ 852x480 ] <480P 清晰 > #3
+ INFO   10 [HEVC] [ 852x480 ] <480P 清晰 > #3
+ INFO   11 [AV1 ] [ 852x480 ] <480P 清晰 > #3
+ INFO   12 [AVC ] [ 640x360 ] <360P 流畅 > #3
+ INFO   13 [HEVC] [ 640x360 ] <360P 流畅 > #3
+ INFO   14 [AV1 ] [ 640x360 ] <360P 流畅 > #3
+ INFO  共包含以下 3 个音频流:
+ INFO  * 0 [MP4A] <320kbps >
+ INFO    1 [MP4A] < 64kbps >
+ INFO    2 [MP4A] <128kbps >
+ 弹幕  ASS 弹幕已生成
+ INFO  开始下载……
+━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━━━━━━━━━━━━━━━━━━  39.05 MiB/ 72.13 MiB 32.22 MiB/⚡
+
+
+
+ +下面我将会从安装开始介绍我的使用方式~ + +## 环境配置 + +::: tip 注意 + +如果你使用 Homebrew、paru、Docker 等可以自动帮助你配置环境的方式来安装,此部分内容可以跳过~ + +::: + +### Python 解释器安装 + +你可能会好奇,我是怎么指挥你的设备来帮助你下载想要的视频的,其实这很大程度归功于 Python 前辈的帮忙,有了 TA,我才能和你的设备正常沟通。不过有一点需要注意的就是,必须要 3.9 以上版本的 Python 前辈才可以哦,不然可能 TA 也听不懂我的一些「方言」呢~ + +如果你是 Windows,请自行去 [Python 官网](https://www.python.org/)下载并安装,安装时记得要勾选「Add to PATH」选项,不然可能需要你手动添加到环境变量。 + +macOS 及 Linux 发行版一般都自带 Python 环境,但要注意版本。 + +安装完成后可以通过 `python --version` 来确定是否正确安装,当然这里仍然需要再次注意一下版本号~ + +### FFmpeg 下载与配置 + +由于 B 站视频是需要混流合并的,因此我的正常运作离不开 FFmpeg 前辈的帮助,因此你需要事先将 TA 正确安装在你的设备上~ + +如果你所使用的操作系统是 Windows,操作有些些麻烦,你需要[手动下载](https://ffmpeg.org/download.html)后,并将可执行文件所在路径设置到到你的环境变量中~ + +::: details 详细操作 + +打开下载链接后,在 「Get packages & executable files」 部分选择 Windows 徽标,在 「Windows EXE Files」 下找到 「Windows builds by BtbN」 并点击,会跳转到一个 GitHub Releases 页面,在 「Latest release」 里就能看到最新的构建版本了~ + +下载后解压,并随便放到一个安全的地方,然后在文件夹中找到 `ffmpeg.exe`,复制其所在文件夹路径。 + +右击「此电脑」,选择属性,在其中找到「高级系统设置」 → 「环境变量」,双击 PATH,在其中添加刚刚复制的路径(非 Win10 系统操作略有差异,请自行查阅「环境变量设置」的方法)。 + +保存保存,完事啦~~~ + +::: + +当然,如果你使用的是 macOS 或者 Linux 发行版的话,直接使用自己的包管理器就能一键完成该过程。 + +:::: details 示例 + +这里给出一些示例,不会一一列举,其它的大家可以自行搜索下~ + +::: code-group + +```bash [macOS] +brew install ffmpeg +``` + +```bash [Ubuntu] +apt install ffmpeg +``` + +```bash [Arch] +pacman -S ffmpeg +``` + +::: + +:::: + +此时,你可以尝试在终端上使用 `ffmpeg -version` 命令来测试下安装是否正确,只要显示的不是 `Command not found` 之类的提示就说明成功啦~ + +## 召唤 yutto + +当当当,是时候主角登场啦~不过在此之前仍然需要学习一点小小的咒语~ + +你可以通过以下几种方式中的任意一种来召唤(安装)我,我随叫随到~ + +### 包管理器一键安装啦 + +我目前可以通过一些包管理器直接安装~这是最简单快捷的安装方式~ + +使用 Homebrew 的小伙伴可以尝试下下面的命令: + +```bash +brew tap siguremo/tap +brew install yutto +``` + +使用 [paru](https://github.com/Morganamilo/paru)(Arch 上的 AUR 包管理器)的用户可以尝试下这样的命令(感谢 @ouuan): + +```bash +paru -S yutto +``` + +### 使用 Docker + +你也可以尝试使用 docker 直接运行(运行时的更多参数需要参考下后面的内容~) + +```bash +docker run --rm -it -v /path/to/download:/app siguremo/yutto [options] +``` + +与直接本机运行不同的是,这里的下载目标路径是通过 `-v :/app` 指定的,也就是说 docker 内的我会将内容下载到 docker 里的 `/app` 目录下,与之相对应的挂载点 `` 就是下载路径。你也可以直接挂载到 `$(pwd)`,此时就和本机运行我的默认行为一致啦,也是下载到当前目录下~ + +### pip/pipx/uv 安装 需自行配置环境 + +我的所有版本均已上传到 PyPI,因此你当然可以通过 pip 来安装啦~ + +```bash +pip install yutto +``` + +当然,你也可以通过 [pipx](https://github.com/pypa/pipx)/[uv](https://github.com/astral-sh/uv) 来安装(当然,前提是你要自己先安装 TA 们) + +```bash +pipx install yutto # 使用 pipx +uv tool install yutto # 或者使用 uv +``` + +pipx/uv 会类似 Homebrew 无感地为我创建一个独立的虚拟环境,与其余环境隔离开,避免污染 pip 的环境,因此相对于 pip,pipx/uv 是更推荐的安装方式(uv 会比 pipx 更快些~)。 + +### 体验 main 分支最新特性 需自行配置环境 + +有些时候有一些在 main 分支还没有发布的新特性或者 bugfix,你可以尝试直接安装 main 分支的代码,最快的方式仍然是通过 pip 安装,只不过需要使用 git 描述符 + +```bash +pip install git+https://github.com/yutto-dev/yutto@main # 通过 pip +pipx install git+https://github.com/yutto-dev/yutto@main # 或者通过 pipx +uv tool install git+https://github.com/yutto-dev/yutto.git@main # 或者通过 uv +``` + +## yutto 一下~ + +嗯,跟随上面的指引后我应当可以正常工作啦,那么现在,请为我分配任务吧~ + +```bash +yutto +``` + +没错,为我分配任务的方式就是这么简单~ + +不过值得注意的是这样只可以下载单个视频,如果有批量下载需求的话请添加 `-b/--batch` 参数: + +```bash +yutto --batch +# 或者使用其短参数 +yutto -b +``` + +此时我会认为该 url 是一个批量下载链接,会从其中解析出多个视频链接出来进行下载。 + +下面举例说明一下: + +这里的 `` 是 [《転スラ日記》](https://www.bilibili.com/bangumi/play/ep395211) 第一话链接,那么如果你只想下载该话的话只需要运行: + +```bash +yutto https://www.bilibili.com/bangumi/play/ep395211 +``` + +或者如果你想要该番剧全集,请运行: + +```bash +yutto -b https://www.bilibili.com/bangumi/play/ep395211 +``` + +## 下一步 + +很好,到此为止你已经了解如何让我来帮你下载视频啦~ + +不过你可能会有一些问题,比如: + +- 为什么下载的视频不够清晰?我在线看的视频明明至少都有 1080P 的呀 +- 为什么需要这么冗长的 url 才能下载视频?是否有更简洁的方式? + +这里先简单卖个关子,因为这些答案你都可以在后续内容中找到,这才刚刚开始呢~ + +这里给出一些推荐内容,你可以尝试: + +- 如果你希望了解我具体都支持哪些链接,可以尝试前往 [支持的链接](./supported-links) 查看 +- 如果你希望了解我的进阶用法,可以尝试前往 [命令行参数](./cli/introduction) 查看 diff --git a/docs/guide/supported-links.md b/docs/guide/supported-links.md new file mode 100644 index 000000000..cd897ace7 --- /dev/null +++ b/docs/guide/supported-links.md @@ -0,0 +1,244 @@ +# 支持的链接 + +本页面主要介绍了我所支持的链接类型,即 `yutto ` 中的 `` 具体可以是哪些值。 + +## 一表速览 + + +| 类型 | 是否支持选集 | 示例链接 | 默认路径模板 | +| - | - | - | - | +| 投稿视频 | - | `https://www.bilibili.com/video/BV1vZ4y1M7mQ/`
`https://www.bilibili.com/video/av371660125/`
`https://www.bilibili.com/video/BV1vZ4y1M7mQ?p=1`
`av371660125`
`BV1vZ4y1M7mQ` | `{title}` | +| 投稿视频 批量 | :white_check_mark: | `https://www.bilibili.com/video/BV1vZ4y1M7mQ/`
`https://www.bilibili.com/video/av371660125/`
`av371660125`
`BV1vZ4y1M7mQ` | `{title}/{name}` | +| 番剧 | - | `https://www.bilibili.com/bangumi/play/ep395211`
`ep395211` | `{name}` | +| 番剧 批量 | :white_check_mark: | `https://www.bilibili.com/bangumi/play/ep395211`
`https://www.bilibili.com/bangumi/play/ss38221`
`https://www.bilibili.com/bangumi/media/md28233903`
`ep395211`
`ss38221`
`md28233903` | `{title}/{name}` | +| 课程 | - | `https://www.bilibili.com/cheese/play/ep6902` | `{name}` | +| 课程 批量 | :white_check_mark: | `https://www.bilibili.com/cheese/play/ep6902`
`https://www.bilibili.com/cheese/play/ss298` | `{title}/{name}` | +| 用户指定收藏夹 批量 | :x: | `https://space.bilibili.com/100969474/favlist?fid=1306978874&ftype=create` | `{username}的收藏夹/{series_title}/{title}/{name}` | +| 当前用户稍后再看 批量 | :x: | `https://www.bilibili.com/watchlater` | `稍后再看/{title}/{name}` | +| 用户全部收藏夹 批量 | :x: | `https://space.bilibili.com/100969474/favlist` | `{username}的收藏夹/{series_title}/{title}/{name}` | +| UP 主个人空间 批量 | :x: | `https://space.bilibili.com/100969474/video` | `{username}的全部投稿视频/{title}/{name}` | +| 合集 批量 | :white_check_mark: | `https://space.bilibili.com/3546619314178489/lists?sid=3221717?type=season`
`https://space.bilibili.com/3546619314178489/channel/collectiondetail?sid=3221717`旧版页面
`https://space.bilibili.com/100969474/favlist?fid=3221717&ftype=collect&ctype=21` | `{series_title}/{title}` | +| 视频列表 批量 | :x: | `https://space.bilibili.com/100969474/lists/1947439?type=series`
`https://space.bilibili.com/100969474/channel/seriesdetail?sid=1947439`旧版页面
`https://www.bilibili.com/list/100969474?sid=1947439` | `{series_title}/{title}/{name}` | + +本表格展示了我所支持的所有类型的链接,以及最终下载的文件结构。 + +值得注意的是,这里标记「批量」的视频都必须通过 `-b/--batch` 参数来下载,否则会按照单个视频来解析下载。 + +::: tip 其他链接 + +此外只要最终会重定向到我所支持的链接我都可以解析,比如以 `https://b23.tv/` 开头的短链接和 SEO 页面链接。 + +::: + +## 投稿视频 + +### 投稿视频单 P + +对于投稿视频,AV 号、BV 号的链接我都会支持,对于没有 `?p=n` 选集参数的页面,默认会认为是第一话。 + +```bash +yutto https://www.bilibili.com/video/BV1vZ4y1M7mQ/ +yutto https://www.bilibili.com/video/av371660125/ +yutto "https://www.bilibili.com/video/BV1vZ4y1M7mQ/?p=1" +``` + +另外,我还支持直接直接使用 AV 号和 BV 号作为视频链接的唯一标识符。 + +```bash +yutto BV1vZ4y1M7mQ +yutto av371660125 +yutto "BV1vZ4y1M7mQ?p=1" +``` + +### 投稿视频全集 + +与投稿视频单集相同,这同样支持 AV 号和 BV 号的链接,但 `?p=n` 的选集参数在此时是无效的,我会认为这只是一个入口链接,而你既然指定了要批量下载,那么我会解析该投稿视频的全集。如果你仍然想要指定下载剧集,请参考选集参数 `-p/--episodes`。 + +```bash +yutto -b https://www.bilibili.com/video/BV1vZ4y1M7mQ/ +yutto -b https://www.bilibili.com/video/av371660125/ +yutto -b BV1vZ4y1M7mQ +yutto -b av371660125 +yutto -b "BV1vZ4y1M7mQ?p=2" # 仍然会下载全集 +yutto -b BV1vZ4y1M7mQ -p 2 # 这样才会只下载第二话 +``` + +## 番剧 + +### 番剧单话 + +番剧的话,主要有三种入口链接,分别是能够代表单话唯一标识符的 EP 号链接、代表整季番剧的 MD 号和 SS 号。 + +对于单话下载来说,MD 号和 SS 号均无法定位到具体某一话,因此单话下载不支持 MD 号和 SS 号,仅支持 EP 号。 + +```bash +yutto https://www.bilibili.com/bangumi/play/ep395211 +yutto ep395211 +``` + +### 番剧全集 + +对于番剧全集来说,MD 号、SS 号、EP 号均可以定位到这个番剧,因此是都支持的。 + +```bash +yutto -b https://www.bilibili.com/bangumi/play/ep395211 +yutto -b https://www.bilibili.com/bangumi/play/ss38221 +yutto -b https://www.bilibili.com/bangumi/media/md28233903 +yutto -b ep395211 +yutto -b ss38221 +yutto -b md28233903 +``` + +当然,如果需要选集请使用 `-p/--episodes` 参数。 + +```bash +yutto -b md28233903 -p 2 +``` + +## 课程 + +### 课程单集 + +课程的链接是以 `cheese` 开头的,与番剧非常类似,课程也是支持 EP 号的。 + +```bash +yutto https://www.bilibili.com/cheese/play/ep6902 +``` + +注意,课程并不支持直接使用 EP 号作为唯一标识符,因为这会和番剧混淆。 + +### 课程全集 + +课程全集的下载方式也和番剧很类似,你可以使用 EP 号或者 SS 号来下载。 + +```bash +yutto -b https://www.bilibili.com/cheese/play/ep6902 +yutto -b https://www.bilibili.com/cheese/play/ss298 +``` + +同样,你不可以直接使用 EP 号和 SS 号作为唯一标识符来下载以免混淆。 + +## 用户个人空间 + +::: warning ⚠️ 注意 + +请注意公开性,比如在未登录的情况下,未公开的收藏夹是无法下载的,这可以在 `个人中心` -> `设置` 中进行调节。 + +::: + +### 用户全部投稿视频 + +如果需要下载全部投稿,只需要 `个人中心` -> `投稿` 页面链接即可。 + +```bash +yutto -b https://space.bilibili.com/100969474/video +``` + +### 用户全部收藏夹 + +如果需要下载全部收藏夹,只需要 `个人中心` -> `收藏` 页面链接即可。 + +```bash +yutto -b https://space.bilibili.com/100969474/favlist +``` + +::: warning ⚠️ 注意 + +用户收藏夹往往非常庞大,解析时很容易触发反爬机制。如遇该问题请稍等片刻后重试。(emmm,如果视频太多的话还是建议逐个收藏夹下载……) + +::: + +### 指定收藏夹 + +对于指定收藏夹,自然就是在收藏页面再次点击具体收藏夹名称后的页面。 + +```bash +yutto -b "https://space.bilibili.com/100969474/favlist?fid=1306978874" +``` + +### 当前用户稍后再看 + +稍后再看的页面链接也是支持的。 + +```bash +yutto -b https://www.bilibili.com/watchlater +``` + +当然,由于你无权访问其他用户的稍后再看,这里只支持当前用户的稍后再看下载。另一方面,这强依赖于你的登录状态,如果你处于未登录状态,那么我当然也是无法获取到你的稍后再看列表的哦~ + +### 合集和列表 + +与其他的相同,你可以在 `个人中心` -> `合集`(旧版页面为 `合集和列表`) 进入具体列表获取对应合集/列表的链接。 + +```bash +# 合集 +yutto -b "https://space.bilibili.com/3546619314178489/lists?sid=3221717?type=season" +yutto -b "https://space.bilibili.com/3546619314178489/channel/collectiondetail?sid=3221717" # 旧版页面链接 +# 列表 +yutto -b "https://space.bilibili.com/100969474/lists/1947439?type=series" +yutto -b "https://space.bilibili.com/100969474/channel/seriesdetail?sid=1947439" # 旧版页面链接 +``` + +另外,视频列表的播放页面也可以唯一定位该视频列表 + +```bash +yutto -b "https://www.bilibili.com/list/100969474?sid=1947439" +``` + +## 任务列表 + +如果你需要一次开启多个任务,可以试试将任务列表写在一个文件中,每行列出一个任务的参数。 + +```text +https://www.bilibili.com/bangumi/play/ss38221/ --batch -p $ +https://www.bilibili.com/bangumi/play/ss38260/ --batch -p $ +``` + +然后运行 + +```bash +yutto file:///path/to/list +``` + +即可分别下载这两个番剧的最新一话。 + +我会将这样的 file scheme 链接视为一个任务列表进行解析,为了方便使用,直接使用相对或者绝对路径也是可以的 + +```bash +yutto ./path/to/list +``` + +值得注意的是,在文件列表各项里的参数优先级是高于命令里的优先级的,比如文件中使用: + +```text +rimuru1 --batch -p $ --no-danmaku --vcodec="hevc:copy" +rimuru2 --batch -p $ +``` + +而命令中则使用 + +```text +yutto file:///path/to/list --vcodec="avc:copy" +``` + +最终下载的 `rimuru1` 会是 `"hevc:copy"`,而 `rimuru2` 则会是 `"avc:copy"` + +另外说一点,列表也是支持嵌套的哦(虽然没什么用 2333) + +## 配置别名 + +除去使用以上所示的链接,你可以通过配置别名来避免每次下载都去视频页面找链接。 + +比如你可以为《転スラ日記》起一个别名 `rimuru-nikki`,我们将其存在一个文件中 `~/yutto-aliases` + +```text +rimuru-nikki=https://www.bilibili.com/bangumi/play/ss38221/ +``` + +之后下载最新话只需要 + +```bash +yutto -b rimuru-nikki --alias-file=~/yutto-aliases +``` + +当然,文件列表也是支持 alias 的,你完全可以为你的常用列表起一个别名,一个比较特别的用例是将你所有追番的内容放在一个文件里,然后为该文件起一个别名(比如 `subscription`),这样只需要 `yutto subscription --alias-file path/to/alias/file` 就可以达到追番效果啦~ diff --git a/docs/guide/thanks.md b/docs/guide/thanks.md new file mode 100644 index 000000000..d9fb2176a --- /dev/null +++ b/docs/guide/thanks.md @@ -0,0 +1,50 @@ +# 特别感谢 + +## 平台以及创作者 + +感谢 [bilibili](https://www.bilibili.com/) 平台以及平台上无数优质内容的创作者。 + +## 依赖项目 + +我的正常运作离不开以下项目的支持 + +- [FFmpeg](https://github.com/FFmpeg/FFmpeg) 用于视频的合并 +- [biliass](https://github.com/yutto-dev/yutto/tree/main/packages/biliass) 用于 XML 弹幕转换为 ASS 弹幕 +- [VitePress](https://github.com/vuejs/vitepress) 本文档的生成器 + +## 参考项目 + +我在探索过程中得到了以下项目的帮助 + +- 基本结构: +- 协程下载: +- 弹幕转换: +- 样式设计: + +## 云服务 + +- [Vercel](https://vercel.com/) 提供[文档托管](https://vercel.com/siguremo/yutto-docs) + +## 贡献者 + +感谢每一位贡献者的辛勤付出 + + + +## 赞助者 + +感谢各位赞助者的资金援助 + + + + + + + + +## 以及你的支持~ + + diff --git a/docs/guide/tips.md b/docs/guide/tips.md new file mode 100644 index 000000000..8af792a4d --- /dev/null +++ b/docs/guide/tips.md @@ -0,0 +1,45 @@ +# 小技巧 + +## 作为 log 输出到文件 + +虽说我不像 bilili 前辈那样会全屏刷新,但进度条还是会一直刷新占据多行,可能影响 log 的阅读,另外颜色码也是难以阅读的,因此我们可以通过选项禁用他们: + +```bash +yutto --no-color --no-progress > log +``` + +## 使用配置自定义默认参数 + +如果你希望修改我的部分参数,那么可能每次运行都需要在后面加上长长一串选项,为了避免这个问题,你可以尝试使用配置文件 + +```toml +# ~/.config/yutto/yutto.toml +#:schema https://raw.githubusercontent.com/yutto-dev/yutto/refs/heads/main/schemas/config.json +[basic] +dir = "~/Movies/yutto" +sessdata = "***************" +num_workers = 16 +vcodec = "av1:copy" +``` + +当然,请手动修改 `sessdata` 内容为自己的 `SESSDATA` 哦~ + +:::: tip + +本方案可替代原有的「自定义命令别名」方式~ + +::: details 原「自定义命令别名」方案 + +在 `~/.zshrc` / `~/.bashrc` 中自定义一条 alias,像这样 + +```bash +alias ytt='yutto -d ~/Movies/yutto/ -c `cat ~/.sessdata` -n 16 --vcodec="av1:copy"' +``` + +这样我每次只需要 `ytt ` 就可以直接使用这些参数进行下载啦~ + +由于我提前在 `~/.sessdata` 存储了我的 `SESSDATA`,所以避免每次都要手动输入 cookie 的问题。 + +::: + +:::: diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..6ec9c538b --- /dev/null +++ b/docs/index.md @@ -0,0 +1,55 @@ +--- +layout: home + +hero: + name: yutto + text: 🧊 一个可爱且任性的 B 站视频下载器 + actions: + - theme: brand + text: 从零开始 + link: /guide/quick-start.html + - theme: alt + text: 从 bilili 迁移 + link: /migration/ + - theme: alt + text: GitHub + link: https://github.com/yutto-dev/yutto + image: + src: /logo.png + alt: yutto-logo +features: + - icon: ⚡️ + title: 快速下载 + details: 协程 + 分块下载,尽可能地利用并行性 + - icon: 📜 + title: 弹幕支持 + details: 默认支持 ASS 弹幕生成 + - icon: 🔁 + title: 断点续传 + details: 即便一次没下完也可以接着下载~ + - icon: 🌈 + title: 支持类型丰富 + details: 支持投稿视频、番剧、视频合集、收藏夹等的下载 +--- + + diff --git a/docs/migration/index.md b/docs/migration/index.md new file mode 100644 index 000000000..2fe261ea6 --- /dev/null +++ b/docs/migration/index.md @@ -0,0 +1,37 @@ +# 从 𝓫𝓲𝓵𝓲𝓵𝓲 迁移 + +## 取消的功能 + +- `- bilibili` 目录的生成 +- 播放列表生成 +- 源格式修改功能(不再支持 flv 源视频下载,如果仍有视频不支持 dash 源,请继续使用 bilili) +- 对 Python3.8 的支持,最低支持 Python3.9 +- 下载前询问 + +## 默认行为的修改 + +- 使用协程而非多线程进行下载 +- 默认生成弹幕为 ASS +- 默认启用从多镜像源下载的特性 +- 不仅可以控制是否使用系统代理,还能配置特定的代理服务器 + +## 参数名修改 + +- 「视频清晰度选择」 `-q`/`--quality` 修改为 `-q`/`--video-quality` +- 「个人信息认证」 `-c`/`--sess-data` 修改为 `-c`/`--sessdata` +- 「指定下载弹幕类型」 `--danmaku` 拆分为「弹幕格式选择」 `-df`/`--danmaku-format` 和「不生成弹幕文件」 `--no-danmaku` 两个参数 +- 「绕过系统代理」 `--disable-proxy` 集成到更为灵活的「代理设置」 `-x`/`--proxy` 参数 + +## 新增的特性 + +- 单视频下载与批量下载命令分离(`bilili` 命令与 `yutto --batch` 相类似) +- 音频/视频编码选择 +- 可选仅下载音频/视频 +- 存放子路径的自由定制 +- 支持 url 别名 +- 支持文件列表 +- 更多的批下载支持(现已支持 UP 主全部投稿视频、视频合集、收藏夹等) +- 更加完善的 warning 与 error 提示 +- 支持仅输入 id 即可下载(aid、bvid、episode_id 等) +- 支持描述文件生成 +- 并行化链接解析(超快的~) diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..2ee5cb251 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,42 @@ +{ + "name": "yutto-docs", + "version": "0.1.0", + "private": true, + "author": "Nyakku Shigure ", + "license": "CC0-1.0", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/yutto-dev/yutto.git" + }, + "bugs": { + "url": "https://github.com/yutto-dev/yutto/issues" + }, + "homepage": "https://github.com/yutto-dev/yutto#readme", + "packageManager": "pnpm@9.15.4", + "scripts": { + "dev": "vitepress dev", + "build": "vitepress build", + "serve": "vitepress serve", + "fmt": "prettier --write .", + "fmt:check": "prettier --check ." + }, + "devDependencies": { + "@moefy-canvas/core": "^0.6.0", + "@moefy-canvas/theme-sparkler": "^0.6.0", + "prettier": "^3.4.2", + "vite": "^6.0.11", + "vitepress": "^1.6.3", + "vitepress-plugin-group-icons": "^1.3.5", + "vue": "^3.5.13" + }, + "pnpm": { + "peerDependencyRules": { + "ignoreMissing": [ + "@algolia/client-search", + "react", + "react-dom", + "@types/react" + ] + } + } +} diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 000000000..426c1f85d --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,2047 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@moefy-canvas/core': + specifier: ^0.6.0 + version: 0.6.0 + '@moefy-canvas/theme-sparkler': + specifier: ^0.6.0 + version: 0.6.0 + prettier: + specifier: ^3.4.2 + version: 3.4.2 + vite: + specifier: ^6.0.11 + version: 6.0.11 + vitepress: + specifier: ^1.6.3 + version: 1.6.3(@algolia/client-search@5.20.0)(postcss@8.5.1)(search-insights@2.17.3) + vitepress-plugin-group-icons: + specifier: ^1.3.5 + version: 1.3.5 + vue: + specifier: ^3.5.13 + version: 3.5.13 + +packages: + + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.20.0': + resolution: {integrity: sha512-YaEoNc1Xf2Yk6oCfXXkZ4+dIPLulCx8Ivqj0OsdkHWnsI3aOJChY5qsfyHhDBNSOhqn2ilgHWxSfyZrjxBcAww==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.20.0': + resolution: {integrity: sha512-CIT9ni0+5sYwqehw+t5cesjho3ugKQjPVy/iPiJvtJX4g8Cdb6je6SPt2uX72cf2ISiXCAX9U3cY0nN0efnRDw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.20.0': + resolution: {integrity: sha512-iSTFT3IU8KNpbAHcBUJw2HUrPnMXeXLyGajmCL7gIzWOsYM4GabZDHXOFx93WGiXMti1dymz8k8R+bfHv1YZmA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.20.0': + resolution: {integrity: sha512-w9RIojD45z1csvW1vZmAko82fqE/Dm+Ovsy2ElTsjFDB0HMAiLh2FO86hMHbEXDPz6GhHKgGNmBRiRP8dDPgJg==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.20.0': + resolution: {integrity: sha512-p/hftHhrbiHaEcxubYOzqVV4gUqYWLpTwK+nl2xN3eTrSW9SNuFlAvUBFqPXSVBqc6J5XL9dNKn3y8OA1KElSQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.20.0': + resolution: {integrity: sha512-m4aAuis5vZi7P4gTfiEs6YPrk/9hNTESj3gEmGFgfJw3hO2ubdS4jSId1URd6dGdt0ax2QuapXufcrN58hPUcw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.20.0': + resolution: {integrity: sha512-KL1zWTzrlN4MSiaK1ea560iCA/UewMbS4ZsLQRPoDTWyrbDKVbztkPwwv764LAqgXk0fvkNZvJ3IelcK7DqhjQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.20.0': + resolution: {integrity: sha512-shj2lTdzl9un4XJblrgqg54DoK6JeKFO8K8qInMu4XhE2JuB8De6PUuXAQwiRigZupbI0xq8aM0LKdc9+qiLQA==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.20.0': + resolution: {integrity: sha512-aF9blPwOhKtWvkjyyXh9P5peqmhCA1XxLBRgItT+K6pbT0q4hBDQrCid+pQZJYy4HFUKjB/NDDwyzFhj/rwKhw==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.20.0': + resolution: {integrity: sha512-T6B/WPdZR3b89/F9Vvk6QCbt/wrLAtrGoL8z4qPXDFApQ8MuTFWbleN/4rHn6APWO3ps+BUePIEbue2rY5MlRw==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.20.0': + resolution: {integrity: sha512-t6//lXsq8E85JMenHrI6mhViipUT5riNhEfCcvtRsTV+KIBpC6Od18eK864dmBhoc5MubM0f+sGpKOqJIlBSCg==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.20.0': + resolution: {integrity: sha512-FHxYGqRY+6bgjKsK4aUsTAg6xMs2S21elPe4Y50GB0Y041ihvw41Vlwy2QS6K9ldoftX4JvXodbKTcmuQxywdQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.20.0': + resolution: {integrity: sha512-kmtQClq/w3vtPteDSPvaW9SPZL/xrIgMrxZyAgsFwrJk0vJxqyC5/hwHmrCraDnStnGSADnLpBf4SpZnwnkwWw==} + engines: {node: '>= 14.0.0'} + + '@antfu/install-pkg@0.4.1': + resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} + + '@antfu/utils@0.7.10': + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.26.7': + resolution: {integrity: sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.26.7': + resolution: {integrity: sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==} + engines: {node: '>=6.9.0'} + + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + + '@docsearch/js@3.8.2': + resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} + + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@iconify-json/logos@1.2.4': + resolution: {integrity: sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==} + + '@iconify-json/simple-icons@1.2.23': + resolution: {integrity: sha512-ySyZ0ZXdNveWnR71t7XGV7jhknxSlTtpM2TyIR1cUHTUzZLP36hYHTNqb2pYYsCzH5ed85KTTKz7vYT33FyNIQ==} + + '@iconify-json/vscode-icons@1.2.11': + resolution: {integrity: sha512-V0ldtWPUKe7ZB3CV/TjgDW1Gbz74AxjGPS2NBWDTSn/y25gTwFycI1YcrluDhuVSoQpDEIYmm3JQJkhtSefh7A==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@2.2.1': + resolution: {integrity: sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@moefy-canvas/core@0.6.0': + resolution: {integrity: sha512-i3GUeqfiEVs6Uuk32x/LYw8W0g8E9jc7y28hjZj/H1/PASYhN++lFmvJc0MhbWqjNmaUBfaWkUFIYw3RqnGcjQ==} + + '@moefy-canvas/theme-sparkler@0.6.0': + resolution: {integrity: sha512-w/a91ZhsuCW74vXloDbxAYziiRHex5Es2hmvKQBs0Tqbx1/P/qniULj1Zh5Et7oPCyCuPQt0A/GACZpxuVWt6w==} + + '@moefy-canvas/utils@0.6.0': + resolution: {integrity: sha512-QYW5FP8PhXTG0Y7fBkGDBNFLFNp70amwMqJiDvVYcDjSJVZYiZuvxrgvfKZYp+piL9oKBbfxgzYir9gbwZpZjA==} + + '@rollup/rollup-android-arm-eabi@4.34.0': + resolution: {integrity: sha512-Eeao7ewDq79jVEsrtWIj5RNqB8p2knlm9fhR6uJ2gqP7UfbLrTrxevudVrEPDM7Wkpn/HpRC2QfazH7MXLz3vQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.34.0': + resolution: {integrity: sha512-yVh0Kf1f0Fq4tWNf6mWcbQBCLDpDrDEl88lzPgKhrgTcDrTtlmun92ywEF9dCjmYO3EFiSuJeeo9cYRxl2FswA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.34.0': + resolution: {integrity: sha512-gCs0ErAZ9s0Osejpc3qahTsqIPUDjSKIyxK/0BGKvL+Tn0n3Kwvj8BrCv7Y5sR1Ypz1K2qz9Ny0VvkVyoXBVUQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.34.0': + resolution: {integrity: sha512-aIB5Anc8hngk15t3GUkiO4pv42ykXHfmpXGS+CzM9CTyiWyT8HIS5ygRAy7KcFb/wiw4Br+vh1byqcHRTfq2tQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.34.0': + resolution: {integrity: sha512-kpdsUdMlVJMRMaOf/tIvxk8TQdzHhY47imwmASOuMajg/GXpw8GKNd8LNwIHE5Yd1onehNpcUB9jHY6wgw9nHQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.34.0': + resolution: {integrity: sha512-D0RDyHygOBCQiqookcPevrvgEarN0CttBecG4chOeIYCNtlKHmf5oi5kAVpXV7qs0Xh/WO2RnxeicZPtT50V0g==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.34.0': + resolution: {integrity: sha512-mCIw8j5LPDXmCOW8mfMZwT6F/Kza03EnSr4wGYEswrEfjTfVsFOxvgYfuRMxTuUF/XmRb9WSMD5GhCWDe2iNrg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.34.0': + resolution: {integrity: sha512-AwwldAu4aCJPob7zmjuDUMvvuatgs8B/QiVB0KwkUarAcPB3W+ToOT+18TQwY4z09Al7G0BvCcmLRop5zBLTag==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.34.0': + resolution: {integrity: sha512-e7kDUGVP+xw05pV65ZKb0zulRploU3gTu6qH1qL58PrULDGxULIS0OSDQJLH7WiFnpd3ZKUU4VM3u/Z7Zw+e7Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.34.0': + resolution: {integrity: sha512-SXYJw3zpwHgaBqTXeAZ31qfW/v50wq4HhNVvKFhRr5MnptRX2Af4KebLWR1wpxGJtLgfS2hEPuALRIY3LPAAcA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.34.0': + resolution: {integrity: sha512-e5XiCinINCI4RdyU3sFyBH4zzz7LiQRvHqDtRe9Dt8o/8hTBaYpdPimayF00eY2qy5j4PaaWK0azRgUench6WQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.0': + resolution: {integrity: sha512-3SWN3e0bAsm9ToprLFBSro8nJe6YN+5xmB11N4FfNf92wvLye/+Rh5JGQtKOpwLKt6e61R1RBc9g+luLJsc23A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.34.0': + resolution: {integrity: sha512-B1Oqt3GLh7qmhvfnc2WQla4NuHlcxAD5LyueUi5WtMc76ZWY+6qDtQYqnxARx9r+7mDGfamD+8kTJO0pKUJeJA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.34.0': + resolution: {integrity: sha512-UfUCo0h/uj48Jq2lnhX0AOhZPSTAq3Eostas+XZ+GGk22pI+Op1Y6cxQ1JkUuKYu2iU+mXj1QjPrZm9nNWV9rg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.34.0': + resolution: {integrity: sha512-chZLTUIPbgcpm+Z7ALmomXW8Zh+wE2icrG+K6nt/HenPLmtwCajhQC5flNSk1Xy5EDMt/QAOz2MhzfOfJOLSiA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.34.0': + resolution: {integrity: sha512-jo0UolK70O28BifvEsFD/8r25shFezl0aUk2t0VJzREWHkq19e+pcLu4kX5HiVXNz5qqkD+aAq04Ct8rkxgbyQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.34.0': + resolution: {integrity: sha512-Vmg0NhAap2S54JojJchiu5An54qa6t/oKT7LmDaWggpIcaiL8WcWHEN6OQrfTdL6mQ2GFyH7j2T5/3YPEDOOGA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.34.0': + resolution: {integrity: sha512-CV2aqhDDOsABKHKhNcs1SZFryffQf8vK2XrxP6lxC99ELZAdvsDgPklIBfd65R8R+qvOm1SmLaZ/Fdq961+m7A==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.34.0': + resolution: {integrity: sha512-g2ASy1QwHP88y5KWvblUolJz9rN+i4ZOsYzkEwcNfaNooxNUXG+ON6F5xFo0NIItpHqxcdAyls05VXpBnludGw==} + cpu: [x64] + os: [win32] + + '@shikijs/core@2.3.0': + resolution: {integrity: sha512-N1r7sIXYm31Zju3CmH6bH6tgIftF0NlHxx0HiZ85X2BLWczgFLo61PJCoF7nFZUu8DeS0g8xhZsiL8zi+tV8YQ==} + + '@shikijs/engine-javascript@2.3.0': + resolution: {integrity: sha512-/ITg36HXHXP486+6zaQgP7Y38LIV9V2Pir4aIk7zQ5XcM1CU8dOlUL9BuyA/E6BKg1XaAgN+rFg3qsaspt+x2A==} + + '@shikijs/engine-oniguruma@2.3.0': + resolution: {integrity: sha512-8nBH/QwDrJLxbIFg7ifrfyEtrW0m9FCfanxU9SJFUEbA+rFNbyDGdoXZ4IxkC6ykT1+Utx2vW6EYVAUk3Q9gcg==} + + '@shikijs/langs@2.3.0': + resolution: {integrity: sha512-gaNaqbUhncigokGehwhzGe5AY+IRJKDnp+1Zp3gjxhhv6RxMYtbn9zDu3cl9ngNZGEdtEtvruz6LasdY3n0MCA==} + + '@shikijs/themes@2.3.0': + resolution: {integrity: sha512-2M3XKry66lB975B7v+kSesGyIJrhxsLdk/RyhB+hbkXmD2Myyuspyox0/2JTqWnM1Y9132NjeVsBl1ZI507ZXw==} + + '@shikijs/transformers@2.3.0': + resolution: {integrity: sha512-y/9RIr/YWENiZpoMbo4GkKpxgYwuP1CZTFgi1lvnxBY9zvwRHhQz62rCH+md34A995Vap1jQmkkG+kye7wotgw==} + + '@shikijs/types@2.3.0': + resolution: {integrity: sha512-rC8ZkfIE6m17RBGwRPoXqj/an4JPUnAB1JGHELPVrzygyB0Gqa9Lc7h4xYb8c3GWywAgrjryLAJSN3kpe1fqhw==} + + '@shikijs/vscode-textmate@10.0.1': + resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/web-bluetooth@0.0.20': + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-vue@5.2.1': + resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + '@vue/compiler-core@3.5.13': + resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} + + '@vue/compiler-dom@3.5.13': + resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + + '@vue/compiler-sfc@3.5.13': + resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} + + '@vue/compiler-ssr@3.5.13': + resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + + '@vue/devtools-api@7.7.1': + resolution: {integrity: sha512-Cexc8GimowoDkJ6eNelOPdYIzsu2mgNyp0scOQ3tiaYSb9iok6LOESSsJvHaI+ib3joRfqRJNLkHFjhNuWA5dg==} + + '@vue/devtools-kit@7.7.1': + resolution: {integrity: sha512-yhZ4NPnK/tmxGtLNQxmll90jIIXdb2jAhPF76anvn5M/UkZCiLJy28bYgPIACKZ7FCosyKoaope89/RsFJll1w==} + + '@vue/devtools-shared@7.7.1': + resolution: {integrity: sha512-BtgF7kHq4BHG23Lezc/3W2UhK2ga7a8ohAIAGJMBr4BkxUFzhqntQtCiuL1ijo2ztWnmusymkirgqUrXoQKumA==} + + '@vue/reactivity@3.5.13': + resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} + + '@vue/runtime-core@3.5.13': + resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} + + '@vue/runtime-dom@3.5.13': + resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} + + '@vue/server-renderer@3.5.13': + resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} + peerDependencies: + vue: 3.5.13 + + '@vue/shared@3.5.13': + resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + + '@vueuse/core@12.5.0': + resolution: {integrity: sha512-GVyH1iYqNANwcahAx8JBm6awaNgvR/SwZ1fjr10b8l1HIgDp82ngNbfzJUgOgWEoxjL+URAggnlilAEXwCOZtg==} + + '@vueuse/integrations@12.5.0': + resolution: {integrity: sha512-HYLt8M6mjUfcoUOzyBcX2RjpfapIwHPBmQJtTmXOQW845Y/Osu9VuTJ5kPvnmWJ6IUa05WpblfOwZ+P0G4iZsQ==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@12.5.0': + resolution: {integrity: sha512-Ui7Lo2a7AxrMAXRF+fAp9QsXuwTeeZ8fIB9wsLHqzq9MQk+2gMYE2IGJW48VMJ8ecvCB3z3GsGLKLbSasQ5Qlg==} + + '@vueuse/shared@12.5.0': + resolution: {integrity: sha512-vMpcL1lStUU6O+kdj6YdHDixh0odjPAUM15uJ9f7MY781jcYkIwFA4iv2EfoIPO6vBmvutI1HxxAwmf0cx5ISQ==} + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + + algoliasearch@5.20.0: + resolution: {integrity: sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==} + engines: {node: '>= 14.0.0'} + + birpc@0.2.19: + resolution: {integrity: sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + color2k@2.0.3: + resolution: {integrity: sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + focus-trap@7.6.4: + resolution: {integrity: sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + globals@15.14.0: + resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} + engines: {node: '>=18'} + + hast-util-to-html@9.0.4: + resolution: {integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.1: + resolution: {integrity: sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==} + + minisearch@7.1.1: + resolution: {integrity: sha512-b3YZEYCEH4EdCAtYP7OlDyx7FdPwNzuNwLQ34SfJpM9dlbBZzeXndGavTrC+VCiRWomL21SWfMc6SCKO/U2ZNw==} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + oniguruma-to-es@3.1.0: + resolution: {integrity: sha512-BJ3Jy22YlgejHSO7Fvmz1kKazlaPmRSUH+4adTDUS/dKQ4wLxI+gALZ8updbaux7/m7fIlpgOZ5fp/Inq5jUAw==} + + package-manager-detector@0.2.9: + resolution: {integrity: sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==} + + pathe@2.0.2: + resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.25.4: + resolution: {integrity: sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==} + + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} + engines: {node: '>=14'} + hasBin: true + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup@4.34.0: + resolution: {integrity: sha512-+4C/cgJ9w6sudisA0nZz0+O7lTP9a3CzNLsoDwaRumM8QHwghUsu6tqHXiTmNUp/rqNiM14++7dkzHDyCRs0Jg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + + shiki@2.3.0: + resolution: {integrity: sha512-wMmrvyxj4i8ft9r2dA+aIi5+G6PL0Dz19h5fr5xG7Jvo8uLIOWxaveSRl3LNcj58h+jUnhdkCh7tQIVULGNXJw==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + superjson@2.2.2: + resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} + engines: {node: '>=16'} + + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + ts-debounce@4.0.0: + resolution: {integrity: sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==} + + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@5.4.14: + resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vite@6.0.11: + resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitepress-plugin-group-icons@1.3.5: + resolution: {integrity: sha512-1f1NP7osRYlNTR0yS5CAqcaasKHRSAzFKpeCUOfCPwYLAFxhCxsEbRtPBm0U1CfrDVa303MsjX18ngGpFGxIMA==} + + vitepress@1.6.3: + resolution: {integrity: sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + + vue@3.5.13: + resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) + '@algolia/client-search': 5.20.0 + algoliasearch: 5.20.0 + + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)': + dependencies: + '@algolia/client-search': 5.20.0 + algoliasearch: 5.20.0 + + '@algolia/client-abtesting@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/client-analytics@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/client-common@5.20.0': {} + + '@algolia/client-insights@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/client-personalization@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/client-query-suggestions@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/client-search@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/ingestion@1.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/monitoring@1.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/recommend@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + '@algolia/requester-browser-xhr@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + + '@algolia/requester-fetch@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + + '@algolia/requester-node-http@5.20.0': + dependencies: + '@algolia/client-common': 5.20.0 + + '@antfu/install-pkg@0.4.1': + dependencies: + package-manager-detector: 0.2.9 + tinyexec: 0.3.2 + + '@antfu/utils@0.7.10': {} + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/parser@7.26.7': + dependencies: + '@babel/types': 7.26.7 + + '@babel/types@7.26.7': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@docsearch/css@3.8.2': {} + + '@docsearch/js@3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3) + preact: 10.25.4 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights + + '@docsearch/react@3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.20.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.24.2': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.24.2': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.24.2': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.24.2': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.24.2': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.24.2': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.24.2': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.24.2': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.24.2': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.24.2': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.24.2': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.24.2': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.24.2': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.24.2': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.24.2': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.24.2': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.24.2': + optional: true + + '@esbuild/openbsd-arm64@0.24.2': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.24.2': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.24.2': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.24.2': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.24.2': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.24.2': + optional: true + + '@iconify-json/logos@1.2.4': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/simple-icons@1.2.23': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/vscode-icons@1.2.11': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@2.2.1': + dependencies: + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 + '@iconify/types': 2.0.0 + debug: 4.4.0 + globals: 15.14.0 + kolorist: 1.8.0 + local-pkg: 0.5.1 + mlly: 1.7.4 + transitivePeerDependencies: + - supports-color + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@moefy-canvas/core@0.6.0': {} + + '@moefy-canvas/theme-sparkler@0.6.0': + dependencies: + '@moefy-canvas/core': 0.6.0 + '@moefy-canvas/utils': 0.6.0 + color2k: 2.0.3 + ts-debounce: 4.0.0 + + '@moefy-canvas/utils@0.6.0': + dependencies: + '@moefy-canvas/core': 0.6.0 + + '@rollup/rollup-android-arm-eabi@4.34.0': + optional: true + + '@rollup/rollup-android-arm64@4.34.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.34.0': + optional: true + + '@rollup/rollup-darwin-x64@4.34.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.34.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.34.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.34.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.34.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.34.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.34.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.34.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.34.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.34.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.34.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.34.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.34.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.34.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.34.0': + optional: true + + '@shikijs/core@2.3.0': + dependencies: + '@shikijs/engine-javascript': 2.3.0 + '@shikijs/engine-oniguruma': 2.3.0 + '@shikijs/types': 2.3.0 + '@shikijs/vscode-textmate': 10.0.1 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.4 + + '@shikijs/engine-javascript@2.3.0': + dependencies: + '@shikijs/types': 2.3.0 + '@shikijs/vscode-textmate': 10.0.1 + oniguruma-to-es: 3.1.0 + + '@shikijs/engine-oniguruma@2.3.0': + dependencies: + '@shikijs/types': 2.3.0 + '@shikijs/vscode-textmate': 10.0.1 + + '@shikijs/langs@2.3.0': + dependencies: + '@shikijs/types': 2.3.0 + + '@shikijs/themes@2.3.0': + dependencies: + '@shikijs/types': 2.3.0 + + '@shikijs/transformers@2.3.0': + dependencies: + '@shikijs/core': 2.3.0 + '@shikijs/types': 2.3.0 + + '@shikijs/types@2.3.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.1 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.1': {} + + '@types/estree@1.0.6': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + + '@types/unist@3.0.3': {} + + '@types/web-bluetooth@0.0.20': {} + + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-vue@5.2.1(vite@5.4.14)(vue@3.5.13)': + dependencies: + vite: 5.4.14 + vue: 3.5.13 + + '@vue/compiler-core@3.5.13': + dependencies: + '@babel/parser': 7.26.7 + '@vue/shared': 3.5.13 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.13': + dependencies: + '@vue/compiler-core': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/compiler-sfc@3.5.13': + dependencies: + '@babel/parser': 7.26.7 + '@vue/compiler-core': 3.5.13 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.1 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.13': + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/devtools-api@7.7.1': + dependencies: + '@vue/devtools-kit': 7.7.1 + + '@vue/devtools-kit@7.7.1': + dependencies: + '@vue/devtools-shared': 7.7.1 + birpc: 0.2.19 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.2 + + '@vue/devtools-shared@7.7.1': + dependencies: + rfdc: 1.4.1 + + '@vue/reactivity@3.5.13': + dependencies: + '@vue/shared': 3.5.13 + + '@vue/runtime-core@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/runtime-dom@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/runtime-core': 3.5.13 + '@vue/shared': 3.5.13 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.13(vue@3.5.13)': + dependencies: + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + vue: 3.5.13 + + '@vue/shared@3.5.13': {} + + '@vueuse/core@12.5.0': + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 12.5.0 + '@vueuse/shared': 12.5.0 + vue: 3.5.13 + transitivePeerDependencies: + - typescript + + '@vueuse/integrations@12.5.0(focus-trap@7.6.4)': + dependencies: + '@vueuse/core': 12.5.0 + '@vueuse/shared': 12.5.0 + vue: 3.5.13 + optionalDependencies: + focus-trap: 7.6.4 + transitivePeerDependencies: + - typescript + + '@vueuse/metadata@12.5.0': {} + + '@vueuse/shared@12.5.0': + dependencies: + vue: 3.5.13 + transitivePeerDependencies: + - typescript + + acorn@8.14.0: {} + + algoliasearch@5.20.0: + dependencies: + '@algolia/client-abtesting': 5.20.0 + '@algolia/client-analytics': 5.20.0 + '@algolia/client-common': 5.20.0 + '@algolia/client-insights': 5.20.0 + '@algolia/client-personalization': 5.20.0 + '@algolia/client-query-suggestions': 5.20.0 + '@algolia/client-search': 5.20.0 + '@algolia/ingestion': 1.20.0 + '@algolia/monitoring': 1.20.0 + '@algolia/recommend': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 + + birpc@0.2.19: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + color2k@2.0.3: {} + + comma-separated-tokens@2.0.3: {} + + confbox@0.1.8: {} + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + csstype@3.1.3: {} + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + emoji-regex-xs@1.0.0: {} + + entities@4.5.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + + estree-walker@2.0.2: {} + + focus-trap@7.6.4: + dependencies: + tabbable: 6.2.0 + + fsevents@2.3.3: + optional: true + + globals@15.14.0: {} + + hast-util-to-html@9.0.4: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hookable@5.5.3: {} + + html-void-elements@3.0.0: {} + + is-what@4.1.16: {} + + kolorist@1.8.0: {} + + local-pkg@0.5.1: + dependencies: + mlly: 1.7.4 + pkg-types: 1.3.1 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + mark.js@8.11.1: {} + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.1: {} + + minisearch@7.1.1: {} + + mitt@3.0.1: {} + + mlly@1.7.4: + dependencies: + acorn: 8.14.0 + pathe: 2.0.2 + pkg-types: 1.3.1 + ufo: 1.5.4 + + ms@2.1.3: {} + + nanoid@3.3.8: {} + + oniguruma-to-es@3.1.0: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.0.1 + regex-recursion: 6.0.2 + + package-manager-detector@0.2.9: {} + + pathe@2.0.2: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.2 + + postcss@8.5.1: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.25.4: {} + + prettier@3.4.2: {} + + property-information@6.5.0: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rfdc@1.4.1: {} + + rollup@4.34.0: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.0 + '@rollup/rollup-android-arm64': 4.34.0 + '@rollup/rollup-darwin-arm64': 4.34.0 + '@rollup/rollup-darwin-x64': 4.34.0 + '@rollup/rollup-freebsd-arm64': 4.34.0 + '@rollup/rollup-freebsd-x64': 4.34.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.0 + '@rollup/rollup-linux-arm-musleabihf': 4.34.0 + '@rollup/rollup-linux-arm64-gnu': 4.34.0 + '@rollup/rollup-linux-arm64-musl': 4.34.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.0 + '@rollup/rollup-linux-riscv64-gnu': 4.34.0 + '@rollup/rollup-linux-s390x-gnu': 4.34.0 + '@rollup/rollup-linux-x64-gnu': 4.34.0 + '@rollup/rollup-linux-x64-musl': 4.34.0 + '@rollup/rollup-win32-arm64-msvc': 4.34.0 + '@rollup/rollup-win32-ia32-msvc': 4.34.0 + '@rollup/rollup-win32-x64-msvc': 4.34.0 + fsevents: 2.3.3 + + search-insights@2.17.3: {} + + shiki@2.3.0: + dependencies: + '@shikijs/core': 2.3.0 + '@shikijs/engine-javascript': 2.3.0 + '@shikijs/engine-oniguruma': 2.3.0 + '@shikijs/langs': 2.3.0 + '@shikijs/themes': 2.3.0 + '@shikijs/types': 2.3.0 + '@shikijs/vscode-textmate': 10.0.1 + '@types/hast': 3.0.4 + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + speakingurl@14.0.1: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + superjson@2.2.2: + dependencies: + copy-anything: 3.0.5 + + tabbable@6.2.0: {} + + tinyexec@0.3.2: {} + + trim-lines@3.0.1: {} + + ts-debounce@4.0.0: {} + + ufo@1.5.4: {} + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + vite@5.4.14: + dependencies: + esbuild: 0.21.5 + postcss: 8.5.1 + rollup: 4.34.0 + optionalDependencies: + fsevents: 2.3.3 + + vite@6.0.11: + dependencies: + esbuild: 0.24.2 + postcss: 8.5.1 + rollup: 4.34.0 + optionalDependencies: + fsevents: 2.3.3 + + vitepress-plugin-group-icons@1.3.5: + dependencies: + '@iconify-json/logos': 1.2.4 + '@iconify-json/vscode-icons': 1.2.11 + '@iconify/utils': 2.2.1 + transitivePeerDependencies: + - supports-color + + vitepress@1.6.3(@algolia/client-search@5.20.0)(postcss@8.5.1)(search-insights@2.17.3): + dependencies: + '@docsearch/css': 3.8.2 + '@docsearch/js': 3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3) + '@iconify-json/simple-icons': 1.2.23 + '@shikijs/core': 2.3.0 + '@shikijs/transformers': 2.3.0 + '@shikijs/types': 2.3.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.2.1(vite@5.4.14)(vue@3.5.13) + '@vue/devtools-api': 7.7.1 + '@vue/shared': 3.5.13 + '@vueuse/core': 12.5.0 + '@vueuse/integrations': 12.5.0(focus-trap@7.6.4) + focus-trap: 7.6.4 + mark.js: 8.11.1 + minisearch: 7.1.1 + shiki: 2.3.0 + vite: 5.4.14 + vue: 3.5.13 + optionalDependencies: + postcss: 8.5.1 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + + vue@3.5.13: + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-sfc': 3.5.13 + '@vue/runtime-dom': 3.5.13 + '@vue/server-renderer': 3.5.13(vue@3.5.13) + '@vue/shared': 3.5.13 + + zwitch@2.0.4: {} diff --git a/docs/public/logo-mini.svg b/docs/public/logo-mini.svg new file mode 100644 index 000000000..3d2084922 --- /dev/null +++ b/docs/public/logo-mini.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo/logo.png b/docs/public/logo.png similarity index 100% rename from logo/logo.png rename to docs/public/logo.png diff --git a/docs/sponsor.md b/docs/sponsor.md new file mode 100644 index 000000000..c9529d16c --- /dev/null +++ b/docs/sponsor.md @@ -0,0 +1,17 @@ +# 赞助 + +首先说明,由于我只是将 B 站的视频搬运到你的电脑上,是一件很简单的事情,请把最大的感激给予平台以及创作者。 + +如果你想支持我的话,在 [GitHub 项目主页](https://github.com/yutto-dev/yutto)给予我一个 「Star」 就是对我的最大鼓励。 + +此外,如果你想给予 Nyakku 一定资金支持以激励 Nyakku 的开发的话,你可以通过以下方式进行 + +## 一次性赞助 + +你可以通过[支付宝](https://img.nyakku.moe/sponsor/alipay.png)或者[微信](https://img.nyakku.moe/sponsor/wechat.png)来为 Nyakku 提供一笔开发资金。 + +## 周期性赞助 + +你可以通过 [Patreon](https://www.patreon.com/SigureMo) 或者[爱发电](https://afdian.net/@siguremo)来为 Nyakku 提供每月的资金支持,以激励 Nyakku 创作更多有趣、实用的开源项目。 + +你的任何金额的赞助我都会无比珍惜,我会在[项目致谢](./guide/thanks)中标注你的 GitHub ID(需要在赞助时备注你的 GitHub ID,如果有资助后忘记留 ID 的可以联系我~)。 diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 000000000..622223491 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "composite": true, + "declaration": true, + "declarationMap": true, + "lib": ["DOM", "ES2020"], + "module": "ESNext", + "moduleResolution": "node", + "jsx": "preserve", + "newLine": "lf", + "noEmitOnError": true, + "noImplicitAny": false, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "strictNullChecks": true, + "target": "ES2018" + }, + "include": ["./.vitepress/**/*", "./.vitepress/env.d.ts"] +} diff --git a/justfile b/justfile index 74f33bf2d..8605c6c7f 100644 --- a/justfile +++ b/justfile @@ -70,6 +70,7 @@ clean-builds: generate-schema: uv run scripts/generate-schema.py +# CI specific ci-install: uv sync --all-extras --dev @@ -85,6 +86,7 @@ ci-test: ci-e2e-test: uv run pytest -m "e2e and not (ci_skip or ignore)" +# docker specific docker-run *ARGS: docker run --rm -it -v `pwd`:/app {{DOCKER_NAME}} {{ARGS}} @@ -94,6 +96,13 @@ docker-build: docker-publish: docker buildx build --no-cache --platform=linux/amd64,linux/arm64 -t "{{DOCKER_NAME}}:{{VERSION}}" -t "{{DOCKER_NAME}}:latest" . --push +# docs specific +docs-dev: + cd docs; pnpm dev + +docs-build: + cd docs; pnpm build + # biliass specific build-biliass: cd packages/biliass; maturin build diff --git a/src/yutto/cli/cli.py b/src/yutto/cli/cli.py index 19b8b24b1..5244536b8 100644 --- a/src/yutto/cli/cli.py +++ b/src/yutto/cli/cli.py @@ -163,17 +163,17 @@ def cli() -> argparse.ArgumentParser: default=settings.basic.banned_mirrors_pattern, help="禁用下载链接的镜像源,使用正则匹配", ) - group_basic.add_argument("--no-color", default=settings.basic.no_color, action="store_true", help="不使用颜色") - group_basic.add_argument( - "--no-progress", default=settings.basic.no_progress, action="store_true", help="不显示进度条" - ) - group_basic.add_argument("--debug", default=settings.basic.debug, action="store_true", help="启用 debug 模式") group_basic.add_argument( "--vip-strict", default=settings.basic.vip_strict, action="store_true", help="启用严格检查大会员生效" ) group_basic.add_argument( "--login-strict", default=settings.basic.login_strict, action="store_true", help="启用严格检查登录状态" ) + group_basic.add_argument("--no-color", default=settings.basic.no_color, action="store_true", help="不使用颜色") + group_basic.add_argument( + "--no-progress", default=settings.basic.no_progress, action="store_true", help="不显示进度条" + ) + group_basic.add_argument("--debug", default=settings.basic.debug, action="store_true", help="启用 debug 模式") # 资源选择 group_resource = parser.add_argument_group("resource", "资源选择参数") @@ -252,9 +252,9 @@ def cli() -> argparse.ArgumentParser: group_resource.set_defaults( require_video=settings.resource.require_video, require_audio=settings.resource.require_audio, + require_danmaku=settings.resource.require_danmaku, require_subtitle=settings.resource.require_subtitle, require_metadata=settings.resource.require_metadata, - require_danmaku=settings.resource.require_danmaku, require_cover=settings.resource.require_cover, require_chapter_info=settings.resource.require_chapter_info, ) diff --git a/src/yutto/cli/settings.py b/src/yutto/cli/settings.py index d1538485e..27c45f404 100644 --- a/src/yutto/cli/settings.py +++ b/src/yutto/cli/settings.py @@ -53,19 +53,19 @@ class YuttoBasicSettings(BaseModel): metadata_format_premiered: Annotated[str, Field(TIME_DATE_FMT)] download_interval: Annotated[int, Field(0)] banned_mirrors_pattern: Annotated[Optional[str], Field(None)] # noqa: UP007 + vip_strict: Annotated[bool, Field(False)] + login_strict: Annotated[bool, Field(False)] no_color: Annotated[bool, Field(False)] no_progress: Annotated[bool, Field(False)] debug: Annotated[bool, Field(False)] - vip_strict: Annotated[bool, Field(False)] - login_strict: Annotated[bool, Field(False)] class YuttoResourceSettings(BaseModel): require_video: Annotated[bool, Field(True)] require_audio: Annotated[bool, Field(True)] + require_danmaku: Annotated[bool, Field(True)] require_subtitle: Annotated[bool, Field(True)] require_metadata: Annotated[bool, Field(False)] - require_danmaku: Annotated[bool, Field(True)] require_cover: Annotated[bool, Field(True)] require_chapter_info: Annotated[bool, Field(True)] save_cover: Annotated[bool, Field(False)] diff --git a/src/yutto/extractor/cheese.py b/src/yutto/extractor/cheese.py index be9a2a695..7759791a1 100644 --- a/src/yutto/extractor/cheese.py +++ b/src/yutto/extractor/cheese.py @@ -38,9 +38,10 @@ class CheeseExtractor(SingleExtractor): def resolve_shortcut(self, id: str) -> tuple[bool, str]: matched = False url = id - if match_obj := self.REGEX_EP_ID.match(id): - url = f"https://www.bilibili.com/cheese/play/ep{match_obj.group('episode_id')}" - matched = True + # TODO 和番剧的快捷方式冲突,课程中暂时放弃快捷方式特性 + # if match_obj := self.REGEX_EP_ID.match(id): + # url = f"https://www.bilibili.com/cheese/play/ep{match_obj.group('episode_id')}" + # matched = True return matched, url def match(self, url: str) -> bool: