wxt2005 / bangumi-list

大陆版权新番播放地址聚合站 V2
http://bgmlist.com/
410 stars 54 forks source link

关于后续数据维护方案的讨论 #39

Closed wxt2005 closed 7 years ago

wxt2005 commented 8 years ago

之前的讨论见 #38

现在的几种方案:

  1. 沿用手工维护 json 的方式
  2. 自建数据库
  3. 采用类似 LeanCloud 的解决方案
weizhenye commented 8 years ago

如果要把数据部分独立出来,可以讨论一下数据的定位了。 是单纯地满足 bgmlist.com 就好?还是作为一个番组信息聚合的项目? 目前不少项目都在使用该数据,是否要满足他们的需求?(企图添加 S1 专楼的我

另外,数据维护面向的群体?会 HTTP 增删查改的人吗?会 GitHub 的人吗?一般大众吗?

在我看来,GitHub 上维护 JSON 提供的是数据,而 API 服务器提供的是服务。数据是去中心化的,服务则是要有各种保障。我该如何知道哪些数据更新了?假设你写完 API 服务器开源了,其他人要自行部署该如何同步到最新数据?

我是认为维护 JSON 的方式比较好,独立为 repository 可以有。

wxt2005 commented 8 years ago

考虑了一下,我觉得去中心化的方案确实较优。大家都可以使用这份数据来构建自己的站点,同时也可以帮助完善内容。

数据定位来说,应该可以不限于 bgmlist.com 所需要的那些,但暂时还是以放送时间、放送站点以及一些主要的 meta data 为主会比较好(你提到的 S1 专楼,可以放在相关链接之类的字段中)。当然有人愿意贡献的话,可以考虑在将来添加更多的字段。

放在 GitHub 上的话,维护者应该主要还是 GitHub 用户了。对于普通用户来说,可以采用 @CnSimonChan 所移到的通过 bot 自动生成 issue 的方式来反馈问题。

weizhenye commented 8 years ago

一些设想:

  1. 发布到 npm。更新数据增加修订号,扩展字段增加次版本号,不兼容的字段改变增加主版本号。
  2. 添加测试。比如一些必填字段有没有填写、字段顺序有没有对。可能的话可以增加「覆盖率」,非必填字段的填写情况。不知道测试和覆盖率能否生成某种形式的「报告」,丢一些 error 和 warning 出来,方便修改和补充。
  3. 尝试实现自动获取站点的放送数据,然后人工审核这些数据是否正确。如果难以实现,尝试检测站点是否有该番组,人工去获取。总之需要一个减少维护工作量的方案。
  4. 命令行工具或一些数据操作 API。假设 package 名为 bgmdata,可以使用 bgmdata -s 201607 -o ./json/bangumi-201607.json 之类的命令;或者 const bgmdata = require('bgmdata'); 后,可以使用 bgmdata.raw(); 之类的 API 来获取特定数据。不过严格来讲这些功能应该算是另一个项目,应该由 bgmdata-cli 之类的包来做,bgmdata 可能应该只提供一个 JSON。
weizhenye commented 8 years ago

讨论下数据格式?

title 是扁平化还是结构化?

{
  "titleCN": "偶像大师 灰姑娘女孩",
  "titleEN": "THE IDOLM@STER CINDERELLA GIRLS",
  "titleJP": "アイドルマスター シンデレラガールズ"
}
{
  "title": {
    "en-US": "THE IDOLM@STER CINDERELLA GIRLS",
    "ja": "アイドルマスター シンデレラガールズ",
    "zh-Hans": "偶像大师 灰姑娘女孩",
    "zh-Hant": "偶像大師 灰姑娘女孩"
  }
}

放送时间是否使用世界标准时间?例如 {"showDate": "2015-01-09", "timeJP": "2300"},由 Date.parse('2015-01-09 23:00:00 UTC+8') 计算得,{"showTimeJP": 1420815600000}weekDayJP 是冗余数据,newBgm 应该也算冗余。 另外和 title 一样,扁平还是结构?showTimeCN 足够表示放送信息吗?各视频站放送时间可能不同,是否都要收录?

{
  "time": {
    // 日本电视台放送开始
    "begin": 1420815600000,
    // 日本电视台放送结束
    "end": 1428678000000,
    // 各视频站放送开始
    "acfun": 1420821000000,
    "bilibili": 1420821000000
  }
}

最后是 sites 信息:

{
  "site": {
    "offical": {
      "type": "info",
      "url": "http://imas-cinderella.com/"
    },
    "tudou": {
      "type": "onair",
      "comment": "注释",
      "url": "http://www.tudou.com/albumcover/qgdKuHlPg9g.html",
      // or 
      "id": "qgdKuHlPg9g"
    }
  }
}

我是觉得 sites 为数组更方便 filter:

{
  "sites": [
    {
      "name": "offical",
      "type": "offical",
      "url": "http://imas-cinderella.com/"
    },
    {
      "name": "tudou",
      "type": "onair",
      "comment": "注释",
      "id": "qgdKuHlPg9g"
    },
    {
      "name": "saraba1st",
      "type": "info",
      "id": "1059367"
    },
    {
      "name": "dmhy",
      "type": "resource",
      "keyword": "娘女孩"
    }
  ]
}

最终的数据:

[
  {
    "id": 101399,
    "title": {},
    "time": {},
    "sites": []
  },
  {
    "id": 110600,
    "title": {},
    "time": {},
    "sites": []
  },
  // ...
]
wxt2005 commented 8 years ago

参考你的建议,修改了一些地方:

[
    {
        // id 建议不要和 bangumi 挂钩比较好,毕竟数据本身是不依赖它的。
        // 可以用“年份+开播月份+index”
        "id": 161002,
        // meta data 尽量还是放在最外层
        "title": "アイドルマスター シンデレラガールズ",
        "officialSite": "http://imas-cinderella.com/",
        "begin": 1420815600000,
        "end": 1428678000000,
        "comments": "注释",
        // 所有翻译的标题放在一起
        "titleTrans": {
            "en-US": [
                "THE IDOLM@STER CINDERELLA GIRLS"
            ],
            // 考虑到多个译名的情况,可以为一个数组
            "zh-Hans": [
                "偶像大师 灰姑娘女孩",
                "爱马仕 灰姑娘"
            ],
            "zh-Hant": [
                "偶像大師 灰姑娘女孩"
            ]
        },
        // 放送站点、下载站点和其它站点分开
        "watchSites": {
            "tudou": {
                // url 建议还是用实际链接,有些站都换过几次链接格式了,id 不一定通用。
                // 可能增加一些维护成本
                "url": "http://www.tudou.com/albumcover/qgdKuHlPg9g.html",
                // 是否官方放送,例如 C 站就不能算
                "official": true,
                // 是否只有会员才能看
                "premuiumOnly": true,
                "begin": 1420821000000,
                // 是否为和谐版
                "censored": true,
                // 预留以后可能引入英文放送站的可能性
                "lang": "zh",
                "comments": "注释"
            }
        },
        "downloadSites": {
            "dmhy": {
                "keyword": "娘女孩"
            }
        },
        "otherSites": {
            "saraba1st": {
                "id": "1059367"
            },
            "bangumi": {
                "id": "68812"
            }
        }
    }
]
weizhenye commented 8 years ago

id 用「年份+开播月份+index」可以,但这样的话最好是字符串,比如 "1610-1" 之类的,万一哪天业界起死回生了一季度上百部番(

标题、官网、放送时间这些 meta 可能确实是放在外层比较好,放在里面感觉变「特例」了。

标题翻译为数组的话,应该要保证流传最广的那个翻译作为第 0 个元素。

对于站点,各种类型分开目前看是可以,但其他类型直接一个 otherSites 感觉不太好。如果以后收录的站点多起来了全塞里面吗?如果为某个类型的站点新增一个字段以何为根据呢?watchSites 和 downloadSites 独立为字段只是因为 bgmlist.com 要用到吧。

我觉得,数据部分独立为一个项目后,应当与业务逻辑无关,要考虑的是数据结构怎样才能方便人工维护、方便程序读取。你的结构限定了先查询站点类型,再查询站点名称。仅仅作为例子,假设所有站点都可能有 comments 字段,而我要获取含有 comments 字段的那些站点,那我要先把所有类型的站点合并到一个数组里,然后再去 filter,这样就比较麻烦了。我更倾向于数组也是这个理由,把 "tudou" 作为 key 是因为绝大多数情况下我们都以站点名作为查询条件,但总会有其他情况。

wxt2005 commented 8 years ago

站点链接用数组存储我没有什么意见。但是对于所有站点都混杂地放在一起,用 type 值来区分这点,我觉得在维护的时候会造成一些麻烦。各种类型的链接穿插在一起,肉眼分辨起来是很困难的。毕竟现在维护的时候,大部分情况下还是要靠人工的。

yume-chan commented 8 years ago

标题的问题,现在提出的方案需要默认名称是日文,如果以后需要收录国产或欧美动画呢?可以改成和 translation 一样的模式

站点,我也赞同全部放在一起,用 type 区分的方式。

weizhenye commented 8 years ago

在上面我有提到,测试部分可以检验字段顺序。或者我们直接写个 format.js,自动修正为规定的格式和顺序,要求提交 commit 前必须 npm run format 一下。这样,如果是添加新站点,直接填进去,npm run format 后就不用管了;如果是修改已有站点,就根据规定好的顺序去找,这个操作复杂度和分成多个字段是一样的。

或许 titleTrans 里应该加上 ja 字段?不懂 translation 是什么模式。

另外放送时间,我想了想还是应该使用 ISO 8601 格式,new Date('2015-01-09 23:00:00 UTC+8').toISOString() -> 2015-01-09T15:00:00.000Z,毕竟是人工维护的,可读性强一点比较好。

wxt2005 commented 8 years ago

meta data 里的 title 就确定为作品的原名,如果是国产作品就为中文。这种情况下,如果有日文译名,则在 titleTrans 里增加 jp 字段好了。

那么现在决定的格式大致上是这样:

[
    {
        "id": "1607-1",
        "title": "アイドルマスター シンデレラガールズ",
        "officialSite": "http://imas-cinderella.com/",
        "begin": "2015-01-09T15:00:00.000Z",
        "end": "2015-01-09T15:00:00.000Z",
        "comments": "注释",
        "titleTrans": {
            "en-US": [
                "THE IDOLM@STER CINDERELLA GIRLS"
            ],
            "zh-Hans": [
                "偶像大师 灰姑娘女孩",
                "爱马仕 灰姑娘"
            ],
            "zh-Hant": [
                "偶像大師 灰姑娘女孩"
            ]
        },
        "sites": [
            {
                "type": "onair",
                "name": "tudou",
                "url": "http://www.tudou.com/albumcover/qgdKuHlPg9g.html",
                "official": true,
                "premuiumOnly": true,
                "begin": 1420821000000,
                "censored": true,
                "lang": "zh",
                "comments": "注释"
            },   
            {
                "type": "onair",
                "name": "bilibili",
                "url": "http://bangumi.bilibili.com/anime/5057/",
                "official": true,
                "premuiumOnly": false,
                "begin": 1420821000000,
                "censored": false,
                "lang": "zh",
                "comments": ""
            }, 
            {
                "type": "resource",
                "name": "dmhy",
                "keyword": "娘女孩"
            }
            {
                "type": "info",
                "name": "saraba1st",
                "id": "1059367"
            },
            {
                "type": "info",
                "name": "bangumi",
                "id": "12345"
            }
        ]
    }
]
yume-chan commented 8 years ago

如果我想显示所有项目的中文名,该如何区分 1) 项目本来就是中文所以翻译列表里没有中文 和 2) 项目名暂时还没有中文翻译所以翻译列表里没有中文 ?

