chore: 导入清理前的完整工程状态(含 Capacitor 原生壳,作为回滚备份点)
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 巡检 packages/data/recipe.csv 中的 B 站视频是否失效,输出 Markdown 报告
|
||||
*
|
||||
* pnpm check:videos # 打印报告
|
||||
* pnpm check:videos --out report.md # 同时写入文件(供 workflow 更新 issue)
|
||||
*/
|
||||
import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { setTimeout as sleep } from 'node:timers/promises'
|
||||
import { bilibiliViewApi, classifyBilibiliCode } from '../packages/cook/src/utils/bilibili'
|
||||
import { recipeCsvFile } from '../packages/cook/src/utils/config'
|
||||
import { parseRecipeCsv } from '../packages/cook/src/utils/csv'
|
||||
|
||||
const DELAY_MS = 400
|
||||
|
||||
async function check(id: string) {
|
||||
try {
|
||||
const res = await fetch(bilibiliViewApi(id), {
|
||||
headers: { 'User-Agent': 'Mozilla/5.0 (cook recipe link checker; +https://github.com/YunYouJun/cook)' },
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
})
|
||||
const body = await res.json() as { code: number, message?: string }
|
||||
return { status: classifyBilibiliCode(body.code), detail: `${body.code} ${body.message ?? ''}`.trim() }
|
||||
}
|
||||
catch (e) {
|
||||
return { status: 'unknown' as const, detail: (e as Error).message }
|
||||
}
|
||||
}
|
||||
|
||||
const recipes = parseRecipeCsv(fs.readFileSync(recipeCsvFile, 'utf-8'))
|
||||
const byVideo = new Map<string, { line: number, name: string }[]>()
|
||||
recipes.forEach((recipe, i) => {
|
||||
if (!recipe.bv)
|
||||
return
|
||||
const list = byVideo.get(recipe.bv) ?? []
|
||||
list.push({ line: i + 2, name: recipe.name })
|
||||
byVideo.set(recipe.bv, list)
|
||||
})
|
||||
|
||||
const dead: string[] = []
|
||||
let unknown = 0
|
||||
for (const [id, uses] of byVideo) {
|
||||
const result = await check(id)
|
||||
if (result.status === 'dead') {
|
||||
for (const use of uses)
|
||||
dead.push(`| ${use.line} | ${use.name} | [${id}](https://www.bilibili.com/video/${id}) | ${result.detail} |`)
|
||||
}
|
||||
else if (result.status === 'unknown') {
|
||||
unknown++
|
||||
}
|
||||
await sleep(DELAY_MS)
|
||||
}
|
||||
|
||||
const report = [
|
||||
`共检查 ${byVideo.size} 个视频,失效 ${dead.length} 条菜谱,无法确认 ${unknown} 个(接口限流或超时,下次巡检会重试)。`,
|
||||
'',
|
||||
...(dead.length
|
||||
? ['| 行 | 菜名 | 视频 | 接口返回 |', '| --- | --- | --- | --- |', ...dead]
|
||||
: ['没有发现失效视频。']),
|
||||
'',
|
||||
`_由 \`pnpm check:videos\` 生成于 ${new Date().toISOString().slice(0, 10)}。替换视频号或删除菜谱后,下一次巡检会自动更新此列表。_`,
|
||||
].join('\n')
|
||||
|
||||
console.log(report)
|
||||
const outIndex = process.argv.indexOf('--out')
|
||||
if (outIndex > -1 && process.argv[outIndex + 1])
|
||||
fs.writeFileSync(process.argv[outIndex + 1]!, report)
|
||||
Reference in New Issue
Block a user