mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 16:19:18 +00:00
commit
c68a0b32f1
257
apps/tools.js
257
apps/tools.js
@ -23,7 +23,7 @@ import {
|
||||
SUMMARY_PROMPT,
|
||||
transMap,
|
||||
TWITTER_BEARER_TOKEN,
|
||||
XHS_NO_WATERMARK_HEADER,
|
||||
XHS_NO_WATERMARK_HEADER
|
||||
} from "../constants/constant.js";
|
||||
import {
|
||||
ANIME_SERIES_SEARCH_LINK,
|
||||
@ -123,7 +123,7 @@ export class tools extends plugin {
|
||||
priority: 300,
|
||||
rule: [
|
||||
{
|
||||
reg: `^(翻|trans)[${ tools.Constants.existsTransKey }]`,
|
||||
reg: `^(翻|trans)[${tools.Constants.existsTransKey}]`,
|
||||
fnc: "trans",
|
||||
},
|
||||
{
|
||||
@ -214,7 +214,18 @@ export class tools extends plugin {
|
||||
{
|
||||
reg: '^#验车(.*?)',
|
||||
fnc: 'yc'
|
||||
}
|
||||
},
|
||||
{
|
||||
reg: "^#(网易云登录状态|网易状态|网易云状态)$",
|
||||
fnc: "neteaseStatus",
|
||||
permission: 'master',
|
||||
},
|
||||
{
|
||||
reg: "^#(网易云扫码登录|网易扫码登录|网易登录|网易云登录)$",
|
||||
fnc: 'netease_scan',
|
||||
permission: 'master',
|
||||
},
|
||||
|
||||
],
|
||||
});
|
||||
// 配置文件
|
||||
@ -226,7 +237,7 @@ export class tools extends plugin {
|
||||
// 魔法接口
|
||||
this.proxyAddr = this.toolsConfig.proxyAddr;
|
||||
this.proxyPort = this.toolsConfig.proxyPort;
|
||||
this.myProxy = `http://${ this.proxyAddr }:${ this.proxyPort }`;
|
||||
this.myProxy = `http://${this.proxyAddr}:${this.proxyPort}`;
|
||||
// 加载识别前缀
|
||||
this.identifyPrefix = this.toolsConfig.identifyPrefix;
|
||||
// 加载直播录制时长
|
||||
@ -251,6 +262,14 @@ export class tools extends plugin {
|
||||
this.biliUseBBDown = this.toolsConfig.biliUseBBDown;
|
||||
// 加载 BBDown 的CDN配置
|
||||
this.biliCDN = this.toolsConfig.biliCDN;
|
||||
// 加载网易云Cookie
|
||||
this.neteaseCookie = this.toolsConfig.neteaseCookie
|
||||
// 加载是否自建服务器
|
||||
this.useLocalNeteaseAPI = this.toolsConfig.useLocalNeteaseAPI
|
||||
// 加载自建服务器API
|
||||
this.neteaseCloudAPIServer = this.toolsConfig.neteaseCloudAPIServer
|
||||
// 加载网易云解析最高音质
|
||||
this.neteaseCloudAudioQuality = this.toolsConfig.neteaseCloudAudioQuality
|
||||
// 加载哔哩哔哩是否使用Aria2
|
||||
this.biliDownloadMethod = this.toolsConfig.biliDownloadMethod;
|
||||
// 加载哔哩哔哩最高分辨率
|
||||
@ -1361,6 +1380,148 @@ export class tools extends plugin {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 网易云登录状态
|
||||
async neteaseStatus(e, reck) {
|
||||
// 优先判断是否使用自建 API
|
||||
let autoSelectNeteaseApi
|
||||
// 判断海外
|
||||
const isOversea = await this.isOverseasServer();
|
||||
if (this.useLocalNeteaseAPI) {
|
||||
// 使用自建 API
|
||||
autoSelectNeteaseApi = this.neteaseCloudAPIServer
|
||||
} else {
|
||||
// 自动选择 API
|
||||
autoSelectNeteaseApi = isOversea ? NETEASE_SONG_DOWNLOAD : NETEASE_API_CN;
|
||||
}
|
||||
const statusUrl = autoSelectNeteaseApi + '/login/status'
|
||||
axios.get(statusUrl, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
"Cookie": reck ? reck : this.neteaseCookie
|
||||
},
|
||||
}).then(res => {
|
||||
// logger.info('登录状态', res.data)
|
||||
const userInfo = res.data.data.profile
|
||||
if (userInfo) {
|
||||
axios.get(`${autoSelectNeteaseApi}/vip/info?uid=${userInfo.userId}`, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
"Cookie": reck ? reck : this.neteaseCookie
|
||||
},
|
||||
}).then(res => {
|
||||
// logger.info('vip信息', res.data)
|
||||
const vipInfo = res.data.data
|
||||
const checkVipStatus = (vipLevel, expireTime, nickname, avatarUrl, e) => {
|
||||
const expireDate = new Date(expireTime);
|
||||
if (expireDate > Date.now()) {
|
||||
e.reply([segment.image(`${avatarUrl}?param=170y170`), `网易云已登录:\n${nickname}\n会员等级:\n${vipLevel}\n会员到期时间:\n${expireDate.toLocaleString()}`]);
|
||||
} else {
|
||||
return false; // 返回 false 以继续检查下一个 VIP 状态
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (vipInfo.redplus.vipCode != 0 && checkVipStatus(`SVIP${vipInfo.redplus.vipLevel}\n最高解析音质:\n jymaster(超清母带)`, vipInfo.redplus.expireTime, userInfo.nickname, userInfo.avatarUrl, e)) {
|
||||
// SVIP 有效,不执行后续逻辑
|
||||
} else if (vipInfo.associator.vipCode != 0 && checkVipStatus(`VIP${vipInfo.associator.vipLevel}\n最高解析音质:\n jyeffect(高清环绕音)`, vipInfo.associator.expireTime, userInfo.nickname, userInfo.avatarUrl, e)) {
|
||||
// VIP 有效,不执行后续逻辑
|
||||
} else {
|
||||
// 如果都已过期,发送 VIP 已过期信息
|
||||
e.reply([segment.image(`${userInfo.avatarUrl}?param=170y170`), `网易云已登录:\n${userInfo.nickname}\n会员等级:\nVIP已过期\n最高解析音质:\nstandard(标准)`]);
|
||||
}
|
||||
})
|
||||
// e.reply([segment.image(userInfo.avatarUrl), `网易云已登录:${userInfo.nickname}\n会员等级:\n`,segment.image(vipInfo.iconUrl)]);
|
||||
} else {
|
||||
e.reply('暂未登录,请发#网易云扫码登陆进行登陆绑定ck')
|
||||
}
|
||||
})
|
||||
}
|
||||
// 网易扫码登录
|
||||
async netease_scan(e) {
|
||||
// 优先判断是否使用自建 API
|
||||
let autoSelectNeteaseApi
|
||||
// 判断海外
|
||||
const isOversea = await this.isOverseasServer();
|
||||
if (this.useLocalNeteaseAPI) {
|
||||
// 使用自建 API
|
||||
autoSelectNeteaseApi = this.neteaseCloudAPIServer
|
||||
} else {
|
||||
// 自动选择 API
|
||||
autoSelectNeteaseApi = isOversea ? NETEASE_SONG_DOWNLOAD : NETEASE_API_CN;
|
||||
}
|
||||
// 获取登录key
|
||||
let keyUrl = autoSelectNeteaseApi + '/login/qr/key'
|
||||
axios.get(keyUrl, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT
|
||||
},
|
||||
}).then(res => {
|
||||
// 获取登录二维码
|
||||
const unikey = res.data.data.unikey
|
||||
let url = autoSelectNeteaseApi + '/login/qr/create?key=' + unikey + "&qrimg=true"
|
||||
axios.get(url, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
},
|
||||
}).then(res => {
|
||||
e.reply([segment.image(res.data.data.qrimg), '请在30秒内使用网易云APP进行扫码']);
|
||||
})
|
||||
|
||||
//最大轮询次数 5s/check
|
||||
let pollCount = 0; // 轮询计数器
|
||||
const maxPolls = 6; // 最大轮询次数
|
||||
|
||||
const pollRequest = async () => {
|
||||
let pollUrl = autoSelectNeteaseApi + '/login/qr/check?key=' + unikey + '×tamp=' + Date.now();
|
||||
try {
|
||||
const res = await axios.get(pollUrl, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
},
|
||||
});
|
||||
// logger.info('轮询状态', res.data);
|
||||
if (res.data.code == '800') {
|
||||
e.reply("二维码过期,请重新获取");
|
||||
clearInterval(intervalId);
|
||||
return;
|
||||
}
|
||||
if (res.data.code == '803') {
|
||||
const regex = /music_u=([^;]+)/i; // 使用 'i' 进行不区分大小写匹配
|
||||
const match = res.data.cookie.match(regex);
|
||||
if (match) {
|
||||
try {
|
||||
await config.updateField("tools", "neteaseCookie", match[0]);
|
||||
// logger.info('ck------', match[0]);
|
||||
this.neteaseStatus(e, match[0])
|
||||
} catch (error) {
|
||||
logger.error('更新ck时出错:', error);
|
||||
e.reply('更新ck时出错,请稍后重试');
|
||||
return;
|
||||
}
|
||||
}
|
||||
e.reply(`扫码登录成功,ck已自动保存`);
|
||||
clearInterval(intervalId);
|
||||
return;
|
||||
}
|
||||
|
||||
pollCount++;
|
||||
|
||||
} catch (error) {
|
||||
logger.error('轮询过程中出错:', error);
|
||||
clearInterval(intervalId);
|
||||
e.reply('轮询过程中发生错误,请稍后再试');
|
||||
}
|
||||
|
||||
if (pollCount > maxPolls) {
|
||||
clearInterval(intervalId); // 停止轮询
|
||||
logger.info('超时轮询已停止');
|
||||
e.reply('扫码超时,请重新获取');
|
||||
}
|
||||
}
|
||||
const intervalId = setInterval(pollRequest, 5000);
|
||||
})
|
||||
}
|
||||
|
||||
// 网易云解析
|
||||
async netease(e) {
|
||||
let message =
|
||||
@ -1386,10 +1547,34 @@ export class tools extends plugin {
|
||||
logger.error("[R插件][网易云解析] 没有找到id,无法进行下一步!")
|
||||
return
|
||||
}
|
||||
// 优先判断是否使用自建 API
|
||||
let autoSelectNeteaseApi
|
||||
// 判断海外
|
||||
const isOversea = await this.isOverseasServer();
|
||||
if (this.useLocalNeteaseAPI) {
|
||||
// 使用自建 API
|
||||
autoSelectNeteaseApi = this.neteaseCloudAPIServer
|
||||
} else {
|
||||
// 自动选择 API
|
||||
const autoSelectNeteaseApi = isOversea ? NETEASE_SONG_DOWNLOAD : NETEASE_API_CN;
|
||||
autoSelectNeteaseApi = isOversea ? NETEASE_SONG_DOWNLOAD : NETEASE_API_CN;
|
||||
}
|
||||
// 检测ck可用性
|
||||
const statusUrl = autoSelectNeteaseApi + '/login/status'
|
||||
const isCkExpired = await axios.get(statusUrl, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
"Cookie": this.neteaseCookie
|
||||
},
|
||||
}).then(res => {
|
||||
const userInfo = res.data.data.profile
|
||||
if (userInfo) {
|
||||
logger.info('ck活着,使用ck进行高音质下载')
|
||||
return true
|
||||
} else {
|
||||
logger.info('ck失效,将启用临时接口下载')
|
||||
return false
|
||||
}
|
||||
})
|
||||
// mv截断
|
||||
if (message.includes("mv")) {
|
||||
const AUTO_NETEASE_MV_DETAIL = autoSelectNeteaseApi + "/mv/detail?mvid={}";
|
||||
@ -1400,50 +1585,88 @@ export class tools extends plugin {
|
||||
axios.get(AUTO_NETEASE_MV_DETAIL.replace("{}", id), {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
"Cookie": this.neteaseCookie
|
||||
}
|
||||
}),
|
||||
axios.get(AUTO_NETEASE_MV_URL.replace("{}", id), {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
"Cookie": this.neteaseCookie
|
||||
}
|
||||
})
|
||||
]);
|
||||
const { name: mvName, artistName: mvArtist, cover: mvCover } = mvDetailData.data?.data;
|
||||
e.reply([segment.image(mvCover), `${ this.identifyPrefix }识别:网易云MV,${ mvName } - ${ mvArtist }`]);
|
||||
e.reply([segment.image(mvCover), `${this.identifyPrefix}识别:网易云MV,${mvName} - ${mvArtist}`]);
|
||||
// logger.info(mvUrlData.data)
|
||||
const { url: mvUrl } = mvUrlData.data?.data;
|
||||
this.downloadVideo(mvUrl).then(path => {
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 国内解决方案,替换为国内API (其中,NETEASE_API_CN是国内基址)
|
||||
const AUTO_NETEASE_SONG_DOWNLOAD = autoSelectNeteaseApi + "/song/url?id={}";
|
||||
const AUTO_NETEASE_SONG_DOWNLOAD = autoSelectNeteaseApi + "/song/url/v1?id={}&level=" + this.neteaseCloudAudioQuality;
|
||||
const AUTO_NETEASE_SONG_DETAIL = autoSelectNeteaseApi + "/song/detail?ids={}";
|
||||
// logger.info(AUTO_NETEASE_SONG_DOWNLOAD.replace("{}", id));
|
||||
const downloadUrl = AUTO_NETEASE_SONG_DOWNLOAD.replace("{}", id)
|
||||
const detailUrl = AUTO_NETEASE_SONG_DETAIL.replace("{}", id)
|
||||
// 请求netease数据
|
||||
axios.get(AUTO_NETEASE_SONG_DOWNLOAD.replace("{}", id), {
|
||||
axios.get(downloadUrl, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
"Cookie": this.neteaseCookie
|
||||
},
|
||||
}).then(async resp => {
|
||||
// 国内解决方案,替换API后这里也需要修改
|
||||
|
||||
// 英转中字典匹配
|
||||
const translationDict = {
|
||||
'standard': '标准',
|
||||
'higher': '较高',
|
||||
'exhigh': '极高',
|
||||
'lossless': '无损',
|
||||
'hires': 'Hi-Res',
|
||||
'jyeffect': '高清环绕声',
|
||||
'sky': '沉浸环绕声',
|
||||
'dolby': '杜比全景声',
|
||||
'jymaster': '超清母带'
|
||||
};
|
||||
|
||||
// 英转中
|
||||
function translateToChinese(word) {
|
||||
return translationDict[word] || word; // 如果找不到对应翻译,返回原词
|
||||
}
|
||||
|
||||
// 字节转MB
|
||||
function bytesToMB(sizeInBytes) {
|
||||
const sizeInMB = sizeInBytes / (1024 * 1024); // 1 MB = 1024 * 1024 bytes
|
||||
return sizeInMB.toFixed(2); // 保留两位小数
|
||||
}
|
||||
|
||||
let url = await resp.data.data?.[0]?.url || null;
|
||||
const AudioLevel = translateToChinese(resp.data.data?.[0]?.level)
|
||||
const AudioSize = bytesToMB(resp.data.data?.[0]?.size)
|
||||
// 获取歌曲信息
|
||||
let title = await axios.get(AUTO_NETEASE_SONG_DETAIL.replace("{}", id)).then(res => {
|
||||
let title = await axios.get(detailUrl).then(res => {
|
||||
// logger.info('歌曲详情---', res.data.songs[0])
|
||||
const song = res.data.songs[0];
|
||||
return cleanFilename(`${ song?.name }-${ song?.ar?.[0].name }`);
|
||||
return cleanFilename(`${song?.name}-${song?.ar?.[0].name}`);
|
||||
});
|
||||
// 一般这个情况是VIP歌曲 (如果没有url或者是国内, 国内全走临时接口,后续如果不要删除逻辑'!isOversea ||')
|
||||
if (!isOversea || url == null) {
|
||||
// 获取歌曲封面
|
||||
let coverUrl = await axios.get(detailUrl).then(res => {
|
||||
const song = res.data.songs[0];
|
||||
return song?.al?.picUrl
|
||||
});
|
||||
// 一般这个情况是VIP歌曲 (如果没有url或者是国内,没有ck的走临时接口)
|
||||
if (!isCkExpired || url == null) {
|
||||
url = await this.musicTempApi(e, title, "网易云音乐");
|
||||
} else {
|
||||
// 不是VIP歌曲,直接识别完就下一步
|
||||
e.reply(`${ this.identifyPrefix }识别:网易云音乐,${ title }`);
|
||||
// 拥有ck,并且有效,直接进行解析
|
||||
e.reply([segment.image(coverUrl), `${this.identifyPrefix}识别:网易云音乐,${title}\n当前下载音质: ${AudioLevel}\n预估大小: ${AudioSize}MB`]);
|
||||
}
|
||||
// 动态判断后缀名
|
||||
const extensionPattern = /\.([a-zA-Z0-9]+)$/;
|
||||
const musicExt = url.match(extensionPattern)?.[0].replace("\.", "");
|
||||
let musicExt = url.match(extensionPattern)?.[0].replace("\.", "");
|
||||
// 下载音乐
|
||||
downloadAudio(url, this.getCurDownloadPath(e), title, 'follow', musicExt).then(async path => {
|
||||
// 发送语音
|
||||
@ -1453,7 +1676,7 @@ export class tools extends plugin {
|
||||
// 删除文件
|
||||
await checkAndRemoveFile(path);
|
||||
}).catch(err => {
|
||||
logger.error(`下载音乐失败,错误信息为: ${ err.message }`);
|
||||
logger.error(`下载音乐失败,错误信息为: ${err}`);
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
@ -22,6 +22,11 @@ biliCDN: 0 # 哔哩哔哩 CDN,默认为0表示不使用
|
||||
biliDownloadMethod: 0 # 哔哩哔哩的下载方式:0默认使用原生稳定的下载方式,如果你在乎内存可以使用轻量的wget和axel下载方式,如果在乎性能可以使用Aria2下载
|
||||
biliResolution: 1 # 哔哩哔哩的下载画质,0为原画,1为清晰画,2为流畅画(默认为0)
|
||||
|
||||
useLocalNeteaseAPI: false #是否使用网易云解析自建API
|
||||
neteaseCookie: '' #网易云ck
|
||||
neteaseCloudAPIServer: '' #网易云自建服务器地址
|
||||
neteaseCloudAudioQuality: exhigh #网易云解析最高音质 默认exhigh(极高) 分类:standard => 标准,higher => 较高, exhigh=>极高, lossless=>无损, hires=>Hi-Res, jyeffect => 高清环绕声, sky => 沉浸环绕声, dolby => 杜比全景声, jymaster => 超清母带
|
||||
|
||||
YouTubeGraphicsOptions: 720 #YouTobe的下载画质,0为原画,1080,720,480,自定义画面高度(默认为720)
|
||||
|
||||
douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;passport_fe_beating_status=xxx;sid_guard=xxx;uid_tt=xxx;uid_tt_ss=xxx;sid_tt=xxx;sessionid=xxx;sessionid_ss=xxx;sid_ucp_v1=xxx;ssid_ucp_v1=xxx;passport_assist_user=xxx;ttwid=xxx;
|
||||
|
@ -193,6 +193,18 @@ export const YOUTUBE_GRAPHICS_LIST = Object.freeze([
|
||||
{ label: '720P 高清', value: 720 },
|
||||
{ label: '480P 清晰', value: 480 },
|
||||
]);
|
||||
|
||||
export const NETEASECLOUD_QUALITY_LIST = Object.freeze([
|
||||
{ label: '标准', value: 'standard' },
|
||||
{ label: '较高', value: 'higher' },
|
||||
{ label: '极高', value: 'exhigh' },
|
||||
{ label: '无损', value: 'lossless' },
|
||||
{ label: 'Hi-Res', value: 'hires' },
|
||||
{ label: '高清环绕声', value: 'jyeffect' },
|
||||
{ label: '沉浸环绕声', value: 'sky' },
|
||||
{ label: '杜比全景声', value: 'dolby' },
|
||||
{ label: '超清母带', value: 'jymaster' },
|
||||
]);
|
||||
/**
|
||||
* 消息撤回时间
|
||||
* @type {number}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import _ from "lodash";
|
||||
import path from "path";
|
||||
import { BILI_CDN_SELECT_LIST, BILI_DOWNLOAD_METHOD, BILI_RESOLUTION_LIST, YOUTUBE_GRAPHICS_LIST } from "./constants/constant.js";
|
||||
import { BILI_CDN_SELECT_LIST, BILI_DOWNLOAD_METHOD, BILI_RESOLUTION_LIST, YOUTUBE_GRAPHICS_LIST, NETEASECLOUD_QUALITY_LIST } from "./constants/constant.js";
|
||||
import model from "./model/config.js";
|
||||
|
||||
const pluginName = `rconsole-plugin`;
|
||||
@ -230,6 +230,46 @@ export function supportGuoba() {
|
||||
options: YOUTUBE_GRAPHICS_LIST,
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "tools.useLocalNeteaseAPI",
|
||||
label: "使用自建网易云API",
|
||||
bottomHelpMessage:
|
||||
"默认不开启,有条件可以查看https://gitlab.com/Binaryify/neteasecloudmusicapi进行搭建",
|
||||
component: "Switch",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
field: "tools.neteaseCloudAPIServer",
|
||||
label: "自建网易云API地址",
|
||||
bottomHelpMessage:
|
||||
"填入自建API地址,例:http://xxxxxxxx",
|
||||
component: "Input",
|
||||
required: false,
|
||||
componentProps: {
|
||||
placeholder: "填入自建API地址",
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "tools.neteaseCookie",
|
||||
label: "网易云Cookie",
|
||||
bottomHelpMessage:
|
||||
"可以发送 #网易云扫码登陆 快捷获取 或 者在网易云官网自己获取",
|
||||
component: "Input",
|
||||
required: false,
|
||||
componentProps: {
|
||||
placeholder: "使用vip账号登陆获取更高音质解析",
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "tools.neteaseCloudAudioQuality",
|
||||
label: "网易云解析最高音质",
|
||||
bottomHelpMessage:
|
||||
"网易云解析最高音质(需vip账号ck!!! 默认极高,更高请根据登陆的账号和服务器承载能力进行选择)",
|
||||
component: "Select",
|
||||
componentProps: {
|
||||
options: NETEASECLOUD_QUALITY_LIST,
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "tools.douyinCookie",
|
||||
label: "抖音的Cookie",
|
||||
|
Loading…
x
Reference in New Issue
Block a user