JSON 的 key 一般用全小写 + 下划线分割单词

sites 里的主要字段统一命名为 value,怎么理解是之后的事

weizhenye commented 8 years ago

或许顶层得再加个 lang 字段指明作品语言,但感觉这样怪怪的。

JSON 的 key 我看到的是驼峰式的比较多,JS 在取值时出现下划线还是比较难受的。Google JSON Style Guide(中译)

sites 里的主要字段是指什么?

wxt2005 commented 8 years ago

那可能真的得加个字段 lang 来指明作品的语言了,这样也方便筛选各种语言的番组。 sites 里的字段这样看起来确实有点乱,但是全用 Value 似乎也不太好,是不是需要有个文档来说明各个站点的规则?毕竟也不是所有 info 类的站点都可以用 id 来表示。或者干脆全部用 url

weizhenye commented 8 years ago

info 类或许还能细分一下,可能可以加的站点有 aniDB、MyAnimeList、豆瓣、贴吧、维基(title 直接拼接?)。当然,这些是等有人能 PR 再考虑。

每种类型的站点有哪些字段肯定要有文档说明的。

主要字段可能是统一比较好,然后想了想该统一为 id 还是 url。

其实 id 肯定是有的,同一站点不同番组 url 中变化的部分就可以看作 id;对于某一番组而言,id 一般也是不会变的,我知道变过链接格式的有 AcFun、Bilibili、乐视、PPTV,只有 PPTV 是改了 id 的,但原 id 还是能用,拼接回去的 url 会跳转到新地址。

