最后更新时间:2026-05-11 已从PoxenStudio/Talebook更名为MyBooks
- 基础 URL:/api(除 OPDS/Podcast/静态资源等非 /api 路由)
- 认证方式:登录后 Cookie(user_id 等)
- 管理员接口:需登录且管理员权限
- 响应约定:大多数 JSON 接口都有 err 字段,err = "ok" 表示成功
成功响应:
{
"err": "ok",
"msg": "success",
"data": {}
}错误响应:
{
"err": "error.code",
"msg": "错误描述信息"
}系统设备分为两类:
- 用户设备:reader_id > 0,绑定具体用户(如用户 Kindle 推送目标)
- 全局设备:reader_id = 0 或空,系统级共享设备
客户端在展示设备列表时,应根据 reader_id 区分设备归属。
完整的图书对象包含以下字段:
{
"id": 123,
"title": "三体",
"rating": 5,
"timestamp": "2026-05-11",
"pubdate": "2024-01-01",
"author": "刘慈欣",
"authors": ["刘慈欣"],
"author_sort": "Liu Cixin",
"tag": "科幻 / 长篇",
"tags": ["科幻", "长篇"],
"publisher": "重庆出版社",
"comments": "暂无简介",
"series": "三体系列",
"series_index": 1,
"languages": ["zho"],
"isbn": "9787536692930",
"img": "https://your.cdn/get/cover/123.jpg",
"thumb": "https://your.cdn/get/thumb_240_320/123.jpg",
"collector": "admin",
"count_visit": 100,
"count_download": 50,
"sole": false,
"has_audio": 0,
"book_type": 0,
"book_count": 1,
"state": {
"favorite": 1,
"favorite_date": "2026-05-10T12:00:00",
"wants": 0,
"wants_date": null,
"read_state": 2,
"read_date": "2026-05-11T10:00:00",
"online_read": 1,
"download": 1
},
"category": "科幻",
"ext_link": "",
"files": [
{"format": "epub", "size": 123456}
],
"dynamic_cover": 0
}- 路径:
/api/access - 方法:POST
- 参数: JSON Body.
invite_code(string, 必填): 访问码
- 认证:无需认证
- 响应示例:
{
"err": "ok",
"version": "v3.20.0"
}- 路径:
/api/user/sign_up - 方法:POST
- 认证:无需认证(需要开启注册功能)
- 参数:
username(string, 必填): 用户名,3-20位字符,仅字母数字下划线nickname(string, 必填): 昵称email(string, 必填): 邮箱地址password(string, 必填): 密码,6-20位
- 响应示例:
{
"err": "ok",
"msg": "注册成功,请查收激活邮件"
}- 路径:
/api/user/sign_in - 方法:POST
- 认证:无需认证
- 参数:
username(string, 必填): 用户名password(string, 必填): 密码
- 响应示例:
{
"err": "ok",
"msg": "ok"
}- 路径:
/api/user/sign_out - 方法:GET
- 认证:需要登录
- 参数:无
- 响应示例:
{
"err": "ok",
"msg": "你已成功退出登录。"
}- 路径:
/api/user/info - 方法:GET
- 认证:无需登录
- 参数:无
- 响应示例:
{
"err": "ok",
"sys": {
"title": "MyBooks",
"books": 10000,
"version": "v3.41.0",
"upgrable": "",
"allow": {
"book_review": false,
"physical_books": false,
"upload": false,
}
},
"user": {
"id": 1,
"username": "admin",
"name": "管理员",
"email": "admin@example.com",
"avatar": "reader.svg",
"is_admin": true,
"show_home_recommendations": false,
"show_other_annotations": false,
"appearance": {
"v": 1,
"darkMode": true,
"brandColor": null,
"accent": "#1976D2",
"radius": "4px",
"background": "default",
"sidebarIconMode": "multi",
"sidebarIconColor": null
}
}
}未登录时返回基础系统信息,用户信息中is_login为false,登录时会返回完整用户信息。
sys中为基础系统信息,title为网站标题, books为在库书籍数量,version为当前系统版本。其中upgrable代表是否有升级版本,如果为有值且与version不同代表有可升级版本。sys/allow下的book_review表示是否允许用户评论,physical_books代表是否支持实体书,upload代表是否允许上传。
user.appearance为外观设置(顶栏品牌色、侧栏图标配色、深浅色、圆角、背景图案),用户从未保存过时为空对象{},由前端沿用「本地缓存 → 站点默认sys.theme → 内置默认」的回退顺序,保存入口见 1.9。
- 路径:
/api/user/update - 方法:POST
- 认证:需要登录
- 参数(JSON):
nickname(string, 可选): 新昵称password0(string, 可选): 当前密码(修改密码时必填)password1(string, 可选): 新密码password2(string, 可选): 确认新密码kindle_email(string, 可选): Kindle推送邮箱podcast_token(string, 可选): Podcast订阅Tokenallow_sending_mail(boolean, 可选): 是否允许发送邮件show_other_annotations(boolean, 可选): 是否查看其他用户的批注数据
- 请求示例:
{
"password0": "",
"nickname": "admin",
"kindle_email": "",
"allow_sending_mail": false,
"allow_statistic": true,
"show_home_recommendations": true,
"show_other_annotations": false
}- 响应示例:
{
"err": "ok"
}- 路径:
/api/user/reset - 方法:POST
- 认证:无需认证
- 参数:
username(string, 必填): 用户名email(string, 必填): 注册邮箱
- 响应示例:
{
"err": "ok",
"msg": "密码重置邮件已发送"
}- 路径:
/api/user/avatar - 方法:POST
- 认证:需要登录
- 参数:
avatar(file, 必填): 头像图片文件
- 响应示例:
{
"err": "ok",
"avatar": "/get/avatar/user_123.jpg"
}- 路径:
/api/user/appearance - 方法:POST(保存)、DELETE(清空)
- 认证:需要登录
- 说明:补丁语义 —— 只提交改动过的键,未提交的键保持原值;未知键与非法值一律丢弃(不会用空值覆盖已有的合法值)。
提交完整对象同样合法(幂等),前端就是把当前完整设置整份提交的。
设置落在
Reader.extra["appearance"],由/api/user/info的user.appearance(见 1.5)下发前端。 客户端声明的v比服务端新时返回appearance.version.unsupported:老服务端不认识新客户端的键、会静默丢弃,宁可让客户端提示用户刷新。 - 参数(JSON):
darkMode(boolean, 可选): 深浅色主题accent(string, 可选): 主色,#RRGGBB(不接受null:主色有内置默认值,前端也只接受合法 hex)brandColor(string|null, 可选): 顶栏品牌色,#RRGGBB;传null表示回到内置默认深蓝radius(string, 可选): 圆角,0px/4px/8px/16pxbackground(string, 可选): 背景图案,见下方枚举sidebarIconMode(string, 可选): 侧栏图标配色,multi(保持多彩,默认)/theme(跟随主色)/custom(统一自定义)sidebarIconColor(string|null, 可选):sidebarIconMode=custom时使用的图标色
- 请求示例:
{
"darkMode": false,
"brandColor": "#4c1d95",
"sidebarIconMode": "theme"
}- 响应示例:
{
"err": "ok",
"appearance": {
"v": 1,
"darkMode": false,
"brandColor": "#4c1d95",
"accent": "#1976D2",
"radius": "4px",
"background": "default",
"sidebarIconMode": "theme",
"sidebarIconColor": null
},
"dropped": []
}- 清空(DELETE):删掉该账号已保存的外观设置,响应
{"err": "ok", "appearance": {}}。 用于外观面板的「重置为默认」—— 语义是回到「从未保存过」,前端随即重新采用 「本地缓存 → 站点默认sys.theme→ 内置默认」的回退顺序。 刻意不做成「提交一份内置默认值」:那会把管理员设置的站点默认永久顶掉(站点默认只在本机没有 外观缓存时才生效)。 - 错误码:
params.invalid(请求体不是合法 JSON 对象)、appearance.version.unsupported、appearance.too_large、db.error。 background枚举:default、cross、left-diagonal、right-diagonal、aurora、horizon、glow、mesh、repeat-image-1~repeat-image-4。
- 路径:
/api/user/active/send - 方法:GET/POST
- 认证:需要登录
- 参数:无
- 响应示例:
{
"err": "ok"
}- 路径:
/api/active/<username>/<code> - 方法:GET/POST
- 认证:无需认证
- 参数:
username(path, 必填): 用户名code(path, 必填): 激活码
- 响应:302重定向到激活成功页面
- 路径:
/api/user/new - 方法:POST
- 认证:需要管理员权限
- 参数:
username(string, 必填): 用户名nickname(string, 必填): 昵称email(string, 必填): 邮箱password(string, 必填): 密码
- 响应示例:
{
"err": "ok",
"msg": "用户添加成功"
}- 路径:
/api/done/ - 方法:GET
- 认证:无需认证(由OAuth流程调用)
- 参数:无
- 响应:302重定向到首页或指定页面
- 路径:
/api/user/messages - 方法:GET
- 认证:需要登录
- 参数:无
- 响应示例:
{
"err": "ok",
"messages": [
{
"id": 1,
"title": "系统通知",
"status": "unread",
"create_time": "2026-05-11 10:00:00",
"data": {}
}
]
}- 路径:
/api/user/messages - 方法:POST
- 认证:需要登录
- 参数(JSON):
id(int, 必填): 消息ID
- 响应示例:
{
"err": "ok"
}- 路径:
/api/user/messages/clear - 方法:POST
- 认证:需要登录
- 参数:无
- 响应示例:
{
"err": "ok"
}- 路径:
/api/user/pin - 方法:POST
- 认证:需要登录
- 参数(JSON):
item_type(int, 必填): 0=作者, 1=标签item(string, 必填): 作者名或标签名
- 响应示例:
{
"err": "ok"
}- 路径:
/api/user/unpin - 方法:POST
- 认证:需要登录
- 参数(JSON):
item_type(int, 必填): 0=作者, 1=标签item(string, 必填): 作者名或标签名
- 响应示例:
{
"err": "ok"
}- 路径:
/api/user/devices - 方法:GET/POST
- 认证:需要登录
- 说明:该接口是“全量替换”模型——没有单个设备的
id,GET 返回当前用户的全部设备列表,POST 时必须提交完整的设备列表(包含要保留的所有设备),服务端会先清空该用户的所有设备记录再写入。要删除某个设备,只需在 POST 时省略它;要清空所有设备,POST 一个空数组。
支持的设备类型(type 字段)共 8 种,按数据模型可分为三组:
| 分组 | type 取值 |
额外字段 |
|---|---|---|
| Kindle 邮箱推送 | kindle |
mailbox |
| WiFi 局域网传输(数据模型相同) | duokan(多看)、ireader(爱阅读)、hanwang(汉王)、boox(文石)、dangdang(当当)、purelibro |
ip、port、schema |
| FTP 传输 | ftp |
ip、port、ftp_username、ftp_password、ftp_path |
注:
type字段当前未做白名单校验(任意字符串都会被存储,省略时默认为duokan);类型校验仅在实际推送书籍的/api/book/<id>/send_to_device接口中执行。
- 参数:无
- 响应:
devices为数组,每个元素的字段取决于设备类型:- 所有类型通用:
name、type、ip、port、schema、mailbox - 仅
ftp类型额外返回:ftp_username、ftp_password、ftp_path(此时mailbox字段固定为空字符串)
- 所有类型通用:
Kindle 设备响应示例:
{
"err": "ok",
"devices": [
{
"name": "我的Kindle",
"type": "kindle",
"ip": "",
"port": 12121,
"schema": "http",
"mailbox": "user@kindle.com"
}
]
}WiFi 传输设备响应示例(duokan/ireader/hanwang/boox/dangdang/purelibro 共用此结构):
{
"err": "ok",
"devices": [
{
"name": "客厅的多看",
"type": "duokan",
"ip": "192.168.1.100",
"port": 8080,
"schema": "http",
"mailbox": ""
}
]
}FTP 设备响应示例:
{
"err": "ok",
"devices": [
{
"name": "NAS",
"type": "ftp",
"ip": "192.168.1.50",
"port": 21,
"schema": "http",
"mailbox": "",
"ftp_username": "admin",
"ftp_password": "123456",
"ftp_path": "/books"
}
]
}- 参数(JSON):
devices(array, 必填): 完整的设备列表,每个元素:type(string, 选填):kindle/duokan/ireader/hanwang/boox/dangdang/purelibro/ftp,默认duokanname(string, 选填): 设备名称,默认空字符串mailbox(string, 选填): 仅kindle类型使用,存放 Kindle 推送邮箱ip(string, 选填): 仅 WiFi 传输类、ftp类型使用,默认空字符串port(int, 选填): 仅 WiFi 传输类、ftp类型使用,默认12121schema(string, 选填): 仅 WiFi 传输类使用,http/https,默认httpftp_username(string, 选填): 仅ftp类型使用ftp_password(string, 选填): 仅ftp类型使用ftp_path(string, 选填): 仅ftp类型使用,序列化后的 FTP 配置总长度不可超过 2048 字符
请求示例(同时保留一个 Kindle、一个多看、一个 FTP 设备):
{
"devices": [
{ "type": "kindle", "name": "我的Kindle", "mailbox": "user@kindle.com" },
{ "type": "duokan", "name": "客厅的多看", "ip": "192.168.1.100", "port": 8080, "schema": "http" },
{ "type": "ftp", "name": "NAS", "ip": "192.168.1.50", "port": 21, "ftp_username": "admin", "ftp_password": "123456", "ftp_path": "/books" }
]
}- 响应示例(成功时不返回
devices,需重新 GET 获取最新列表):
{
"err": "ok",
"msg": "设备保存成功"
}- 错误响应:
- 请求体非合法 JSON 或
devices不是数组:{"err": "params.invalid", "msg": "参数无效"} - FTP 配置序列化后超过 2048 字符:
{"err": "params.invalid", "msg": "FTP配置信息过长"} - 数据库写入失败:
{"err": "db.error", "msg": "数据库操作异常,请重试"} - 未登录:
{"err": "user.need_login", "msg": "请先登录"}
- 请求体非合法 JSON 或
- 路径:
/api/user/expected - 方法:GET/POST
- 认证:需要登录
- 参数:
- GET: 无参数,获取期望列表
- POST: 添加/删除期望(JSON参数)
- 响应示例:
{
"err": "ok",
"items": [
{
"id": 1,
"title": "三体II",
"author": "刘慈欣"
}
]
}- 路径:
/api/user/history/clear - 方法:POST
- 认证:需要登录
- 参数:无
- 响应示例:
{
"err": "ok"
}- 路径:
/api/user/memo - 方法:GET/POST
- 认证:需要登录
- 参数:
- GET: 无参数,获取便签列表
- POST: 添加/更新/删除便签(JSON参数)
- 响应示例:
{
"err": "ok",
"memos": [
{
"id": 1,
"content": "便签内容",
"create_time": "2026-05-11 10:00:00"
}
]
}- 路径:
/api/user/reading_stats - 方法:GET
- 认证:需要登录
- 参数:
uid(query, 可选,仅管理员可用): 指定要查看的用户ID。普通用户传此参数会返回permission denied;不传则默认查询当前登录用户自己的统计数据
- 说明:
- 该接口是首页“阅读统计”Banner 的数据源(详见
document/Reading_Dashboard_Design.md)。功能受配置项ENABLE_HOMEPAGE_READING_STATS控制(默认开启),关闭时接口仍返回 200,但仅含enabled: false,不含其余字段 weekly中每周的起止按自然周(周一为一周起点)划分,历史(昨天及以前)数据按用户读取每日缓存文件聚合得出、每天最多重建一次;当天(今天)的数据始终来自实时查询,不做缓存totals中的三个字段是账号维度的累计计数器(存储在Reader表上),不受weekly时间窗口限制book_status为实时查询结果,不做缓存
- 该接口是首页“阅读统计”Banner 的数据源(详见
- 响应示例(功能开启,正常返回):
{
"err": "ok",
"enabled": true,
"totals": {
"total_reading_seconds": 123456,
"download_count": 12,
"push_count": 3
},
"weekly": [
{ "week_start": "2026-06-15", "reading_seconds": 5400, "download_count": 1, "push_count": 0 },
{ "week_start": "2026-06-22", "reading_seconds": 3200, "download_count": 0, "push_count": 2 },
{ "week_start": "2026-06-29", "reading_seconds": 0, "download_count": 0, "push_count": 0 },
{ "week_start": "2026-07-06", "reading_seconds": 7100, "download_count": 2, "push_count": 0 },
{ "week_start": "2026-07-13", "reading_seconds": 4500, "download_count": 0, "push_count": 1 },
{ "week_start": "2026-07-20", "reading_seconds": 6300, "download_count": 1, "push_count": 0 },
{ "week_start": "2026-07-27", "reading_seconds": 2900, "download_count": 0, "push_count": 0 },
{ "week_start": "2026-08-03", "reading_seconds": 1200, "download_count": 0, "push_count": 0 }
],
"book_status": {
"reading": 3,
"to_read": 12,
"finished": 27
}
}字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
err |
string | 固定为 "ok"(异常/无权限时见下方错误响应) |
enabled |
boolean | 阅读统计功能总开关(ENABLE_HOMEPAGE_READING_STATS)当前是否启用。为 false 时,响应中不包含 totals/weekly/book_status 字段 |
totals |
object | 账号累计统计(读取自 Reader 表对应字段,与统计周期无关) |
totals.total_reading_seconds |
integer | 累计在线阅读时长,单位:秒 |
totals.download_count |
integer | 累计下载次数 |
totals.push_count |
integer | 累计推送次数(推送到设备/邮箱)。注意:该字段是后续版本新增的,历史数据未回填,仅统计功能上线后产生的推送 |
weekly |
array | 最近 8 个自然周的统计数据,按周一为起点、时间正序排列(最早的一周在前,最后一项为本周至今的数据) |
weekly[].week_start |
string (YYYY-MM-DD) |
该周的起始日期(周一) |
weekly[].reading_seconds |
integer | 该周阅读时长,单位:秒 |
weekly[].download_count |
integer | 该周下载次数 |
weekly[].push_count |
integer | 该周推送次数 |
book_status |
object | 当前图书状态分布(基于阅读状态表实时统计,不含缓存) |
book_status.reading |
integer | 「在读」图书数量 |
book_status.to_read |
integer | 「想读」图书数量(已标记 wants 但尚未开始/完成阅读) |
book_status.finished |
integer | 「已读完」图书数量 |
- 响应示例(功能关闭):
{
"err": "ok",
"enabled": false
}- 错误响应示例:
{ "err": "failed", "msg": "permission denied" }{ "err": "failed", "msg": "user not found" }{ "err": "user.need_login", "msg": "请先登录" }- 路径:
/api/friends/favicon/<filename> - 方法:GET
- 认证:无需认证
- 参数:
filename(path, 必填): 图标文件名
- 响应:返回图标文件(图片)
- 路径:
/api/index - 方法:GET
- 认证:无需认证
- 参数:
random(int, 可选): 随机书籍数量,默认12recent(int, 可选): 最新书籍数量,默认12
- 响应示例:
{
"err": "ok",
"random_books_count": 12,
"new_books_count": 12,
"random_books": [...],
"new_books": [...]
}- 路径:
/api/search - 方法:GET
- 认证:无需认证
- 参数:
query(string, 必填): 搜索关键词page(int, 可选): 页码,默认1num(int, 可选): 每页数量,默认20
- 响应示例:
{
"err": "ok",
"total": 100,
"books": [...]
}- 路径:
/api/recent - 方法:GET
- 认证:无需认证
- 参数:
page(int, 可选): 页码,默认1num(int, 可选): 每页数量,默认20
- 响应示例:
{
"err": "ok",
"total": 100,
"books": [...]
}- 路径:
/api/all - 方法:GET
- 认证:无需认证
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 1000,
"books": [...]
}- 路径:
/api/hot - 方法:GET
- 认证:无需认证
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 50,
"books": [...]
}- 路径:
/api/printbooks - 方法:GET
- 认证:无需认证
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 200,
"books": [...]
}- 路径:
/api/soledbooks - 方法:GET
- 认证:需要登录
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 20,
"books": [...]
}- 路径:
/api/book/nav - 方法:GET
- 认证:无需认证
- 参数:无
- 响应示例:
{
"err": "ok",
"nav": {
"categories": [...],
"tags": [...],
"authors": [...]
}
}- 路径:
/api/book/<id> - 方法:GET
- 认证:无需认证(但登录后可获取阅读状态)
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"kindle_sender": "sender@example.com",
"book": {...},
"audios": {...}
}- 路径:
/api/book/<id>/delete - 方法:POST
- 认证:需要管理员权限
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "删除成功"
}- 路径:
/api/book/<id>/delete_format - 方法:POST
- 认证:需要管理员权限
- 参数:
id(path, 必填): 图书IDformat(string, 必填): 格式名,如epub、pdf
- 响应示例:
{
"err": "ok",
"msg": "格式删除成功"
}- 路径:
/api/book/<id>/edit - 方法:POST
- 认证:需要管理员或书籍所有者权限
- 参数:
id(path, 必填): 图书ID- JSON数据:包含title、authors、tags、publisher等字段
- 响应示例:
{
"err": "ok",
"msg": "更新成功"
}- 路径:
/api/book/<id>.<ext> - 方法:GET
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDext(path, 必填): 文件格式,如epub、pdf
- 响应:返回文件下载
- 路径:
/api/book/<id>/refer - 方法:GET
- 认证:无需认证
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"refer": {
"douban": "https://book.douban.com/...",
"amazon": "https://www.amazon.com/..."
}
}- 路径:
/api/book/<id>/send_to_device - 方法:POST
- 认证:需要登录(若管理员开启了"允许游客推送",则游客也可调用)
- 参数(JSON body):
id(path, 必填): 图书IDdevice_type(string, 必填): 设备类型,取值之一:duokan、ireader、hanwang、boox、dangdang、kindle、purelibro、ftpdevice_url(string): 设备地址(IP/URL)。除kindle外均必填;不允许填写指向本机的地址(127.开头)mailbox(string): 目标邮箱,仅device_type=kindle时必填ftp_path(string): FTP路径,仅device_type=ftp时必填ftp_username(string, 可选): FTP用户名,仅device_type=ftp时使用ftp_password(string, 可选): FTP密码,仅device_type=ftp时使用
- 响应示例:
{
"err": "ok",
"msg": "推送成功"
}- 路径:
/api/book/<id>/mailto - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDemail(string, 必填): 目标邮箱format(string, 可选): 格式
- 响应示例:
{
"err": "ok",
"msg": "发送成功"
}- 路径:
/read/<id> - 方法:GET
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应:返回在线阅读页面(HTML)
- 路径:
/api/read/txt - 方法:GET
- 认证:需要登录
- 参数:
book_id(int, 必填): 图书IDchapter(int, 可选): 章节号
- 响应示例:
{
"err": "ok",
"content": "章节内容...",
"chapter": 1,
"total_chapters": 10
}- 路径:
/api/book/txt/init - 方法:GET
- 认证:需要登录
- 参数:
book_id(int, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"chapters": [...],
"total": 10
}- 路径:
/api/book/<id>/convert - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDformat(string, 可选): 目标格式
- 响应示例:
{
"err": "ok",
"content": "转换成功,请稍后刷新页面查看"
}- 路径:
/api/book/<id>/topdf - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"content": "转换成功,请稍后刷新页面查看"
}- 路径:
/api/book/<id>/totxtz - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "转换成功"
}- 路径:
/api/book/<id>/setsole - 方法:POST
- 认证:需要管理员权限
- 参数:
id(path, 必填): 图书IDsole(boolean, 必填): 是否已售出
- 响应示例:
{
"err": "ok",
"msg": "设置成功"
}- 路径:
/api/book/<id>/cover - 方法:GET
- 认证:无需认证
- 参数:
id(path, 必填): 图书ID
- 响应:返回图片文件
- 路径:
/api/book/<id>/favorite - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDfavorite(body, boolean, 必填):true为添加收藏,false为取消收藏
- 请求示例:
{
"favorite": true
}- 响应示例:
{
"err": "ok",
"msg": "收藏成功"
}- 路径:
/api/book/<id>/wants - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDwants(body, boolean, 必填):true为标记待读,false为取消待读
- 请求示例:
{
"wants": true
}- 响应示例:
{
"err": "ok",
"msg": "标记为待读成功"
}- 路径:
/api/book/<id>/readstate - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDread_state(body, int, 必填): 阅读状态(0=未读,1=在读,2=已读)online_read(body, boolean, 可选): 是否在线阅读,传入时会一并更新该状态download(body, boolean, 可选): 是否已下载,传入时会一并更新该状态
- 请求示例:
{
"read_state": 1
}- 响应示例:
{
"err": "ok",
"msg": "状态更新成功"
}- 路径:
/api/favorites - 方法:GET
- 认证:需要登录
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 50,
"books": [...]
}- 路径:
/api/wants - 方法:GET
- 认证:需要登录
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 30,
"books": [...]
}- 路径:
/api/reading - 方法:GET
- 认证:需要登录
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 10,
"books": [...]
}- 路径:
/api/read-done - 方法:GET
- 认证:需要登录
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 100,
"books": [...]
}- 路径:
/api/reading/stats - 方法:GET
- 认证:需要登录
- 参数:无
- 响应示例:
{
"err": "ok",
"stats": {
"favorites": 50,
"wants": 30,
"reading": 10,
"read_done": 100
}
}- 路径:
/api/book/<id>/tags - 方法:POST
- 认证:需要管理员或书籍所有者权限
- 参数:
id(path, 必填): 图书ID- JSON数据:包含tags数组
- 响应示例:
{
"err": "ok",
"msg": "标签更新成功"
}- 路径:
/api/book/<id>/aifill - 方法:POST
- 认证:需要管理员或书籍所有者权限
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "AI更新成功",
"category": "科幻",
"tags": ["硬科幻", "太空歌剧"]
}- 路径:
/api/book/update_tags - 方法:POST
- 认证:需要管理员权限
- 参数:
tag(string, 必填): 标签名
- 响应示例:
{
"err": "ok",
"msg": "已提交300本书籍的标签更新任务",
"count": 300
}- 路径:
/api/book/<id>/category - 方法:POST
- 认证:需要管理员或书籍所有者权限
- 参数:
id(path, 必填): 图书IDcategory(string, 必填): 分类名称
- 响应示例:
{
"err": "ok",
"msg": "分类更新成功"
}- 路径:
/api/book/category - 方法:POST
- 认证:需要管理员权限
- 参数:
category(string, 必填): 分类名称author(string, 可选): 作者名tag(string, 可选): 标签名
- 响应示例:
{
"err": "ok",
"msg": "成功更新100本书籍分类",
"count": 100
}- 路径:
/api/categories - 方法:GET
- 认证:无需认证
- 参数:无
- 响应示例:
{
"err": "ok",
"categories": [
{"name": "科幻", "count": 100},
{"name": "文学", "count": 200}
]
}- 路径:
/api/tags/search - 方法:GET
- 认证:无需认证
- 参数:
q(string, 必填): 搜索关键词limit(int, 可选): 返回数量,默认20
- 响应示例:
{
"err": "ok",
"tags": [
{"name": "科幻", "count": 100},
{"name": "硬科幻", "count": 50}
]
}- 路径:
/api/book/<id>/suggestion - 方法:GET
- 认证:无需认证
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"books": [...]
}- 路径:
/api/book/<id>/separate - 方法:POST
- 认证:需要管理员权限
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "拆分成功"
}- 路径:
/api/book/<id>/savemeta - 方法:POST
- 认证:需要管理员权限
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "元数据已保存"
}- 路径:
/api/book/<id>/addstamp - 方法:POST
- 认证:需要管理员权限
- 参数:
id(path, 必填): 图书IDstamp_id(int, 必填): 印章ID
- 响应示例:
{
"err": "ok",
"msg": "印章添加成功"
}- 路径:
/api/book/exchange_type - 方法:POST
- 认证:需要管理员权限
- 参数:
id1(int, 必填): 图书1的IDid2(int, 必填): 图书2的ID
- 响应示例:
{
"err": "ok",
"msg": "交换成功"
}- 路径:
/api/clear_rare_tags - 方法:POST
- 认证:需要管理员权限
- 参数:
threshold(int, 可选): 阈值,默认为1
- 响应示例:
{
"err": "ok",
"msg": "清理成功",
"count": 50
}- 路径:
/api/book/add - 方法:POST
- 认证:需要登录
- 参数:
- JSON数据:包含title、authors等信息
- 响应示例:
{
"err": "ok",
"book_id": 123
}- 路径:
/api/book/upload - 方法:POST
- 认证:需要登录
- 参数:
ebook(file, 必填): 图书文件,multipart/form-data 字段名须为ebook
- 响应示例:
{
"err": "ok",
"book_id": 123
}- 路径:
/api/book/upload/chunk - 方法:POST
- 认证:需要登录
- 参数:
chunk(file, 必填): 分块数据chunkIndex(int, 必填): 分块索引totalChunks(int, 必填): 总分块数filename(string, 必填): 文件名
- 响应示例:
{
"err": "ok",
"completed": false
}- 路径:
/api/book/<id>/reading_stats - 方法:GET
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDformat(query, 可选): 只查询指定格式(epub/pdf/mobi/azw3/txt 等,大小写不敏感,内部统一转小写)的统计数据;不传则返回该书籍下所有格式的统计
- 说明:
- 返回当前登录用户在该书籍下、按格式分别统计的阅读时长/进度/状态,与
BookDetail接口内嵌的book["reading_stats"]同源 - 只返回
total_seconds > 10(累计阅读时长超过10秒)的格式,按format字母序排列;从未产生有效阅读时长的格式不会出现在结果里。传了format但该格式尚无有效统计数据(或格式不存在)时,返回stats: [],不会报错 - 与本接口对应的 POST 方法用于手动补记/纠正某个格式的统计数据(例如导入历史阅读记录,或网页阅读器等没有自动心跳上报的场景),不在此文档展开
- 返回当前登录用户在该书籍下、按格式分别统计的阅读时长/进度/状态,与
- 响应示例:
{
"err": "ok",
"stats": [
{
"format": "epub",
"state": 0,
"total_seconds": 5460,
"progress_current": 128,
"progress_total": 320,
"progress_percent": 40.0,
"start_time": "2026-08-20T09:12:33Z",
"finish_time": null,
"start_count": 2,
"update_time": "2026-08-30T13:05:11Z"
},
{
"format": "pdf",
"state": 1,
"total_seconds": 9800,
"progress_current": 200,
"progress_total": 200,
"progress_percent": 100.0,
"start_time": "2026-07-01T02:00:00Z",
"finish_time": "2026-07-10T11:30:00Z",
"start_count": 1,
"update_time": "2026-07-10T11:30:00Z"
}
]
}字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
err |
string | 固定为 "ok" |
stats |
array | 按 format 字母序排列的分格式统计列表 |
stats[].format |
string | 书籍格式,统一小写(epub/pdf/mobi/azw3/txt 等) |
stats[].state |
integer | 阅读状态:0=在读,1=已完成 |
stats[].total_seconds |
integer | 该格式的累计阅读时长,单位:秒,跨轮次累加 |
stats[].progress_current |
integer | null | 最近一次上报的阅读进度当前值(如当前页码),未上报过为 null |
stats[].progress_total |
integer | null | 最近一次上报的阅读进度总量(如总页数),未上报过为 null |
stats[].progress_percent |
number | null | 阅读进度百分比(0~100,两位小数);state 为已完成(1)时固定返回 100.0,否则为最近一次上报换算得到的值,未上报过为 null |
stats[].start_time |
string (ISO8601, UTC, 以 Z 结尾) | null |
当前/最近一轮阅读的开始时间;未开始过为 null |
stats[].finish_time |
string (ISO8601, UTC, 以 Z 结尾) | null |
当前/最近一轮阅读的完成时间;重新开始阅读时会被清空为 null |
stats[].start_count |
integer | 开始阅读的次数(含首次) |
stats[].update_time |
string (ISO8601, UTC, 以 Z 结尾) |
该格式统计数据最后一次写入时间(心跳上报或手动更新) |
- 按格式过滤示例(
GET /api/book/123/reading_stats?format=epub,只返回stats中format为epub的一项,数组长度最多为 1):
{
"err": "ok",
"stats": [
{
"format": "epub",
"state": 0,
"total_seconds": 5460,
"progress_current": 128,
"progress_total": 320,
"progress_percent": 40.0,
"start_time": "2026-08-20T09:12:33Z",
"finish_time": null,
"start_count": 2,
"update_time": "2026-08-30T13:05:11Z"
}
]
}- 错误响应示例:
{ "err": "user.need_login", "msg": "请先登录" }- 路径:
/api/<meta> - 方法:GET
- 认证:无需认证
- 参数:
meta(path, 必填): 类型,可选值:author、publisher、tag、rating、series、languageshow(string, 可选): "all" 显示全部
- 响应示例:
{
"err": "ok",
"meta": "author",
"title": "全部作者",
"items": [
{"name": "刘慈欣", "count": 10},
{"name": "金庸", "count": 20}
],
"total": 100,
"pins": [...]
}- 路径:
/api/<meta>/<name> - 方法:GET
- 认证:无需认证
- 参数:
meta(path, 必填): 类型name(path, 必填): 元数据名称page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 50,
"books": [...]
}- 路径:
/api/author/<name>/update - 方法:POST
- 认证:需要管理员权限
- 参数:
name(path, 必填): 作者名
- 响应:302重定向
- 路径:
/api/publisher/<name>/update - 方法:POST
- 认证:需要管理员权限
- 参数:
name(path, 必填): 出版社名
- 响应:302重定向
- 路径:
/api/audio/<id> - 方法:GET
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"audio_dir": "/data/books/audios/123",
"audios": [
{
"filename": "第1章",
"url": "/api/audio/123/chapter01.mp3",
"size": 1024000
}
],
"total_files": 10,
"is_paid": true
}- 路径:
/api/audio/<id>/conversion - 方法:GET/POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID- POST: 启动音频转换
- 响应示例:
{
"err": "ok",
"status": "converting",
"progress": 50
}- 路径:
/api/audio/<id>/cancel - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "转换已取消"
}- 路径:
/api/audio/<id>/delete - 方法:POST
- 认证:需要管理员权限
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "音频文件删除成功"
}- 路径:
/api/audio/<id>/purchase - 方法:POST
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "购买成功"
}- 路径:
/api/audiobooks - 方法:GET
- 认证:无需认证
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 50,
"books": [...]
}- 路径:
/api/audio/<id>/<filename> - 方法:GET
- 认证:需要登录
- 参数:
id(path, 必填): 图书IDfilename(path, 必填): 音频文件名
- 响应:返回音频文件
- 路径:
/api/audios/<id>/collection - 方法:GET
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"files": [...]
}- 路径:
/api/audios/<id>/collection/download - 方法:GET
- 认证:需要登录
- 参数:
id(path, 必填): 图书ID
- 响应:返回ZIP文件
- 路径:
/api/admin/ssl - 方法:GET/POST
- 认证:需要管理员权限
- 参数:
- GET: 获取SSL证书信息
- POST: 上传SSL证书(JSON)
- 响应示例:
{
"err": "ok",
"ssl": {
"enabled": true,
"expire_date": "2027-05-11"
}
}- 路径:
/api/admin/users - 方法:GET/POST
- 认证:需要管理员权限
- 参数:
- GET: 获取用户列表
page(int, 可选): 页码,默认1num(int, 可选): 每页数量,默认20sort(string, 可选): 排序字段desc(string, 可选): 是否降序
- POST: 更新用户信息(JSON)
- GET: 获取用户列表
- 响应示例:
{
"err": "ok",
"users": {
"items": [...],
"total": 100
},
"settings": {...}
}- 路径:
/api/admin/install - 方法:GET/POST
- 认证:无需认证(仅在未安装时可用)
- 参数:
- POST: 安装参数(JSON)
- 响应示例:
{
"err": "ok",
"msg": "安装成功"
}- 路径:
/api/admin/settings - 方法:GET/POST
- 认证:需要管理员权限
- 参数:
- GET: 获取系统设置
- POST: 更新系统设置(JSON)
- 响应示例:
{
"err": "ok",
"settings": {...}
}- 路径:
/api/admin/testmail - 方法:POST
- 认证:需要管理员权限
- 参数:
smtp_server(string, 必填): SMTP服务器smtp_username(string, 必填): SMTP用户名smtp_password(string, 必填): SMTP密码smtp_encryption(string, 必填): 加密方式
- 响应示例:
{
"err": "ok",
"msg": "发送成功"
}- 路径:
/api/admin/book/list - 方法:GET
- 认证:需要管理员权限
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量
- 响应示例:
{
"err": "ok",
"total": 1000,
"books": [...]
}- 路径:
/api/admin/book/fill - 方法:GET/POST
- 认证:需要管理员权限
- 参数:
book_id(int, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "填充成功"
}- 路径:
/api/admin/book/aifill - 方法:GET/POST
- 认证:需要管理员权限
- 参数:
book_id(int, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "AI填充成功"
}- 路径:
/api/admin/book/epubconvert - 方法:GET/POST
- 认证:需要管理员权限
- 参数:
book_id(int, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "转换已启动"
}- 路径:
/api/admin/book/update_title_sort - 方法:GET/POST
- 认证:需要管理员权限
- 参数:无或book_id
- 响应示例:
{
"err": "ok",
"msg": "更新成功"
}- 路径:
/api/admin/bookbarn/token/apply - 方法:POST
- 认证:需要管理员权限
- 参数:
email(string, 必填): 邮箱
- 响应示例:
{
"err": "ok",
"msg": "申请成功"
}- 路径:
/api/admin/books/delete - 方法:POST
- 认证:需要管理员权限
- 参数:
book_ids(array, 必填): 图书ID数组
- 响应示例:
{
"err": "ok",
"msg": "删除成功",
"count": 10
}- 路径:
/api/admin/books/save_meta - 方法:POST
- 认证:需要管理员权限
- 参数:
book_ids(array, 必填): 图书ID数组
- 响应示例:
{
"err": "ok",
"msg": "保存成功"
}- 路径:
/api/admin/clear/invalid/items - 方法:POST
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"msg": "清理成功",
"count": 50
}- 路径:
/api/admin/audio/test - 方法:POST
- 认证:需要管理员权限
- 参数:
text(string, 必填): 测试文本
- 响应示例:
{
"err": "ok",
"audio_url": "/path/to/test.mp3"
}- 路径:
/api/admin/release/notes - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"notes": "版本更新说明..."
}- 路径:
/api/admin/token - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"token": "admin-token-here"
}- 路径:
/api/admin/tasks/running - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"tasks": [
{
"id": "task-123",
"type": "convert",
"status": "running",
"progress": 50
}
]
}- 路径:
/api/admin/trash/size - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"size": 1024000000
}- 路径:
/api/admin/trash/clear - 方法:POST
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"msg": "清空成功"
}- 路径:
/api/admin/stamp - 方法:GET/POST
- 认证:需要管理员权限
- 参数:
- GET: 获取印章列表
- POST: 添加/更新/删除印章
- 响应示例:
{
"err": "ok",
"stamps": [...]
}- 路径:
/api/library/stats - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"stats": {
"total_books": 1000,
"total_authors": 200,
"total_publishers": 100,
"total_users": 50
}
}- 路径:
/api/admin/syslog - 方法:GET
- 认证:需要管理员权限
- 参数:
lines(int, 可选): 显示行数,默认100
- 响应示例:
{
"err": "ok",
"log": "系统日志内容..."
}- 路径:
/api/admin/book/update_all_meta - 方法:POST
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"msg": "批量更新已启动"
}- 路径:
/api/admin/book/update_all_dynamic_cover - 方法:POST
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"msg": "批量更新已启动"
}- 路径:
/api/admin/book/reset_cover - 方法:POST
- 认证:需要管理员权限
- 参数:
book_id(int, 必填): 图书ID
- 响应示例:
{
"err": "ok",
"msg": "重置成功"
}- 路径:
/api/admin/syslog/download - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应:返回日志文件下载
- 路径:
/api/admin/barcode - 方法:POST
- 认证:需要登录
- 参数:
barcode_image(file, 必填): 条形码图片
- 响应示例:
{
"err": "ok",
"isbn": "9787536692930"
}- 路径:
/api/admin/import/list - 方法:GET
- 认证:需要管理员权限
- 参数:
page(int, 可选): 页码num(int, 可选): 每页数量sort(string, 可选): 排序字段desc(string, 可选): 是否降序filter(string, 可选): 过滤条件(all/todo/done)
- 响应示例:
{
"err": "ok",
"items": [...],
"total": 100,
"scanning": false,
"importing": false,
"summary": {
"total": 100,
"done": 50,
"todo": 50
},
"scan_dir": "/data/books/import"
}- 路径:
/api/admin/import/delete - 方法:POST
- 认证:需要管理员权限
- 参数:
hashlist(array/string, 必填): 文件hash列表或"all"
- 响应示例:
{
"err": "ok",
"msg": "删除成功",
"count": 10
}- 路径:
/api/admin/import/run - 方法:POST
- 认证:需要管理员权限
- 参数:
filelist(array/string, 必填): 文件列表或"all"skip_last_dirs(int, 可选): 跳过最后几级目录
- 响应示例:
{
"err": "ok",
"msg": "扫描成功"
}- 路径:
/api/admin/import/status - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"task": "task-id",
"status": {
"ready": 10,
"importing": 5,
"imported": 50,
"exist": 30,
"total": 100,
"processed": 85
},
"summary": {...},
"scanning": false,
"ignored_errors": [],
"importing": true
}- 路径:
/api/admin/batch_add/run - 方法:POST
- 认证:需要管理员权限
- 参数:
csv_file(file, 必填): CSV文件
- 响应示例:
{
"err": "ok",
"msg": "批量添加已启动"
}- 路径:
/api/admin/batch_add/status - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"status": "running",
"progress": 50,
"total": 100
}- 路径:
/api/admin/audio_import/run - 方法:POST
- 认证:需要管理员权限
- 参数:
audio_files(array, 必填): 音频文件列表
- 响应示例:
{
"err": "ok",
"msg": "音频导入已启动"
}- 路径:
/api/admin/audio_import/status - 方法:GET
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"status": "running",
"progress": 30
}- 路径:
/api/admin/import/cancel - 方法:POST
- 认证:需要管理员权限
- 参数:无
- 响应示例:
{
"err": "ok",
"msg": "导入已取消"
}OPDS(Open Publication Distribution System)接口用于提供标准化的图书目录服务,兼容各种OPDS客户端。
- 路径:
/opds/ - 方法:GET
- 认证:无需认证
- 参数:无
- 响应:返回OPDS Feed(XML)
- 路径:
/opds/nav/<which> - 方法:GET
- 认证:无需认证
- 参数:
which(path, 必填): 导航类型offset(int, 可选): 偏移量
- 响应:返回OPDS Feed(XML)
- 路径:
/opds/category/<category>/<which> - 方法:GET
- 认证:无需认证
- 参数:
category(path, 必填): 分类名which(path, 必填): 子分类offset(int, 可选): 偏移量
- 响应:返回OPDS Feed(XML)
- 路径:
/opds/categorygroup/<category>/<which> - 方法:GET
- 认证:无需认证
- 参数:
category(path, 必填): 分类组名which(path, 必填): 子分类
- 响应:返回OPDS Feed(XML)
- 路径:
/opds/search/<query> - 方法:GET
- 认证:无需认证
- 参数:
query(path, 必填): 搜索关键词offset(int, 可选): 偏移量
- 响应:返回OPDS Feed(XML)
- 路径:
/get/pcover - 方法:GET
- 认证:无需认证
- 参数:
url(string, 必填): 原始图片URL
- 响应:返回图片文件
- 路径:
/get/progress/<id> - 方法:GET
- 认证:无需认证
- 参数:
id(path, 必填): 任务ID
- 响应:返回进度文本
- 路径:
/get/extract/<book_id>/<path> - 方法:GET
- 认证:需要登录
- 参数:
book_id(path, 必填): 图书IDpath(path, 必填): 内部路径
- 响应:返回提取的文件内容
- 路径:
/get/<type>/<name> - 方法:GET
- 认证:无需认证
- 参数:
type(path, 必填): 资源类型(cover/thumb_*等)name(path, 必填): 资源名称
- 响应:返回资源文件
- 路径:
/<path> - 方法:GET
- 认证:无需认证
- 参数:
path(path, 必填): 文件路径
- 响应:返回静态文件
Podcast接口用于提供RSS订阅服务,支持播客客户端订阅有声书。
- 路径:
/podcast/ - 方法:GET
- 认证:无需认证(部分功能需要Token)
- 参数:无
- 响应:返回Podcast首页(HTML)
- 路径:
/podcast/all - 方法:GET
- 认证:无需认证
- 参数:
token(string, 可选): 用户Token
- 响应:返回RSS Feed(XML)
- 路径:
/podcast/book/<id> - 方法:GET
- 认证:无需认证
- 参数:
id(path, 必填): 图书IDtoken(string, 可选): 用户Token
- 响应:返回RSS Feed(XML)
- 路径:
/podcast/book/<id>/opml - 方法:GET
- 认证:无需认证
- 参数:
id(path, 必填): 图书ID
- 响应:返回OPML(XML)
- 路径:
/podcast/category/<name> - 方法:GET
- 认证:无需认证
- 参数:
name(path, 必填): 分类名token(string, 可选): 用户Token
- 响应:返回HTML页面列出该分类下的有声书
- 路径:
/podcast/tag/<name> - 方法:GET
- 认证:无需认证
- 参数:
name(path, 必填): 标签名token(string, 可选): 用户Token
- 响应:返回HTML页面列出该标签下的有声书
- 路径:
/podcast/author/<name> - 方法:GET
- 认证:无需认证
- 参数:
name(path, 必填): 作者名token(string, 可选): 用户Token
- 响应:返回HTML页面列出该作者的有声书
- 路径:
/podcast/<token>/book/<id> - 方法:GET
- 认证:通过Token认证
- 参数:
token(path, 必填): 用户Podcast Tokenid(path, 必填): 图书ID
- 响应:返回RSS Feed(XML)
- 路径:
/podcast/<token>/ - 方法:GET
- 认证:通过Token认证
- 参数:
token(path, 必填): 用户Podcast Token
- 响应:返回HTML页面列出个人订阅
- 路径:
/podcast/audio/<book_id>/<token>/<filename> - 方法:GET
- 认证:通过Token认证
- 参数:
book_id(path, 必填): 图书IDtoken(path, 必填): 访问Tokenfilename(path, 必填): 音频文件名
- 响应:返回音频文件
- 路径:
/api/assistant/ws - 方法:WebSocket
- 认证:需要登录
- 参数:
- WebSocket消息(JSON):
content(string, 必填): 用户输入内容
- WebSocket消息(JSON):
- 响应:
- 流式返回AI助手响应(JSON chunks)
- 消息类型:
{"type": "status", "content": "..."}{"type": "start"}{"type": "data", "content": "..."}{"type": "end"}{"type": "error", "content": "..."}
- 路径:
/api/mcp/stream - 方法:GET/POST
- 认证:可选(支持Token参数)
- 参数:
- GET: 获取MCP服务信息
- POST: 发送MCP请求(JSON-RPC格式)
token(query, 可选): 访问Token
- 响应示例:
GET响应:
{
"err": "ok",
"version": "v3.20.0",
"settings": {
"base_url": "https://your.site",
"mcp_version": "v3.20.0"
},
"timestamp": "2026-05-11T10:00:00"
}POST响应:
{
"err": "ok",
"jsonrpc": "2.0",
"result": {...}
}- 路径:
/api/mcp/health - 方法:GET
- 认证:无需认证
- 参数:无
- 响应示例:
{
"err": "ok",
"status": "healthy",
"server": "mybooks-mcp"
}常见错误代码:
ok: 请求成功params.invalid: 参数无效params.book.invalid: 图书不存在params.user.not_exist: 用户不存在permission.not_admin: 非管理员无权限permission.denied: 权限不足permission.inactive: 用户未激活user.no_permission: 无权限操作db.error: 数据库错误email.server_error: 邮件服务器错误internal: 内部错误
阅读状态字段(read_state):
0: 未读1: 在读2: 已读
收藏/待读状态:
favorite: 1=已收藏,0=未收藏wants: 1=想读,0=不想读
图书类型字段(book_type):
0: 电子书1: 实体书
大部分API使用Cookie进行认证,登录后会设置以下Cookie:
user_id: 用户ID(加密)admin_id: 管理员ID(管理员切换用户时使用)
部分API支持Token认证:
- Podcast订阅:使用
podcast_token参数 - MCP服务:使用
token查询参数 - 音频访问:使用Token进行权限验证