当前浏览的是 v7 预览版文档(v7 尚未正式发布,API 可能随版本调整)。查看对应的 v6 正式版文档
@ikenxuan/amagi

SDK 使用指南

详细了解如何使用 amagi SDK

SDK 使用指南

调用 amagi 有三种写法,区别只在于如何传入 Cookie。日常使用第一种即可,另两种用于特定场景。

创建实例(推荐)

将 Cookie 传入 amagi(),得到客户端实例,之后即可直接调用各平台的方法,无需再关心 Cookie:

import  from '@ikenxuan/amagi'

const  = ({
  : {
    : 'your_bilibili_cookie',
    : 'your_douyin_cookie',
    : 'your_kuaishou_cookie',
    : 'your_xiaohongshu_cookie'
  }
})

// 通过 client.<平台>.fetcher 调用方法
const  = await ...({ : 'BV1xx411c7mD' })
const  = await ...({ : '123456789' })

如果只需调用少数几个接口,不想为此创建客户端,可以使用静态 fetcher。方法名与用法相同,只是 Cookie 改为第二个参数,且每次都要传入:

import  from '@ikenxuan/amagi'

const  = await ..({ : 'BV1xx411c7mD' }, 'SESSDATA=xxx; bili_jct=yyy')

如果既想要静态 fetcher 无需创建实例的简便,又不想每次重复传入 Cookie,可以用 createBound*Fetcher 预先绑定 Cookie,之后调用只需传入业务参数:

import  from '@ikenxuan/amagi'

// 预先将 Cookie 绑定到 fetcher
const  = .('your_cookie')
const  = .('your_cookie')

// 后续调用无需再传入 Cookie
const  = await .({ : 'BV1xx411c7mD' })

返回值:统一封装在「信封」中

无论调用哪个方法,返回的都不是数据本身,而是一层统一的外层结构,称为「信封」(AmagiResult<T>)。信封中要么是数据,要么是错误。先判断 success 字段:为 true 时读取 data,为 false 时读取 error。顶层没有 code,请求失败时也不会抛出异常,失败同样以返回值的形式给出。

v7 的信封与 v6 的 Result<T> 不同:顶层 code 已删除,error 也不再是 any。 v6 用同一个 code 混装 HTTP 状态码、平台业务码与内部码,v7 把它们各归各位 —— HTTP 状态在 error.http.status、平台原始码在 error.platform.code、amagi 自己的错误码在 error.code。要整体切回 v6 语义(含顶层 code)用 @ikenxuan/amagi/compat,一行改导入即可。

下面两张表由 packages/core 的 TypeScript 源码在构建期生成,字段名、类型、说明 全部取自源码注释:

成功信封

Prop

Type

失败信封

Prop

Type

error 的完整字段(kind / code / retryable / platform / http / issues / raw)与 meta 的字段见响应类型那一页, 连同四种读法(直接读 / if 收窄 / filter(isSuccess) / unwrap)。

使用示例

const  = await ...({ : 'BV1xx411c7mD' })

if (.) {
  .(.)
} else {
  .(., .)
}

自定义请求头、超时与代理

创建实例时写在 request 中,该客户端此后发出的每一条请求都会带上这些配置:

import  from '@ikenxuan/amagi'

const  = ({
  : {
    : 'your_cookie',
    : 'your_cookie',
    : 'your_cookie',
    : 'your_cookie'
  },
  : {
    : {
      'User-Agent': 'Mozilla/5.0 ...'
    },
    : 10000,
    : {
      : '127.0.0.1',
      : 7890
    }
  }
})

平台模块结构

每个平台模块包含以下内容(request 与 apiUrls 是 v7 新增的两个键:前者用于请求任意接口,见自定义请求;后者是 v7 那份 URL 构造器。v6 那份(douyinApiUrls 等带平台名的四个)不在平台模块上 —— 它从包顶层与 @ikenxuan/amagi/compat 到达)。四个平台都带 login(扫码登录会话),B站另带 refreshCredential(Cookie 续期),抖音另带 credentials(Cookie / 登录态 / 游客凭据工具,见工具集):

// fetcher - 数据获取器
...({ : 'BV1xx' })
...({ : '123', : 1 })
...({ : 123456 })

// sign - 签名算法
...(170001)
...('BV1xx411c7mD')

// danmaku - 弹幕解析
// client.bilibili.danmaku.parseDmSegMobileReply(buffer)

// login - 扫码登录;refreshCredential - Cookie 续期
// client.bilibili.login.qrcode()
// client.bilibili.refreshCredential({ cookie, refreshToken })

扫码登录

四个平台都提供 client.<平台>.login。它是一次有状态会话,不是端点:qrcode() 新建、resume(blob) 从 serialize() 的产物恢复,轮询 / 退避 / 超时 / 二次验证编排都在引擎里。

import  from '@ikenxuan/amagi'

const  = ({})

const  = await ...().({
  : () => .(.), // 拿去生成二维码图片
  : () => .('已扫码')
})

if (.) {
  .(..) // 直接传给 fetcher,或下次创建 client 时使用
}

watch() 返回的是 { ok, credential } | { ok, error },不是 AmagiResult。同一会话还有两种消费方式:for await (const state of session) 逐状态消费,或 start() + next() 手动单步(challenge 阶段用 answer() 应答)。想把会话存下来跨进程继续,用 session.serialize(),再用 client.<平台>.login.resume(blob) 恢复。

LoginState.phase 的完整状态机、各平台协议差异与已知限制见翻页与会话。会话每一步还会发到实例总线(session:state / session:success / session:error),见事件系统。

B站有一套官方续期协议:登录时下发的 refresh_token(Web 端存在 localStorage.ac_time_value)可以在不重新扫码的情况下换一份新 Cookie。四平台里只有 B站有这条协议:

import  from '@ikenxuan/amagi'

const  = ({})

declare const : string
declare const : string

const  = await ..({ ,  })

if (.) {
  // 两个值都要持久化替换旧值:旧 refresh_token 已在协议第 4 步作废
  .(..)
  .(..)
} else {
  .(.., ..)
}

cookie 至少要含 SESSDATA 与 bili_jct。这是平台正规续期通道,不涉及风控对抗;失败时不会自动重试,按 refreshed.error 处理即可。

最后更新于 2026-10-23

On this page