#1 - 2019-5-10 13:37
astrea (-Wish upon a Shooting Star-)
总结一下我在使用Bangumi API中的一些坑,希望能帮到以后使用API的人。如果你发现了什么我没注意到的坑欢迎补充,我会补充到主楼。
# 如何阅读本指南
每个条目以下面其中的一个词开头:
*要* 开头所表述的内容是要准守的。 几乎从来不会有合理的理由来违反这些条目。
*推荐* 开头所表述的内容你应该准守。 但是在有些情况下,可能有更好的或者更合理的做法。 当你不遵守这些条目的时候,请确保你有合理的 理由这么做。
*避免* 开头所表述的内容和“推荐”相反: 如果没有足够好的理由,你不应该这么做。
*考虑* 开头所表述的内容你可以遵守, 也可以不准守,根据你的实际情况来选择。
*注意* Bangumi API中需要注意的坑
本格式参考 [Effective Dart](http://dart.goodev.org/guides/language/effective-dart)
# 正文
要 :在Oauth认证的过程中,出错后重试你的请求至少一次。
理由:具体原因就是https://github.com/bangumi/api/b ... -raw/How-to-Auth.md 里提到的`POST https://bgm.tv/oauth/access_token`步骤,这一步有大概(体感)10%~20%的几率返回一个`httpStatus=500`和`null`导致认证失败,要解决也很简单,直接带着原来的所有数据重试一次即可。
要: 在使用搜索API `/search/subject/{keywords}` 时,在每个请求后附加`chii_searchDateLine=[填入搜索时间]`cookie。
理由:避免过于频繁的API搜索是我们应该遵守的,但实际上在使用过程中会发生非常奇妙的事情:请尝试`GET https://api.bgm.tv/search/subject/,,,`
在不附加`chii_searchDateLine=[填入搜索时间]` cookie的情况下会无条件返回“搜索过于频繁”的html。目前只有一部分关键词会返回html,但因为不清楚触发条件,最好的做法是在每个搜索请求中附加此cookie
参考bug https://github.com/bangumi/api/issues/43
避免 :使用"批量更新收视进度"API(`/subject/{subject_id}/update/watched_eps`)更新非书籍的进度
理由:先看官方文档,此API的作用非常简单,更新进度到指定话数/卷数。
对书籍这是唯一一个批量更新进度的方法也没什么坑,不多说了。
动画和三次元就有坑了,此API有众多令人惊讶的行为:
1. 对于动画话数不是从第一话开始的作品,比如[一拳超人第二季](https://bgm.tv/subject/193619)从第13话开始,但是如果尝试传入`watched_eps=13`则会把用户的第13话到第25话全部更新为看过。没错,这里的`watched_eps=${x}`准确的说指的是“从这部作品的真正的第一话(对于一拳超人这是第13话)开始往后数x话,并把他们全部标为看过”。
2. “看到”对非正篇的章节无效,还是拿一拳超人第二季举例,它有2个sp,第1话和第12.5话,我们知道1的坑了,那么现在如果尝试把前三话更新为看过,传入`watched_eps=3`,会被更新进度的是13,14和15话,而非第1,12.5和第13话。换句话说批量更新只对正篇起效。
3. `watched_eps=${x}`之后的话数进度会被清空,这一行为对所有动画/三次元作品均起效。比如[皿三昧](https://bgm.tv/subject/239646),假设用户目前只标记第5话为看过,然后程序尝试通过批量更新收视进度API标记前三话为看过,会导致第5话进度直接消失。
总结一下,"批量更新收视进度"的真实作用为“从这部作品的真正的第一话(而非作品本身的话数)开始更新x话正篇为看过,同时把x话之后的进度全部清空”
那么正确进行“看过”操作的做法是什么?我的建议是对`/ep/{id}/status/{status}`批量传入需要更新的章节id。
摘抄自Bangumi的代码
另外需要注意,/ep/{id}/status/{status} status传入的值只对query的id有效,post里的ep status只要不是抛弃就总会变成“看过”(@ekibun)。
点击“看到某话”不影响已抛弃章节的状态这一行为和网页版点击看到的结果相同(另外此请求似乎需要发送两次,否则api返回的数据不会更新)。
推荐 :在拿回API数据后对Bangumi的API和json的field进行合理的重命名。
理由:比如`GET /user/{username}/collection`的返回值里有一个`ep_status`,这里返回了一个`int`,值的意义其实是"完成过的话数"而不是“看过的话数的状态”,过一段时间重读代码很容易忘记这个field的真实含义,如果在开发初期就在拿回数据后重命名为更准确的名字比如`completedEpisodesCount`可以避免很多困惑。类似的field还有很多,建议不要怕麻烦在初期就想好一个清晰的重命名,否则到了后期需要重命名时只会更麻烦。
考虑 :在更新作品tag时,手动过滤非法字符
理由:Bangumi的服务器会帮我们过滤掉一部分的非法字符而非所有,有时候非法的参数会导致返回一个html,请参见https://github.com/bangumi/api/issues/27
考虑 :把bangumi返回的code作为辅助状态参考信息而非主要状态信息,并永远以Bangumi的返回值里不存在`code`为前提假设进行开发。
理由: 请参考 https://github.com/bangumi/api/issues/13
Bangumi会尽量尝试永远返回`HTTPStatusCode=200`并在返回的json里添加一个顶层field`code`来展示真实状态。但实际api并不会永远返回这个`code`。
比如“用户收视进度” `/user/{username}/progress` 返回的就是一个`List`或者`null`,其中并不含`code`但不管是`List`还是`null`其实都代表响应成功。
注意 :`GET /collection/{subject_id}`的返回值里有一个field叫`tags`,返回的是一个由`String`组成的`List`。但是当这个条目用户没有输入任何标签时,tags返回的是一个包含一个空字符串的`List`(`[""]`),而非`null`或者`[]`
注意 : `POST /collection/{subject_id}/{action}`里,请求的field有一个叫做`privacy`决定这是否是一个隐私收藏,但返回值里这个field则被称作`private`
注意 : 搜索API `/search/subject/{keywords}`里有一个`max_results`可以指定返回条目的数目,这里的`max_results`就是字面意思`max_results`,有些条目不会被返回,所以`max_results=10`可能只返回8个条目。
注意(@trim21): calendar里面的name_cn是经过html escape过的,需要你在使用的时候unescape
注意 :Bangumi的API偶尔会导致串号,既认证成功后尝试用token更新用户状态时,会导致其他用户的状态被更新。
目前没有发现避免的方法,不过这个问题并不频繁。串号之后访问`https://bgm.tv/oauth/token_status`会拿到串号用户的信息,可以在每次刷新token后验证此信息。
目前我能想到的就这些问题,欢迎补充。
顺便打个广告,我们(BangumiN, https://github.com/edwardez/BangumiN/tree/develop/app )正在开发基于flutter的Bangumi客户端,计划发布iOS/Android双端,除了官方API的功能之外还通过解析html实现了查看时间线,查看用户主页和所有收藏,搜索人物,刷超展开,查看作品所有评论,一键生成作品评论海报等功能,欢迎有使用flutter经验的朋友来贡献代码:P
# 如何阅读本指南
每个条目以下面其中的一个词开头:
*要* 开头所表述的内容是要准守的。 几乎从来不会有合理的理由来违反这些条目。
*推荐* 开头所表述的内容你应该准守。 但是在有些情况下,可能有更好的或者更合理的做法。 当你不遵守这些条目的时候,请确保你有合理的 理由这么做。
*避免* 开头所表述的内容和“推荐”相反: 如果没有足够好的理由,你不应该这么做。
*考虑* 开头所表述的内容你可以遵守, 也可以不准守,根据你的实际情况来选择。
*注意* Bangumi API中需要注意的坑
本格式参考 [Effective Dart](http://dart.goodev.org/guides/language/effective-dart)
# 正文
要 :在Oauth认证的过程中,出错后重试你的请求至少一次。
理由:具体原因就是https://github.com/bangumi/api/b ... -raw/How-to-Auth.md 里提到的`POST https://bgm.tv/oauth/access_token`步骤,这一步有大概(体感)10%~20%的几率返回一个`httpStatus=500`和`null`导致认证失败,要解决也很简单,直接带着原来的所有数据重试一次即可。
要: 在使用搜索API `/search/subject/{keywords}` 时,在每个请求后附加`chii_searchDateLine=[填入搜索时间]`cookie。
理由:避免过于频繁的API搜索是我们应该遵守的,但实际上在使用过程中会发生非常奇妙的事情:请尝试`GET https://api.bgm.tv/search/subject/,,,`
在不附加`chii_searchDateLine=[填入搜索时间]` cookie的情况下会无条件返回“搜索过于频繁”的html。目前只有一部分关键词会返回html,但因为不清楚触发条件,最好的做法是在每个搜索请求中附加此cookie
参考bug https://github.com/bangumi/api/issues/43
避免 :使用"批量更新收视进度"API(`/subject/{subject_id}/update/watched_eps`)更新非书籍的进度
理由:先看官方文档,此API的作用非常简单,更新进度到指定话数/卷数。
对书籍这是唯一一个批量更新进度的方法也没什么坑,不多说了。
动画和三次元就有坑了,此API有众多令人惊讶的行为:
1. 对于动画话数不是从第一话开始的作品,比如[一拳超人第二季](https://bgm.tv/subject/193619)从第13话开始,但是如果尝试传入`watched_eps=13`则会把用户的第13话到第25话全部更新为看过。没错,这里的`watched_eps=${x}`准确的说指的是“从这部作品的真正的第一话(对于一拳超人这是第13话)开始往后数x话,并把他们全部标为看过”。
2. “看到”对非正篇的章节无效,还是拿一拳超人第二季举例,它有2个sp,第1话和第12.5话,我们知道1的坑了,那么现在如果尝试把前三话更新为看过,传入`watched_eps=3`,会被更新进度的是13,14和15话,而非第1,12.5和第13话。换句话说批量更新只对正篇起效。
3. `watched_eps=${x}`之后的话数进度会被清空,这一行为对所有动画/三次元作品均起效。比如[皿三昧](https://bgm.tv/subject/239646),假设用户目前只标记第5话为看过,然后程序尝试通过批量更新收视进度API标记前三话为看过,会导致第5话进度直接消失。
总结一下,"批量更新收视进度"的真实作用为“从这部作品的真正的第一话(而非作品本身的话数)开始更新x话正篇为看过,同时把x话之后的进度全部清空”
那么正确进行“看过”操作的做法是什么?我的建议是对`/ep/{id}/status/{status}`批量传入需要更新的章节id。
摘抄自Bangumi的代码
if (ep_status == 'WatchedTill') {
var epLiIndex = epAs.index(epBtn);
var ids = new Array();
for (var i = 0; i <= epLiIndex; i++) {
ids[i] = epAs[i].id.split('_')[1];
}
params['ep_id'] = ids.toString();
if (OtherEps[subject_id] != undefined) {
params['ep_id'] = OtherEps[subject_id] + ',' + params['ep_id'];
}
}
另外需要注意,/ep/{id}/status/{status} status传入的值只对query的id有效,post里的ep status只要不是抛弃就总会变成“看过”(@ekibun)。
点击“看到某话”不影响已抛弃章节的状态这一行为和网页版点击看到的结果相同(另外此请求似乎需要发送两次,否则api返回的数据不会更新)。
推荐 :在拿回API数据后对Bangumi的API和json的field进行合理的重命名。
理由:比如`GET /user/{username}/collection`的返回值里有一个`ep_status`,这里返回了一个`int`,值的意义其实是"完成过的话数"而不是“看过的话数的状态”,过一段时间重读代码很容易忘记这个field的真实含义,如果在开发初期就在拿回数据后重命名为更准确的名字比如`completedEpisodesCount`可以避免很多困惑。类似的field还有很多,建议不要怕麻烦在初期就想好一个清晰的重命名,否则到了后期需要重命名时只会更麻烦。
考虑 :在更新作品tag时,手动过滤非法字符
理由:Bangumi的服务器会帮我们过滤掉一部分的非法字符而非所有,有时候非法的参数会导致返回一个html,请参见https://github.com/bangumi/api/issues/27
考虑 :把bangumi返回的code作为辅助状态参考信息而非主要状态信息,并永远以Bangumi的返回值里不存在`code`为前提假设进行开发。
理由: 请参考 https://github.com/bangumi/api/issues/13
Bangumi会尽量尝试永远返回`HTTPStatusCode=200`并在返回的json里添加一个顶层field`code`来展示真实状态。但实际api并不会永远返回这个`code`。
比如“用户收视进度” `/user/{username}/progress` 返回的就是一个`List`或者`null`,其中并不含`code`但不管是`List`还是`null`其实都代表响应成功。
注意 :`GET /collection/{subject_id}`的返回值里有一个field叫`tags`,返回的是一个由`String`组成的`List`。但是当这个条目用户没有输入任何标签时,tags返回的是一个包含一个空字符串的`List`(`[""]`),而非`null`或者`[]`
注意 : `POST /collection/{subject_id}/{action}`里,请求的field有一个叫做`privacy`决定这是否是一个隐私收藏,但返回值里这个field则被称作`private`
注意 : 搜索API `/search/subject/{keywords}`里有一个`max_results`可以指定返回条目的数目,这里的`max_results`就是字面意思`max_results`,有些条目不会被返回,所以`max_results=10`可能只返回8个条目。
注意(@trim21): calendar里面的name_cn是经过html escape过的,需要你在使用的时候unescape
注意 :Bangumi的API偶尔会导致串号,既认证成功后尝试用token更新用户状态时,会导致其他用户的状态被更新。
目前没有发现避免的方法,不过这个问题并不频繁。串号之后访问`https://bgm.tv/oauth/token_status`会拿到串号用户的信息,可以在每次刷新token后验证此信息。
目前我能想到的就这些问题,欢迎补充。
顺便打个广告,我们(BangumiN, https://github.com/edwardez/BangumiN/tree/develop/app )正在开发基于flutter的Bangumi客户端,计划发布iOS/Android双端,除了官方API的功能之外还通过解析html实现了查看时间线,查看用户主页和所有收藏,搜索人物,刷超展开,查看作品所有评论,一键生成作品评论海报等功能,欢迎有使用flutter经验的朋友来贡献代码:P
原来会掉吗,侠坐茶说
现象大概是在refreshtoken能返回token的情况下(好像还能正确获取到进度信息),会偶然出现无法获取和设置收藏状态。
这时候重新授权问题消失。
检查了一下,获取到的token应该是没问题的,因为每次都会根据token的user_id更新用户名和头像:https://github.com/ekibun/Bangum ... ainPresenter.kt#L77
我刚才突然发现如果status是抛弃那就不受影响..改了一下这句话
如果你按照sort去调用这个api,就会踩坑
这个api现在的行为实际上是网页右侧的修改完成度,而不是所描述的修改播放进度
在某集tooltip中调用看到本集的话,不会清空某集之后的进度的
如果这个api叫做修改条目完成度的话倒是一点问题都没有
回到第一点,要看你怎么定义有问题,对我来说容易被误解容易出错的说明就是有问题的,就像trim21所说在任何api里都是拿不到一拳超人第一话其实是1这个数据的,同时api又会返回一个sort(话数)=13,我相信不止我一个人会误解这一点,所以就在建议里说明了一下。
如果要做发帖估计我会研究一下怎么markdown转bbcode,不过这块目前app完全没做,不知道第一版发出来之前有没有时间研究一下
(((
我之前想过用Markdown来发帖但是没有类似的转换工具,所以才写了一个。
但bbcode转markdown到底要什么情况下从能用的到,会bbcode又不会markdown的人又怎么会有markdown的使用需求。。。
混合编辑似乎也不错
不过我刚刚发现,个人主页这个预览也有可能会暴露
比如你个人主页的在看显示了一个“向山进发第二季”的条目,但是在看的全部列表里面没有
我想了下这其实不算一个坑更像是一个bug,应该去github开个issue?
主要问题是这个null
而且下面的说明里面还有“默认为在看”