1 parent
7f25f1546f
commit
4fd7ccf412
7 files changed
+57
-19
No files matched your search
@@ -304,7 +304,7 @@ LogVar 弹幕 API 服务器
|
||||
> cf部署可能不稳定,推荐用vercel/netlify部署。
|
||||
|
||||
## API食用指南
|
||||
支持 forward/senplayer/hills/小幻/yamby/eplayerx/afusekt/uz影视/dscloud/lenna/danmaku-anywhere/omnibox/ChaiChaiEmbyTV/moontv/capyplayer/kerkerker 等支持弹幕API的播放器。
|
||||
支持 forward/senplayer/hills/小幻/yamby/eplayerx/afusekt/uz影视/dscloud/lenna/danmaku-anywhere/omnibox/ChaiChaiEmbyTV/moontv/capyplayer/kerkerker/LinPlayer 等支持弹幕API的播放器。
|
||||
|
||||
配合 dd-danmaku 扩展新增对 Emby Web 端弹幕的支持,具体使用方法参考 [PR #98](https://github.com/huangxd-/danmu_api/pull/98) 。
|
||||
|
||||
@@ -385,7 +385,8 @@ API 支持返回 Bilibili 标准 XML 格式的弹幕数据,通过查询参数
|
||||
| CONVERT_TOP_BOTTOM_TO_SCROLL | 【可选】是否将顶部和底部弹幕转换为浮动弹幕,默认为`false`(不转换),启用后顶部弹幕(ct=5)和底部弹幕(ct=4)会被转换为浮动弹幕(ct=1),可选值:`true`、`false` |
|
||||
| CONVERT_COLOR | 【可选】弹幕转换颜色配置,默认为`default`(不转换),`white` 将所有非白色的弹幕颜色转换为纯白色,`color` 将所有白色弹幕转换为随机颜色(包含白色),可选值:`default`、`white`、`color` |
|
||||
| DANMU_OUTPUT_FORMAT | 【可选】弹幕输出格式,默认为`json`,可选值:`json`(JSON格式)、`xml`(XML格式),支持通过查询参数`?format=xml`或`?format=json`覆盖此设置,优先级:查询参数 > 环境变量 > 默认值 |
|
||||
| DANMU_SIMPLIFIED | 【可选】是否将繁体弹幕转换为简体,目前只对巴哈姆特生效,默认为`true`(转换),可选值:`false`(不转换) |
|
||||
| DANMU_SIMPLIFIED | 【可选】是否将繁体弹幕转换为简体,目前只对巴哈姆特生效,默认为`true`(转换),可选值:`false`(不转换),优先级:DANMU_SIMPLIFIED < DANMU_TRADITIONAL |
|
||||
| DANMU_TRADITIONAL | 【可选】是否将简体弹幕转换为繁体,对所有源生效,默认为`false`(不转换),可选值:`true`(转换),优先级:DANMU_TRADITIONAL > DANMU_SIMPLIFIED |
|
||||
| PROXY_URL | 【可选】代理/反代地址,目前只对巴哈姆特、TMDB API、bilibili生效,支持格式:<br> 正常代理:`http://127.0.0.1:7890` <br> 万能反代:`@http://127.0.0.1` <br> 特定反代:`源字段@http://127.0.0.1`,目前支持的字段有:`bahamut,tmdb,bilibili`(bilibili字段会启用阿b的港澳台番剧的搜索与获取)<br> 混合配置/示例:`http://你的代理地址:28233,bahamut@你的巴哈反代地址,tmdb@你的tmdb反代地址,@你的万能反代地址` <br> 优先级:特定反代 > 万能反代 > 正常代理,高优先级覆盖低优先级使用。 <br> (注意:如果巴哈姆特请求不通,会拖慢搜索返回速度,如需使用bahamut源请在SOURCE_ORDER环境变量中手动添加`bahamut`)如果你使用docker部署并且访问不了bahamut源,请配置代理地址或者反代([Netlify反代教程](https://github.com/wan0ge/bahamut-api-proxy));vercel/netlify/cf中理应都自然能联通,不用填写 |
|
||||
| TMDB_API_KEY | 【可选】TMDB API Key地址,目前只对巴哈姆特生效,配置后并行从TMDB获取日语原名搜索巴哈(如果TMDB条目类型不是动画或制作地区不是jp则不会进行巴哈搜索)可以解决巴哈译名不同导致的搜索无结果问题,例如大陆常用译名`间谍过家家`在巴哈译名为`間諜家家酒`,正常搜索无法搜索到,配置后可以解决这一问题但会稍微影响请求速度,[TMDBAPI](https://www.themoviedb.org/settings/api)获取方法参考:[TMDB API Key申请 - 绿联NAS私有云](https://www.ugnas.com/tutorial-detail/id-226.html) |
|
||||
| RATE_LIMIT_MAX_REQUESTS | 【可选】限流配置:1分钟内同一IP最大请求次数,默认为`3`,设置为`0`表示不限流 |
|
||||
@@ -583,6 +584,8 @@ API 支持返回 Bilibili 标准 XML 格式的弹幕数据,通过查询参数
|
||||
- 弹幕分片下载请求已加入重试机制,重试次数为1次
|
||||
- 如果同时配置了本地缓存和redis缓存,则以redis缓存优先
|
||||
- 弹弹play源用的是第三方接口,感谢开源项目 [danmaku-anywhere](https://github.com/Mr-Quin/danmaku-anywhere)
|
||||
- 西瓜视频源因为部分调用了douyin接口,所以在国外环境部署的情况下会获取不到弹幕
|
||||
- 有任何问题,如部署/环境变量配置等,可通过deepwiki对本项目进行提问,链接入口:https://deepwiki.com/huangxd-/danmu_api,其中项目内容一般每周刷新一次
|
||||
|
||||
### 关联项目
|
||||
[喂饭教程1:danmu_api vercel 自动同步部署方案 - 永远保持最新版本!实时同步原作者更新](https://github.com/xiaoyao20084321/log-var-danmu-deployment-guide)
|
||||
|
||||
+7
-1
@@ -138,10 +138,16 @@ CONVERT_COLOR=default
|
||||
|
||||
# 是否将繁体弹幕转换为简体,目前只对巴哈姆特源生效
|
||||
# 默认值:true(转换)
|
||||
# 说明:禁用后,不进行巴哈弹幕繁转简
|
||||
# 说明:禁用后,不进行巴哈弹幕繁转简,优先级:DANMU_SIMPLIFIED < DANMU_TRADITIONAL
|
||||
# 可选值:true, false
|
||||
DANMU_SIMPLIFIED=true
|
||||
|
||||
# 是否将简体弹幕全部转换为繁体,对所有源生效
|
||||
# 默认值:false(不转换)
|
||||
# 说明:优先级:DANMU_TRADITIONAL > DANMU_SIMPLIFIED
|
||||
# 可选值:true, false
|
||||
DANMU_TRADITIONAL=false
|
||||
|
||||
# ==================== 弹幕推送配置 ====================
|
||||
|
||||
# 弹幕推送地址
|
||||
|
||||
@@ -280,7 +280,7 @@ export class Envs {
|
||||
'VOD_REQUEST_TIMEOUT': { category: 'source', type: 'number', description: 'VOD请求超时时间,默认10000', min: 5000, max: 30000 },
|
||||
'BILIBILI_COOKIE': { category: 'source', type: 'text', description: 'B站Cookie' },
|
||||
'YOUKU_CONCURRENCY': { category: 'source', type: 'number', description: '优酷并发配置,默认8', min: 1, max: 16 },
|
||||
'MERGE_SOURCE_PAIRS': { category: 'source', type: 'text', description: '源合并配置,配置后将对应源合并同时一起获取弹幕返回,支持多源链式合并,第一个为主源。\n格式:源1&源2&源3,多组用逗号分隔。\n示例:dandan&animeko&bahamut, bilibili&animeko\n目前允许的源:tencent,youku,iqiyi,imgo,bilibili,sohu,leshi,xigua,renren,hanjutv,bahamut,dandan,animeko' },
|
||||
'MERGE_SOURCE_PAIRS': { category: 'source', type: 'text', description: '源合并配置,配置后将对应源合并同时一起获取弹幕返回,支持多源链式合并,第一个为主源。\n格式:源1&源2&源3,多组用逗号分隔。\n示例:dandan&animeko&bahamut, bilibili&animeko\n目前允许的源:tencent,youku,iqiyi,imgo,bilibili,sohu,leshi,xigua,renren,hanjutv,bahamut,dandan,animeko' },
|
||||
|
||||
// 匹配配置
|
||||
'PLATFORM_ORDER': { category: 'match', type: 'multi-select', options: this.ALLOWED_PLATFORMS, description: '平台排序配置' },
|
||||
@@ -294,7 +294,8 @@ export class Envs {
|
||||
'BLOCKED_WORDS': { category: 'danmu', type: 'text', description: '屏蔽词列表' },
|
||||
'GROUP_MINUTE': { category: 'danmu', type: 'number', description: '分钟内合并去重(0表示不去重),默认1', min: 0, max: 30 },
|
||||
'DANMU_LIMIT': { category: 'danmu', type: 'number', description: '弹幕数量限制,单位为k,即千:默认 0,表示不限制弹幕数', min: 0, max: 100 },
|
||||
'DANMU_SIMPLIFIED': { category: 'danmu', type: 'boolean', description: '弹幕繁体转简体开关' },
|
||||
'DANMU_SIMPLIFIED': { category: 'danmu', type: 'boolean', description: '弹幕繁体转简体开关,优先级:DANMU_SIMPLIFIED < DANMU_TRADITIONAL,默认值为true' },
|
||||
'DANMU_TRADITIONAL': { category: 'danmu', type: 'boolean', description: '弹幕简体转繁体开关,优先级:DANMU_TRADITIONAL > DANMU_SIMPLIFIED,默认值为false' },
|
||||
'CONVERT_TOP_BOTTOM_TO_SCROLL': { category: 'danmu', type: 'boolean', description: '顶部/底部弹幕转换为浮动弹幕' },
|
||||
'CONVERT_COLOR': { category: 'danmu', type: 'select', options: ['default', 'white', 'color'], description: '弹幕转换颜色配置' },
|
||||
'DANMU_OUTPUT_FORMAT': { category: 'danmu', type: 'select', options: ['json', 'xml'], description: '弹幕输出格式,默认json' },
|
||||
@@ -331,14 +332,15 @@ export class Envs {
|
||||
vodRequestTimeout: this.get('VOD_REQUEST_TIMEOUT', '10000', 'string'), // vod超时时间(默认10秒)
|
||||
bilibliCookie: this.get('BILIBILI_COOKIE', '', 'string', true), // b站cookie
|
||||
youkuConcurrency: Math.min(this.get('YOUKU_CONCURRENCY', 8, 'number'), 16), // 优酷并发配置
|
||||
mergeSourcePairs: this.resolveMergeSourcePairs(), // 源合并配置,用于将源合并获取
|
||||
mergeSourcePairs: this.resolveMergeSourcePairs(), // 源合并配置,用于将源合并获取
|
||||
platformOrderArr: this.resolvePlatformOrder(), // 自动匹配优选平台
|
||||
episodeTitleFilter: this.resolveEpisodeTitleFilter(), // 剧集标题正则过滤
|
||||
blockedWords: this.get('BLOCKED_WORDS', '', 'string'), // 屏蔽词列表
|
||||
groupMinute: Math.min(this.get('GROUP_MINUTE', 1, 'number'), 30), // 分钟内合并去重(默认 1,最大值30,0表示不去重)
|
||||
danmuLimit: this.get('DANMU_LIMIT', 0, 'number'), // 等间隔采样限制弹幕总数,单位为k,即千:默认 0,表示不限制弹幕数,若改为5,弹幕总数在超过5000的情况下会将弹幕数控制在5000
|
||||
proxyUrl: this.get('PROXY_URL', '', 'string', true), // 代理/反代地址
|
||||
danmuSimplified: this.get('DANMU_SIMPLIFIED', true, 'boolean'), // 弹幕繁体转简体开关
|
||||
danmuSimplified: this.get('DANMU_SIMPLIFIED', true, 'boolean'), // 弹幕繁体转简体开关,优先级:DANMU_SIMPLIFIED < DANMU_TRADITIONAL,默认值为true
|
||||
danmuTraditional: this.get('DANMU_TRADITIONAL', false, 'boolean'), // 弹幕简体转繁体开关,优先级:DANMU_TRADITIONAL > DANMU_SIMPLIFIED,默认值为false
|
||||
danmuPushUrl: this.get('DANMU_PUSH_URL', '', 'string'), // 代理/反代地址
|
||||
tmdbApiKey: this.get('TMDB_API_KEY', '', 'string', true), // TMDB API KEY
|
||||
redisUrl: this.get('UPSTASH_REDIS_REST_URL', '', 'string', true), // upstash redis url
|
||||
|
||||
@@ -13,7 +13,7 @@ export const Globals = {
|
||||
accessedEnvVars: {},
|
||||
|
||||
// 静态常量
|
||||
VERSION: '1.13.0',
|
||||
VERSION: '1.13.1',
|
||||
MAX_LOGS: 500, // 日志存储,最多保存 500 行
|
||||
MAX_ANIMES: 100,
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { globals } from '../configs/globals.js';
|
||||
import { log } from './log-util.js'
|
||||
import { jsonResponse, xmlResponse } from "./http-util.js";
|
||||
import { traditionalized } from './zh-util.js';
|
||||
|
||||
// =====================
|
||||
// danmu处理相关函数
|
||||
@@ -300,6 +301,15 @@ export function convertToDanmakuJson(contents, platform) {
|
||||
}
|
||||
}
|
||||
|
||||
// 在最后一步转换弹幕文本为繁体字
|
||||
if (globals.danmuTraditional) {
|
||||
convertedDanmus = convertedDanmus.map(danmu => ({
|
||||
...danmu,
|
||||
m: traditionalized(danmu.m)
|
||||
}));
|
||||
log("info", `[danmu convert] 转换了 ${convertedDanmus.length} 条弹幕为繁体字`);
|
||||
}
|
||||
|
||||
log("info", `danmus_original: ${danmus.length}`);
|
||||
log("info", `danmus_filter: ${filteredDanmus.length}`);
|
||||
log("info", `danmus_group: ${groupedDanmus.length}`);
|
||||
|
||||
+27
-11
@@ -301,7 +301,7 @@ WidgetMetadata = {
|
||||
},
|
||||
{
|
||||
name: "danmuSimplified",
|
||||
title: "弹幕繁体转简体开关,目前只对巴哈姆特生效,默认true",
|
||||
title: "弹幕繁体转简体开关,目前只对巴哈姆特生效,默认true,优先级:DANMU_SIMPLIFIED < DANMU_TRADITIONAL",
|
||||
type: "input",
|
||||
placeholders: [
|
||||
{
|
||||
@@ -314,6 +314,21 @@ WidgetMetadata = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "danmuTraditional",
|
||||
title: "弹幕简体转繁体开关,对所有源生效,默认false,优先级:DANMU_TRADITIONAL > DANMU_SIMPLIFIED",
|
||||
type: "input",
|
||||
placeholders: [
|
||||
{
|
||||
title: "false",
|
||||
value: "false",
|
||||
},
|
||||
{
|
||||
title: "true",
|
||||
value: "true",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "convertTopBottomToScroll",
|
||||
title: "顶部/底部弹幕转换为浮动弹幕,默认false",
|
||||
@@ -434,7 +449,7 @@ if (typeof window !== 'undefined') {
|
||||
let globals;
|
||||
async function initGlobals(sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey) {
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey) {
|
||||
// 将传入的参数设置到环境变量中,以便Globals可以访问它们
|
||||
const env = {};
|
||||
|
||||
@@ -454,6 +469,7 @@ async function initGlobals(sourceOrder, otherServer, customSourceApiUrl, vodServ
|
||||
if (groupMinute !== undefined) env.GROUP_MINUTE = groupMinute;
|
||||
if (danmuLimit !== undefined) env.DANMU_LIMIT = danmuLimit;
|
||||
if (danmuSimplified !== undefined) env.DANMU_SIMPLIFIED = danmuSimplified;
|
||||
if (danmuTraditional !== undefined) env.DANMU_TRADITIONAL = danmuTraditional;
|
||||
if (convertTopBottomToScroll !== undefined) env.CONVERT_TOP_BOTTOM_TO_SCROLL = convertTopBottomToScroll;
|
||||
if (convertColor !== undefined) env.CONVERT_COLOR = convertColor;
|
||||
if (proxyUrl !== undefined) env.PROXY_URL = proxyUrl;
|
||||
@@ -524,11 +540,11 @@ const PREFIX_URL = "http://localhost:9321"
|
||||
async function searchDanmu(params) {
|
||||
const { tmdbId, type, title, season, link, videoUrl, sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
|
||||
await initGlobals(sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
|
||||
const response = await searchAnime(new URL(`${PREFIX_URL}/api/v2/search/anime?keyword=${title}`));
|
||||
const resJson = await response.json();
|
||||
@@ -597,11 +613,11 @@ async function searchDanmu(params) {
|
||||
async function getDetailById(params) {
|
||||
const { animeId, sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
|
||||
await initGlobals(sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
|
||||
const response = await getBangumi(`${PREFIX_URL}/api/v2/bangumi/${animeId}`);
|
||||
const resJson = await response.json();
|
||||
@@ -616,11 +632,11 @@ async function getDetailById(params) {
|
||||
async function getCommentsById(params) {
|
||||
const { commentId, link, videoUrl, season, episode, tmdbId, type, title, segmentTime, sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
|
||||
await initGlobals(sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
|
||||
if (commentId) {
|
||||
const storeKey = season && episode ? `${tmdbId}.${season}.${episode}` : `${tmdbId}`;
|
||||
@@ -637,7 +653,7 @@ async function getCommentsById(params) {
|
||||
if (lastCommentId === commentId && segmentList) {
|
||||
return await getDanmuWithSegmentTime({ segmentTime, tmdbId, season, episode, otherServer, customSourceApiUrl, vodServers, bilibiliCookie, sourceOrder, blockedWords, groupMinute,
|
||||
vodReturnMode, vodRequestTimeout, platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey })
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey })
|
||||
} else {
|
||||
Widget.storage.remove(storeKey);
|
||||
Widget.storage.remove(commentIdKey);
|
||||
@@ -663,11 +679,11 @@ async function getCommentsById(params) {
|
||||
async function getDanmuWithSegmentTime(params) {
|
||||
const { segmentTime, tmdbId, season, episode, sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey } = params;
|
||||
|
||||
await initGlobals(sourceOrder, otherServer, customSourceApiUrl, vodServers, vodReturnMode, vodRequestTimeout, bilibiliCookie,
|
||||
platformOrder, episodeTitleFilter, enableEpisodeFilter, strictTitleMatch, titleMappingTable, blockedWords, groupMinute,
|
||||
danmuLimit, danmuSimplified, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
danmuLimit, danmuSimplified, danmuTraditional, convertTopBottomToScroll, convertColor, proxyUrl, tmdbApiKey);
|
||||
|
||||
const storeKey = season && episode ? `${tmdbId}.${season}.${episode}` : `${tmdbId}`;
|
||||
const segmentList = Widget.storage.get(storeKey);
|
||||
|
||||
@@ -231,6 +231,7 @@ async function testNewFlow() {
|
||||
groupMinute: 1,
|
||||
danmuLimit: 0,
|
||||
danmuSimplified: false,
|
||||
danmuTraditional: false,
|
||||
convertTopBottomToScroll: false,
|
||||
convertColor: 'default',
|
||||
proxyUrl: '',
|
||||
|
||||
Reference in new issue
Block a user