更新日志

2022/08/12

  1. 修改卡片样式
  2. 移除“总集数”和“描述”
  3. 添加“标签”

2022/07/24

  1. 修复与pjax的兼容问题

2022/07/15

  1. 修正追番列表JSON文件生成为空的问题
  2. 在自动生成JSON文件的基础上保留命令生成的功能

2022/07/13

  1. 修正追番列表编号错乱的问题
  2. 生成JSON文件从命令手动生成改为自动生成

2022/06/09

  1. 实现基本功能

介绍

  本篇博文中的script代码主要从hexo-bilibili-bangumi插件中CV而来,本人进行了一些细节上的修改,其余代码为本人自行编写。

  想要预览前端页面可以点击这里进行查看。

  目前前端实现了以下功能:

  1. 切换页面后支持使用浏览器的回退功能进行返回(会留下比较多的历史记录)
  2. 通过JS控制页面显示
  3. 将番剧列表信息存储在JSON中而不是HTML内
  4. 自动修正URL的错误参数(不会删掉多余的参数)
  5. 没有提供链接的番剧不再跳转到404页面
  6. 根据浏览器支持情况自动选择是否使用webp格式的图片

用法

  在运行hexo ghexo s时会自动在根目录生成bilibili.json文件(如果文件存在会跳过生成),但是需要注意的是,频繁生成文件会导致访问被拒绝,所以建议本地调试时先使用指令生成一个bilibili.json文件。

指令

  1. hexo bangumi -u | 生成JSON文件
  2. hexo bangumi -d | 删除JSON文件

创建页面

  执行命令(名字自己随意,根据喜好修改):

1
hexo new page "这里写页面URL名称(%s)"

  打开[hexo]/source/%s/index.md,在两个---之间添加type: "bangumis",修改完之后你的index.md应该与下面的样例类似:

1
2
3
4
5
6
7
8
---
title: 追 番 列 表
date: 2022-06-22 19:02:32
type: "bangumis"
---

<!-- 这里可以写你要加入的东西,会出现在列表的上方 -->

配置文件

  修改配置文件(hexo或主题的配置文件均可):

1
2
3
4
5
6
7
+ bilibili:
+ # 是否启用
+ enable: true
+ # 你的B站 vmid
+ vmid: ...
+ # 拓展JSON的文件名(/source/_data/*.json)
+ extra: 'extra_bangumis'

vmid

  打开你的B站个人主页,链接格式应该如下:

1
https://space.bilibili.com/***?spm_id_from=...

  其中的***就是你的vmid

拓展文件

  拓展文件是用于手动向追番列表中添加B站没有的番剧,JSON格式完全兼容hexo-bangumi-bilibili插件的格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"watchedExtra": [
{
"title": "库特wafter",
"type": "番剧",
"area": "日本",
"cover": "https://pic.rmb.bdstatic.com/bjh/4f1d277c4dcc90673e36115d4d673ae1.jpeg",
"id": 0,
"follow": "-",
"view": "-",
"danmaku": "-",
"coin": "-",
"score": "-",
"tags": ["爱情", "青春"],
"index": 0
}
]
}