用 url 的话,如果链接格式改变就要改变所有番组的 url,但使用者就不用改了;用 id 的话,链接格式改变与本项目无关,需要变的是使用者,他要根据新的格式拼回去。上述的「使用者」是假定要使用 url(可能是大多数情况),如果「使用者」的目标是 id(比如我),那 url 就没什么好处了。

一些比较:

讲道理这时候应该优先考虑兼容性选择 url,但作为少数派的挣扎,我提个方案希望不会被打死:

在顶层加一个 urlTemplate 字段,指明某一站点的 url 模板。

{
  "urlTemplate": {
    "tudou": {
      "before": "http://www.tudou.com/albumcover/",
      "after": ".html"
    },
    // or
    "tudou": ["http://www.tudou.com/albumcover/", ".html"]
  },
  "sites": [
    {
      "type": "onair",
      "name": "tudou",
      "id": "uGqEOjd4Wd4"
    }
  ]
}

这样要 url 的有固定方法拼接不会有兼容问题,要 id 的不用另行为每一个站点写正则匹配。只是长得有点丑?

wxt2005 commented 8 years ago

如果用这种方案的话,就干脆把站点的 meta data 分出来维护好了。 url 规则就用类似模板的方式去写。

{
    "sites": {
        "tudou": {
            "name": "土豆",
            "type": "onair",
            "urlTemplate": "http://www.tudou.com/albumcover/{{id}}.html"
        },
         "saraba1st": {
            "name": "S1",
            "type": "info",
            "urlTemplate": "http://bbs.saraba1st.com/2b/thread-{{id}}-1-1.html"
        },
        "dmhy": {
            "name": "动漫花园",
            "type": "resource",
            "urlTemplate": "https://share.dmhy.org/topics/list?keyword={{keyword}}"
        }
    },
    "bangumis": [
        // ...
    ]
}
weizhenye commented 8 years ago