描述

  key值可以为(区分大小写):

  • watchedExtra/watched: 已看
  • watchingExtra/watching: 在看
  • wantWatchExtra/wantWatch: 想看

  属性值描述:

  • title- 番剧名称
  • type- 类型
  • area- 区域
  • cover- 封面
  • id- 番剧地址,没有地址请填0,非B站地址请填写完整地址(如:"https://kmar.top/"
  • follow- 追番人数
  • view- 播放数量
  • danmaku- 弹幕数量
  • coin- 硬币数量
  • score- 评分
  • tags- 标签
  • index- 定位编号,不填写默认为0

定位编号

  定位编号是为了确定该番剧卡片所在的位置,在生成JSON的时候,代码会自动给从B站获取到的番剧从0开始编号。

  显示时会按照定位编号从大到小进行排序,拓展信息中定位编号填写n表示将该卡片放置在定位编号为n的卡片的上面。

  在页面中,查看指定卡片的HTML代码,就能看到一个名为index的属性,其值就是其本身的定位编号。

教程

本教程基于 Butterfly 主题编写,其它主题的用户请根据实际情况修改代码

  修改[butterfly]\layout\page.pug

1
2
3
4
5
6
7
8
9
10
11
  case page.type
when 'tags'
include includes/page/tags.pug
when 'link'
include includes/page/flink.pug
when 'categories'
include includes/page/categories.pug
+ when 'bangumis'
+ include includes/page/bangumi.pug
default
include includes/page/default-page.pug

  新建[butterfly]\layout\includes\page\bangumi.pug

我是把JS直接写到HTML里面了,要是不想的话也可以自己挪出去,挪出去的话开启PJAX的用户记得打上no-pjax的标签。

注意:如果你的博客开启了pjax,请务必使用pjax兼容的方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
link(rel="preconnect" href="//i0.hdslb.com/")

!=page.content

#bangumis
.bangumi-tabs#bangumi-top
button#wantWatch 想看
button#watching 在看
button#watched 已看

div#inner

.bangumi-tabs#bangumi-bottom
button#bottom-first 首页
button#bottom-pre 上一页
p#bottom-num 0/0
button#bottom-next 下一页
button#bottom-end 尾页

script.
document.addEventListener('DOMContentLoaded', function fun() {
const isSupportWebp = (function () {
try {
return document.createElement('canvas').toDataURL('image/webp', 0.5).indexOf('data:image/webp') === 0;
} catch (err) {
return false;
}
})()
// 单页卡片数量限制
const maxCount = 10

/** 追加文本 */
function appendText(element, text, clean = false) {
text = `(${text})`
if (navigator.userAgent.indexOf('Firefox') < 0)
element.innerText = clean ? text : element.innerText + text
else
element.textContent = clean ? text : element.textContent + text
}

/** 处理参数 */
function parseArg() {
const url = location.href
let arg
if (url.endsWith('/')) {
arg = {id: 'watching', page: 1}
} else {
arg = JSON.parse(decodeURIComponent(location.hash.substring(1)))
// 校对参数
if (!arg.id || (arg.id !== 'watching' &&
arg.id !== 'wantWatch' && arg.id !== 'watched'))
arg.id = 'watching'
if (!arg.page || arg.page < 1) arg.page = 1
}
sessionStorage.setItem('bangumis', JSON.stringify(arg))
return arg
}

/** 初始化页码信息 */
function initPageNumber(json) {
appendText(document.getElementById('wantWatch'), json.wantWatch.length)
appendText(document.getElementById('watching'), json.watching.length)
appendText(document.getElementById('watched'), json.watched.length)
}

/** 更新列表内容 */
function update(json, arg, updateURL = true) {
const content = json[arg.id]
const maxPage = Math.ceil(content.length / maxCount)
if (arg.page > maxPage) arg.page = maxPage

const pre = document.getElementById('bottom-pre').classList
const next = document.getElementById('bottom-next').classList
if (arg.page === 1) pre.add('disable')
else pre.remove('disable')
if (arg.page === maxPage) next.add('disable')
else next.remove('disable')

const pageNum = document.getElementById('bottom-num')
appendText(pageNum, `${arg.page} / ${maxPage}`, true)

function buildCard(title, img, href, follow, type, area, play, coin, danmaku, score, tagList, index) {
if (!img.startsWith('http')) img = `https://i0.hdslb.com/bfs/bangumi/${img}${isSupportWebp ? '@220w_280h.webp' : ''}`
let tags = ''
for (let name of tagList) tags += `<p>${name}</p>`
// noinspection HtmlUnknownAttribute,HtmlRequiredAltAttribute
return `<div class="card" link="${href}" index="${index}"><img src="${img}" referrerpolicy="no-referrer"><div class="info"><a class="title">${title}</a><div class="details"><span class="area"><p>${type}</p><em>${area}</em></span><span class="play"><p>播放量</p><em>${play}</em></span><span class="follow"><p>追番</p><em>${follow}</em></span><span class="coin"><p>硬币</p><em>${coin}</em></span><span class="danmaku"><p>弹幕</p><em>${danmaku}</em></span><span class="score"><p>评分</p><em>${score}</em></span></div><div class="tags">${tags}</div></div></div>`
}

let newHTML = ''
console.log(content)
for (let i = (arg.page - 1) * maxCount, k = 0;
k !== maxCount && i !== content.length; ++k, ++i) {
const value = content[i]
const href = value.id === 0 ? '' : (typeof value.id !== 'number' ? value.id : `https://www.bilibili.com/bangumi/media/md${value.id}/`)
newHTML += buildCard(value.title, value.cover, href ?? 0,
value.follow ?? '-', value.type ?? '番剧', value.area ?? '日本', value.view ?? '-',
value.coin ?? '-', value.danmaku ?? '-', value.score ?? '-', value.tags ?? [], value.index ?? 0)
}
document.getElementById('inner').innerHTML = newHTML

if (updateURL) location.hash = JSON.stringify(arg)
for (let value of document.getElementById('bangumi-top').children) {
if (value.id === arg.id) value.classList.add('active')
else value.classList.remove('active')
}
}

/** 注册点击事件 */
function initClick(json, arg) {
const top = document.getElementById('bangumi-top')
top.addEventListener('click', event => {
const element = event.target.id ? event.target : event.target.parentNode
if (element.nodeName !== 'BUTTON') return
element.classList.add('active')
for (let value of top.children) {
if (value.id !== element.id) value.classList.remove('active')
}
arg.id = element.id
arg.page = 1
sessionStorage.setItem('bangumis', JSON.stringify(arg))
update(json, arg)
})
const bottom = document.getElementById('bangumi-bottom')
const height = document.getElementById('page-header').clientHeight
bottom.addEventListener('click', event => {
const element = event.target.id ? event.target : event.target.parentNode
if (element.nodeName !== 'BUTTON' || element.classList.contains('disable')) return
btf.scrollToDest(height)
switch (element.id) {
case 'bottom-first':
arg.page = 1
break
case 'bottom-end':
arg.page = Math.ceil(json[arg.id].length / maxCount)
break
case 'bottom-next':
++arg.page
break
case 'bottom-pre':
--arg.page
break
}
update(json, arg)
})
const card = document.getElementById('inner')
card.addEventListener('click', event => {
let element = event.target.id ? event.target : event.target.parentNode
if (!element.classList.contains('descr')) {
while (!element.classList.contains('card')) element = element.parentElement
const link = element.getAttribute('link')
if (link.length > 0) window.open(link)
else btf.snackbarShow('博主没有为这个番剧设置链接~')
}
})
}

new Promise(resolve => resolve(parseArg())).then(arg => {
fetch('/bilibili.json').then(it => it.text().then(it => {
const json = JSON.parse(it)
const hashchangeTask = () => {
const newArg = parseArg()
if (newArg !== arg) {
arg.id = newArg.id
arg.page = newArg.page
update(json, arg, false)
}
}
addEventListener('hashchange', hashchangeTask)
document.addEventListener('pjax:send', function fun() {
window.removeEventListener('hashchange', hashchangeTask)
document.removeEventListener('pjax:send', fun)
})
addEventListener('beforeunload', function fun() {
window.removeEventListener('hashchange', hashchangeTask)
window.removeEventListener('beforeunload', fun)
})
initClick(json, arg)
initPageNumber(json)
update(json, arg, false)
}))
})
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
link(rel="preconnect" href="//i0.hdslb.com/")

!=page.content

#bangumis
.bangumi-tabs#bangumi-top
button#wantWatch 想看
button#watching 在看
button#watched 已看

div#inner

.bangumi-tabs#bangumi-bottom
button#bottom-first 首页
button#bottom-pre 上一页
p#bottom-num 0/0
button#bottom-next 下一页
button#bottom-end 尾页

script.
document.addEventListener('kms:loaded', function fun() {
document.removeEventListener('kms:loaded', fun)

const isSupportWebp = (function () {
try {
return document.createElement('canvas').toDataURL('image/webp', 0.5).indexOf('data:image/webp') === 0;
} catch (err) {
return false;
}
})()
// 单页卡片数量限制
const maxCount = 10

/** 追加文本 */
function appendText(element, text, clean = false) {
text = `(${text})`
if (navigator.userAgent.indexOf('Firefox') < 0)
element.innerText = clean ? text : element.innerText + text
else
element.textContent = clean ? text : element.textContent + text
}

/** 处理参数 */
function parseArg() {
const url = location.href
let arg
if (url.endsWith('/')) {
arg = {id: 'watching', page: 1}
} else {
arg = JSON.parse(decodeURIComponent(location.hash.substring(1)))
// 校对参数
if (!arg.id || (arg.id !== 'watching' &&
arg.id !== 'wantWatch' && arg.id !== 'watched'))
arg.id = 'watching'
if (!arg.page || arg.page < 1) arg.page = 1
}
sessionStorage.setItem('bangumis', JSON.stringify(arg))
return arg
}

/** 初始化页码信息 */
function initPageNumber(json) {
appendText(document.getElementById('wantWatch'), json.wantWatch.length)
appendText(document.getElementById('watching'), json.watching.length)
appendText(document.getElementById('watched'), json.watched.length)
}

/** 更新列表内容 */
function update(json, arg, updateURL = true) {
const content = json[arg.id]
const maxPage = Math.ceil(content.length / maxCount)
if (arg.page > maxPage) arg.page = maxPage

const pre = document.getElementById('bottom-pre').classList
const next = document.getElementById('bottom-next').classList
if (arg.page === 1) pre.add('disable')
else pre.remove('disable')
if (arg.page === maxPage) next.add('disable')
else next.remove('disable')

const pageNum = document.getElementById('bottom-num')
appendText(pageNum, `${arg.page} / ${maxPage}`, true)

function buildCard(title, img, href, follow, type, area, play, coin, danmaku, score, tagList, index) {
if (!img.startsWith('http')) img = `https://i0.hdslb.com/bfs/bangumi/${img}${isSupportWebp ? '@220w_280h.webp' : ''}`
let tags = ''
for (let name of tagList) tags += `<p>${name}</p>`
// noinspection HtmlUnknownAttribute,HtmlRequiredAltAttribute
return `<div class="card" link="${href}" index="${index}"><img src="${img}" referrerpolicy="no-referrer"><div class="info"><a class="title">${title}</a><div class="details"><span class="area"><p>${type}</p><em>${area}</em></span><span class="play"><p>播放量</p><em>${play}</em></span><span class="follow"><p>追番</p><em>${follow}</em></span><span class="coin"><p>硬币</p><em>${coin}</em></span><span class="danmaku"><p>弹幕</p><em>${danmaku}</em></span><span class="score"><p>评分</p><em>${score}</em></span></div><div class="tags">${tags}</div></div></div>`
}

let newHTML = ''
console.log(content)
for (let i = (arg.page - 1) * maxCount, k = 0;
k !== maxCount && i !== content.length; ++k, ++i) {
const value = content[i]
const href = value.id === 0 ? '' : (typeof value.id !== 'number' ? value.id : `https://www.bilibili.com/bangumi/media/md${value.id}/`)
newHTML += buildCard(value.title, value.cover, href ?? 0,
value.follow ?? '-', value.type ?? '番剧', value.area ?? '日本', value.view ?? '-',
value.coin ?? '-', value.danmaku ?? '-', value.score ?? '-', value.tags ?? [], value.index ?? 0)
}
document.getElementById('inner').innerHTML = newHTML

if (updateURL) location.hash = JSON.stringify(arg)
for (let value of document.getElementById('bangumi-top').children) {
if (value.id === arg.id) value.classList.add('active')
else value.classList.remove('active')
}
}

/** 注册点击事件 */
function initClick(json, arg) {
const top = document.getElementById('bangumi-top')
top.addEventListener('click', event => {
const element = event.target.id ? event.target : event.target.parentNode
if (element.nodeName !== 'BUTTON') return
element.classList.add('active')
for (let value of top.children) {
if (value.id !== element.id) value.classList.remove('active')
}
arg.id = element.id
arg.page = 1
sessionStorage.setItem('bangumis', JSON.stringify(arg))
update(json, arg)
})
const bottom = document.getElementById('bangumi-bottom')
const height = document.getElementById('page-header').clientHeight
bottom.addEventListener('click', event => {
const element = event.target.id ? event.target : event.target.parentNode
if (element.nodeName !== 'BUTTON' || element.classList.contains('disable')) return
btf.scrollToDest(height)
switch (element.id) {
case 'bottom-first':
arg.page = 1
break
case 'bottom-end':
arg.page = Math.ceil(json[arg.id].length / maxCount)
break
case 'bottom-next':
++arg.page
break
case 'bottom-pre':
--arg.page
break
}
update(json, arg)
})
const card = document.getElementById('inner')
card.addEventListener('click', event => {
let element = event.target.id ? event.target : event.target.parentNode
if (!element.classList.contains('descr')) {
while (!element.classList.contains('card')) element = element.parentElement
const link = element.getAttribute('link')
if (link.length > 0) window.open(link)
else btf.snackbarShow('博主没有为这个番剧设置链接~')
}
})
}

new Promise(resolve => resolve(parseArg())).then(arg => {
fetch('/bilibili.json').then(it => it.text().then(it => {
const json = JSON.parse(it)
const hashchangeTask = () => {
const newArg = parseArg()
if (newArg !== arg) {
arg.id = newArg.id
arg.page = newArg.page
update(json, arg, false)
}
}
addEventListener('hashchange', hashchangeTask)
document.addEventListener('pjax:send', function fun() {
window.removeEventListener('hashchange', hashchangeTask)
document.removeEventListener('pjax:send', fun)
})
addEventListener('beforeunload', function fun() {
window.removeEventListener('hashchange', hashchangeTask)
window.removeEventListener('beforeunload', fun)
})
initClick(json, arg)
initPageNumber(json)
update(json, arg, false)
}))
})
})

  修改[butterfly]\source\css\index.styl

1
2
3
4
5
6
7
  @import '_mode/*'
+ @import '_custom/bangumis'

// search
if hexo-config('algolia_search.enable')
@import '_search/index'
@import '_search/algolia'

  新建[butterfly]\source\css\_custom\bangumis.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
:root
--km-card-bg #66ccff34
--km-link-bg #0084ff
--km-button-light-active #66ccff
--km-general-shadow #546e7a
--km-globle-font black

[data-theme="dark"]
--km-card-bg #66ccff10
--km-link-bg #0056ac
--km-button-light-active #4c4c4cbc
--km-globle-font white

#bangumis
.bangumi-tabs
display block
border-radius 15px
border 2px solid var(--km-card-bg)
text-align center

& > p
display inline-block
margin-bottom 0
margin-top 0

button
display inline-block
color var(--font-color)
padding 5px
margin 5px
border-radius 5px
transition all .2s

&:not(.active):not(.disable):hover
color white
background-color var(--km-link-bg)

&.active
background-color var(--km-button-light-active)

&.disable
color #90a4ae

.card
display block
margin-top 10px
margin-bottom 10px
padding-left 10px
padding-right 10px
white-space nowrap
transition all .2s
border-radius 8px
border 1px #66ccff99 dashed
text-align center

[data-theme="light"] &
border-color dodgerblue

img
display inline-block
width 110px
border-radius 10px
vertical-align middle
transition all .2s

&:hover
box-shadow 1px 2px 4px var(--km-general-shadow)

.info
display inline-block
margin-left 10px
vertical-align middle
width calc(100% - 120px)

.title
font-size 20px
border-radius 5px
color #49b1f5
white-space normal
word-break break-word

&:hover
padding 2px
color var(--km-globle-font)
background-color var(--km-link-bg)

.details
display block
overflow-x overlay
padding-bottom 6px

span
display inline-block
border-right 1px solid #2fd8d8
text-align center
margin 5px
vertical-align middle
padding 5px
padding-top 0
padding-right 10px
height 50px

p, em
color #2fd8d8
font-size 12px
display block
line-height 12px

em
font-weight bold

.total p
padding-top 12px

.score
border-right unset

.tags
overflow-x overlay

p
display inline-block
border 2px #66ccff solid
border-radius 16px
padding 0 8px
margin-right 10px
transition .2s

&:hover
background #55bbeeaa

&:hover
background-color var(--km-card-bg)

[data-theme="light"]
#bangumis
.title
color #99a9bf !important

&:hover
color white !important

.details
p, em
color black !important

  执行命令:npm install axios --save

  新建[butterfly]\scripts\customs\bili.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
'use strict'

const logger = require('hexo-log')()
const axios = require('axios')
const fs = require('hexo-fs')
const {source_dir} = require("hexo/lib/hexo/default_config")

hexo.extend.generator.register('bangumi', async () => {
if (fs.existsSync(`${source_dir}/bilibili.json`)) {
logger.info(`"${source_dir}/bilibili.json"已存在,跳过JSON生成`)
} else {
const json = await buildJson()
return {
path: '/bilibili.json',
data: JSON.stringify(json)
}
}
})

const options = {
options: [
{ name: '-u, --update', desc: 'Update data' },
{ name: '-d, --delete', desc: 'Delete data' }
]
}

hexo.extend.console.register('bangumi', '番剧JSON文件的相关操作', options, async args => {
const path = `${source_dir}/bilibili.json`
if (args.u) {
const json = await buildJson()
fs.writeFile(path, JSON.stringify(json))
logger.info("成功生成JSON文件")
} else if (args.d) {
if (fs.existsSync(path)) {
fs.deleteFile(path)
logger.info("成功删除JSON文件")
}
}
})

async function buildJson() {
const config = hexo.config.bilibili || hexo.theme.config.bilibili
if (!(config && config.enable)) return
const readJson = async (json, list) => {
for (let element of json) list.push(element)
}
const data = {
vmid: config.vmid,
extra: config.extra || 'extra_bangumis'
}
const wantWatch = await getBiliJson(data.vmid, 1)
const watching = await getBiliJson(data.vmid, 2)
const watched = await getBiliJson(data.vmid, 3)
const extraPath = `${source_dir}/_data/${data.extra}.json`
if (fs.existsSync(extraPath)) {
const extra = JSON.parse(fs.readFileSync(extraPath))
for (let key in extra) {
switch (key) {
case 'watchedExtra':
case 'watched':
await readJson(extra[key], watched)
break
case 'watchingExtra':
case 'watching':
await readJson(extra[key], watching)
break
case 'wantWatchExtra':
case 'wantWatch':
await readJson(extra[key], wantWatch)
break
}
}
}
const info = {
wantWatch: mergeSort(wantWatch),
watching: mergeSort(watching),
watched: mergeSort(watched)
}
const sum = info.watching.length + info.watched.length + info.wantWatch.length
logger.info(`wantWatch(${info.wantWatch.length}) + watching(${info.watching.length}) + watched(${info.watched.length}) = ${sum}`)
return info
}

// eslint-disable-next-line no-nested-ternary
const count = (e) => (e ? (e > 10000 && e < 100000000 ? `${(e / 10000).toFixed(1)} 万` : e > 100000000 ? `${(e / 100000000).toFixed(1)} 亿` : e) : '-')

const getDataPage = async (vmid, status) => {
const response = await axios.get(`https://api.bilibili.com/x/space/bangumi/follow/list?type=1&follow_status=${status}&vmid=${vmid}&ps=1&pn=1`)
if (response?.data?.code === 0 && response?.data?.message === '0' && response?.data?.data && typeof response?.data?.data?.total !== 'undefined') {
return { success: true, data: Math.ceil(response.data.data.total / 30) + 1 }
} else if (response && response.data && response.data.message !== '0') {
return { success: false, data: response.data.message }
} else if (response && response.data) {
return { success: false, data: response.data }
}
return { success: false, data: response }
}
// kmar edit point
const getData = async (vmid, status, pn) => {
const response = await axios.get(`https://api.bilibili.com/x/space/bangumi/follow/list?type=1&follow_status=${status}&vmid=${vmid}&ps=30&pn=${pn}`)
const $data = []
if (response?.data?.code === 0) {
const data = response?.data?.data
const list = data?.list || []

for (const bangumi of list) {
let cover = bangumi?.cover
if (cover) {
const href = new URL(cover)
href.protocol = 'https'
cover = href.href
if (cover.startsWith('https://i0.hdslb.com/bfs/bangumi/')) {
cover = cover.substring(33)
}
}
$data.push({
title: bangumi?.title,
type: bangumi?.season_type_name,
area: bangumi?.areas?.[0]?.name,
cover,
id: bangumi?.media_id,
follow: count(bangumi?.stat?.follow),
view: count(bangumi?.stat?.view),
danmaku: count(bangumi?.stat?.danmaku),
coin: count(bangumi.stat.coin),
score: bangumi?.rating?.score ?? '-',
tags: bangumi?.styles
})
}
return $data
}
}

async function getBiliJson(vmid, status) {
const page = await getDataPage(vmid, status)
if (page?.success) {
const list = []
// eslint-disable-next-line no-plusplus
for (let i = 1; i < page.data; i++) {
const data = await getData(vmid, status, i)
list.push(...data)
}
for (let i = 0; i < list.length; i++)
list[i].index = list.length - i
return list
}
return []
}

function mergeSort(array) {
function merge(left, right) {
let arr = []
while (left.length && right.length) {
arr.push(left[0].index > right[0].index ? left.shift() : right.shift())
}
return [ ...arr, ...left, ...right ]
}
const half = array.length / 2
if (array.length < 2) return array
const left = array.splice(0, half)
return merge(mergeSort(left), mergeSort(array))
}

创作不易,扫描下方打赏二维码支持一下吧ヾ(≧▽≦*)o