这么一说才发现 type 也算冗余,以后有没有可能出现一个站点有两种类型的情况?比如 S1 的讨论楼 type 为 forum,投票楼为 vote 之类的。

bangumis 字段指的是「番组们」而不是「Bangumi.tv 的数据」吧?罗马字加英文 s 实在是比较少见,况且与 Bangumi.tv 有歧义。不如直接命名为 sitessitesMeta

冗余和效率总是难以两全,type 作为最常用的字段,是否应该优先效率?

data.sites.filter(site => site.type === 'onair');
// vs
data.sites.filter(site => data.sitesMeta[site.name].type === 'onair');

不过感觉后者也能接受。

sites 中的 name 是来自域名的代号,sitesMeta 中的 name 是官方表达的名称,是否应该换个字段名防止歧义?

resource 类的主要字段用 keyword 不是又没有统一了么。像 acg.rip 使用的是数字 ID,应当统一为 id

模板的字符串替换怕一些奇葩的 url,不过应该不会有。另外主要字段统一为 id 后,{{id}} 中的 id 就不必须了,替换部分只要是个唯一的字符串就行了,替换更方便。

wxt2005 commented 8 years ago
weizhenye commented 8 years ago

如果主要字段只用作拼接 url 的话,那没什么问题;如果其他部分也要用到的话,还要先从 urlTemplate 里获取得知哪个字段是主要字段才行。现在暂时也想不出有什么其他用途。

另外腾讯视频的 url 有点特殊,http://v.qq.com/detail/w/wgzklo5zkrgalda.html 中 id 前面会有一个字符,与 id 的第一位相同,这个该怎么处理?直接 w/wgzklo5zkrgalda 作为 id 吗? 还有优酷,http://www.youku.com/show_page/id_z1e13314abec311e38b3f.html 的 id 部分似乎总是以 z 开头的,实际上在优酷的各种 API 中也是以 z 后面的那部分作为 id 的,应该要和官方保持一致吧?

wxt2005 commented 8 years ago

虽然麻烦了点,不过想写正则替换的时候应该还是会瞄一眼字段里有啥字段名的吧。 或者用类似下面这样的函数,就不用去关心字段名是什么了。

urlTemplate.replace(/{{(\w+)}}/g, (match, key) => data.key);

id 规则特殊情况特殊处理。 腾讯视频的话,确实看起来比较难受,但也只能把变化的部分作为 id 了,用 w/wgzklo5zkrgalda 这部份。 优酷也是同样,既然变化的只有 z 之后的部分,而且官方 api 也有规定,那就遵序这个规则好了。

weizhenye commented 8 years ago

先 repo 建起来文档写起来?

GPBeta commented 8 years ago

目前 bangumi-list 有相关 API 么?

wxt2005 commented 8 years ago

@weizhenye @CnSimonChan 总之先建了个 repo: https://github.com/bangumi-data/bangumi-data

@GPBeta 不好意思,暂时还没有哦

weizhenye commented 8 years ago

给 bangumi-data 加个 gitter 或 Slack 的 badge 吧,简单的讨论就不必 issue 了。