mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 16:19:18 +00:00
commit
e5074ea4a5
331
apps/songRequest.js
Normal file
331
apps/songRequest.js
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { formatTime } from '../utils/other.js'
|
||||||
|
import puppeteer from "../../../lib/puppeteer/puppeteer.js";
|
||||||
|
import PickSongList from "../model/pick-song.js";
|
||||||
|
import { NETEASE_API_CN, NETEASE_SONG_DOWNLOAD, NETEASE_TEMP_API } from "../constants/tools.js";
|
||||||
|
import { COMMON_USER_AGENT, REDIS_YUNZAI_ISOVERSEA, REDIS_YUNZAI_SONGINFO } from "../constants/constant.js";
|
||||||
|
import { downloadAudio } from "../utils/common.js";
|
||||||
|
import { redisExistKey, redisGetKey, redisSetKey } from "../utils/redis-util.js";
|
||||||
|
import { checkAndRemoveFile } from "../utils/file.js";
|
||||||
|
import config from "../model/config.js";
|
||||||
|
|
||||||
|
export class songRequest extends plugin {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
name: "R插件点歌",
|
||||||
|
dsc: "实现快捷点歌",
|
||||||
|
priority: 300,
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
reg: '^#?点歌|#?听[1-9][0-9]*|#?听[1-9]*$',
|
||||||
|
fnc: 'pickSong'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: "^#播放(.*)",
|
||||||
|
fnc: "playSong"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
this.toolsConfig = config.getConfig("tools");
|
||||||
|
// 加载网易云Cookie
|
||||||
|
this.neteaseCookie = this.toolsConfig.neteaseCookie
|
||||||
|
// 加载是否自建服务器
|
||||||
|
this.useLocalNeteaseAPI = this.toolsConfig.useLocalNeteaseAPI
|
||||||
|
// 加载自建服务器API
|
||||||
|
this.neteaseCloudAPIServer = this.toolsConfig.neteaseCloudAPIServer
|
||||||
|
// 加载网易云解析最高音质
|
||||||
|
this.neteaseCloudAudioQuality = this.toolsConfig.neteaseCloudAudioQuality
|
||||||
|
// 加载识别前缀
|
||||||
|
this.identifyPrefix = this.toolsConfig.identifyPrefix;
|
||||||
|
// 加载是否开启网易云点歌功能
|
||||||
|
this.useNeteaseSongRequest = this.toolsConfig.useNeteaseSongRequest
|
||||||
|
// 加载点歌列表长度
|
||||||
|
this.songRequestMaxList = this.toolsConfig.songRequestMaxList
|
||||||
|
}
|
||||||
|
|
||||||
|
async pickSong(e) {
|
||||||
|
// 判断功能是否开启
|
||||||
|
if(!this.useNeteaseSongRequest) {
|
||||||
|
logger.info('当前未开启网易云点歌')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const autoSelectNeteaseApi = await this.pickApi()
|
||||||
|
// 只在群里可以使用
|
||||||
|
let group_id = e.group.group_id
|
||||||
|
if (!group_id) return
|
||||||
|
// 初始化
|
||||||
|
let songInfo = await redisGetKey(REDIS_YUNZAI_SONGINFO)
|
||||||
|
const saveId = songInfo.findIndex(item => item.group_id === e.group.group_id)
|
||||||
|
let musicDate = { 'group_id': group_id, data: [] }
|
||||||
|
// 获取搜索歌曲列表信息
|
||||||
|
let searchUrl = autoSelectNeteaseApi + '/search?keywords={}&limit='+ this.songRequestMaxList //搜索API
|
||||||
|
let detailUrl = autoSelectNeteaseApi + "/song/detail?ids={}" //歌曲详情API
|
||||||
|
if (e.msg.replace(/\s+/g, "").match(/点歌(.+)/)) {
|
||||||
|
const songKeyWord = e.msg.replace(/\s+/g, "").match(/点歌(.+)/)[1]
|
||||||
|
searchUrl = searchUrl.replace("{}", songKeyWord)
|
||||||
|
await axios.get(searchUrl, {
|
||||||
|
headers: {
|
||||||
|
"User-Agent": COMMON_USER_AGENT
|
||||||
|
},
|
||||||
|
}).then(async res => {
|
||||||
|
if (res.data.result.songs) {
|
||||||
|
for (const info of res.data.result.songs) {
|
||||||
|
musicDate.data.push({
|
||||||
|
'id': info.id,
|
||||||
|
'songName': info.name,
|
||||||
|
'singerName': info.artists[0]?.name,
|
||||||
|
'duration': formatTime(info.duration)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const ids = musicDate.data.map(item => item.id).join(',');
|
||||||
|
detailUrl = detailUrl.replace("{}", ids)
|
||||||
|
await axios.get(detailUrl, {
|
||||||
|
headers: {
|
||||||
|
"User-Agent": COMMON_USER_AGENT
|
||||||
|
},
|
||||||
|
}).then(res => {
|
||||||
|
for (let i = 0; i < res.data.songs.length; i++) {
|
||||||
|
musicDate.data[i].cover = res.data.songs[i].al.picUrl
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (saveId == -1) {
|
||||||
|
songInfo.push(musicDate)
|
||||||
|
} else {
|
||||||
|
songInfo[saveId] = musicDate
|
||||||
|
}
|
||||||
|
await redisSetKey(REDIS_YUNZAI_SONGINFO, songInfo)
|
||||||
|
const data = await new PickSongList(e).getData(musicDate.data)
|
||||||
|
let img = await puppeteer.screenshot("pick-song", data);
|
||||||
|
e.reply(img, true);
|
||||||
|
} else {
|
||||||
|
e.reply('暂未找到你想听的歌哦~')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (await redisGetKey(REDIS_YUNZAI_SONGINFO) != []) {
|
||||||
|
if (e.msg.match(/听(\d+)/)) {
|
||||||
|
const pickNumber = e.msg.match(/听(\d+)/)[1] - 1
|
||||||
|
let group_id = e.group.group_id
|
||||||
|
if (!group_id) return
|
||||||
|
let songInfo = await redisGetKey(REDIS_YUNZAI_SONGINFO)
|
||||||
|
const saveId = songInfo.findIndex(item => item.group_id === e.group.group_id)
|
||||||
|
const AUTO_NETEASE_SONG_DOWNLOAD = autoSelectNeteaseApi + "/song/url/v1?id={}&level=" + this.neteaseCloudAudioQuality;
|
||||||
|
const pickSongUrl = AUTO_NETEASE_SONG_DOWNLOAD.replace("{}", songInfo[saveId].data[pickNumber].id)
|
||||||
|
const statusUrl = autoSelectNeteaseApi + '/login/status' //用户状态API
|
||||||
|
const isCkExpired = await this.checkCooike(statusUrl)
|
||||||
|
// // 请求netease数据
|
||||||
|
this.neteasePlay(e, pickSongUrl, songInfo[saveId].data, pickNumber, isCkExpired)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放策略
|
||||||
|
async playSong(e) {
|
||||||
|
if(!this.useNeteaseSongRequest) {
|
||||||
|
logger.info('当前未开启网易云点歌')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 只在群里可以使用
|
||||||
|
let group_id = e.group.group_id
|
||||||
|
if (!group_id) return
|
||||||
|
const autoSelectNeteaseApi = await this.pickApi()
|
||||||
|
let songInfo = []
|
||||||
|
// 获取搜索歌曲列表信息
|
||||||
|
const AUTO_NETEASE_SONG_DOWNLOAD = autoSelectNeteaseApi + "/song/url/v1?id={}&level=" + this.neteaseCloudAudioQuality;
|
||||||
|
let searchUrl = autoSelectNeteaseApi + '/search?keywords={}&limit=1' //搜索API
|
||||||
|
let detailUrl = autoSelectNeteaseApi + "/song/detail?ids={}" //歌曲详情API
|
||||||
|
if (e.msg.replace(/\s+/g, "").match(/播放(.+)/)) {
|
||||||
|
const songKeyWord = e.msg.replace(/\s+/g, "").match(/播放(.+)/)[1]
|
||||||
|
searchUrl = searchUrl.replace("{}", songKeyWord)
|
||||||
|
await axios.get(searchUrl, {
|
||||||
|
headers: {
|
||||||
|
"User-Agent": COMMON_USER_AGENT
|
||||||
|
},
|
||||||
|
}).then(async res => {
|
||||||
|
if (res.data.result.songs) {
|
||||||
|
for (const info of res.data.result.songs) {
|
||||||
|
songInfo.push({
|
||||||
|
'id': info.id,
|
||||||
|
'songName': info.name,
|
||||||
|
'singerName': info.artists[0]?.name,
|
||||||
|
'duration': formatTime(info.duration)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const ids = songInfo.map(item => item.id).join(',');
|
||||||
|
detailUrl = detailUrl.replace("{}", ids)
|
||||||
|
await axios.get(detailUrl, {
|
||||||
|
headers: {
|
||||||
|
"User-Agent": COMMON_USER_AGENT
|
||||||
|
},
|
||||||
|
}).then(res => {
|
||||||
|
for (let i = 0; i < res.data.songs.length; i++) {
|
||||||
|
songInfo[i].cover = res.data.songs[i].al.picUrl
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const pickSongUrl = AUTO_NETEASE_SONG_DOWNLOAD.replace("{}", songInfo[0].id)
|
||||||
|
const statusUrl = autoSelectNeteaseApi + '/login/status' //用户状态API
|
||||||
|
const isCkExpired = await this.checkCooike(statusUrl)
|
||||||
|
this.neteasePlay(e, pickSongUrl, songInfo, 0, isCkExpired)
|
||||||
|
} else {
|
||||||
|
e.reply('暂未找到你想听的歌哦~')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 判断是否海外服务器
|
||||||
|
async isOverseasServer() {
|
||||||
|
// 如果第一次使用没有值就设置
|
||||||
|
if (!(await redisExistKey(REDIS_YUNZAI_ISOVERSEA))) {
|
||||||
|
await redisSetKey(REDIS_YUNZAI_ISOVERSEA, {
|
||||||
|
os: false,
|
||||||
|
})
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 如果有就取出来
|
||||||
|
return (await redisGetKey(REDIS_YUNZAI_ISOVERSEA)).os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API选择
|
||||||
|
async pickApi() {
|
||||||
|
const isOversea = await this.isOverseasServer();
|
||||||
|
let autoSelectNeteaseApi
|
||||||
|
if (this.useLocalNeteaseAPI) {
|
||||||
|
// 使用自建 API
|
||||||
|
return autoSelectNeteaseApi = this.neteaseCloudAPIServer
|
||||||
|
} else {
|
||||||
|
// 自动选择 API
|
||||||
|
return autoSelectNeteaseApi = isOversea ? NETEASE_SONG_DOWNLOAD : NETEASE_API_CN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测cooike活性
|
||||||
|
|
||||||
|
async checkCooike(statusUrl) {
|
||||||
|
let status
|
||||||
|
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进行高音质下载')
|
||||||
|
status = true
|
||||||
|
} else {
|
||||||
|
logger.info('ck失效,将启用临时接口下载')
|
||||||
|
status = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网易云音乐下载策略
|
||||||
|
neteasePlay(e, pickSongUrl, songInfo, pickNumber = 0, isCkExpired) {
|
||||||
|
axios.get(pickSongUrl, {
|
||||||
|
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 = songInfo[pickNumber].songName + '-' + songInfo[pickNumber].singerName
|
||||||
|
// 一般这个情况是VIP歌曲 (如果没有url或者是国内,公用接口暂时不可用,必须自建并且ck可用状态才能进行高质量解析)
|
||||||
|
if (!isCkExpired || !this.useLocalNeteaseAPI || url == null) {
|
||||||
|
url = await this.musicTempApi(e, title, "网易云音乐");
|
||||||
|
} else {
|
||||||
|
// 拥有ck,并且有效,直接进行解析
|
||||||
|
let audioInfo = AudioLevel;
|
||||||
|
if (AudioLevel == '杜比全景声') {
|
||||||
|
audioInfo += '\n(杜比下载文件为MP4,编码格式为AC-4,需要设备支持才可播放)';
|
||||||
|
}
|
||||||
|
e.reply([segment.image(songInfo[pickNumber].cover), `${this.identifyPrefix}识别:网易云音乐,${title}\n当前下载音质: ${audioInfo}\n预估大小: ${AudioSize}MB`]);
|
||||||
|
}
|
||||||
|
// 动态判断后缀名
|
||||||
|
let musicExt = resp.data.data?.[0]?.type
|
||||||
|
// 下载音乐
|
||||||
|
downloadAudio(url, this.getCurDownloadPath(e), title, 'follow', musicExt).then(async path => {
|
||||||
|
// 发送语音
|
||||||
|
if (musicExt != 'mp4') {
|
||||||
|
await e.reply(segment.record(path));
|
||||||
|
}
|
||||||
|
// 上传群文件
|
||||||
|
await this.uploadGroupFile(e, path);
|
||||||
|
// 删除文件
|
||||||
|
await checkAndRemoveFile(path);
|
||||||
|
}).catch(err => {
|
||||||
|
logger.error(`下载音乐失败,错误信息为: ${err}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async musicTempApi(e, title, musicType) {
|
||||||
|
let musicReqApi = NETEASE_TEMP_API;
|
||||||
|
// 临时接口,title经过变换后搜索到的音乐质量提升
|
||||||
|
const vipMusicData = await axios.get(musicReqApi.replace("{}", title.replace("-", " ")), {
|
||||||
|
headers: {
|
||||||
|
"User-Agent": COMMON_USER_AGENT,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const messageTitle = title + "\nR插件检测到当前为VIP音乐,正在转换...";
|
||||||
|
// ??后的内容是适配`QQ_MUSIC_TEMP_API`、最后是汽水
|
||||||
|
const url = vipMusicData.data?.music_url ?? vipMusicData.data?.data?.music_url ?? vipMusicData.data?.music;
|
||||||
|
const cover = vipMusicData.data?.cover ?? vipMusicData.data?.data?.cover ?? vipMusicData.data?.cover;
|
||||||
|
await e.reply([segment.image(cover), `${this.identifyPrefix}识别:${musicType},${messageTitle}`]);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前发送人/群的下载路径
|
||||||
|
* @param e Yunzai 机器人事件
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getCurDownloadPath(e) {
|
||||||
|
return `${this.defaultPath}${e.group_id || e.user_id}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传到群文件
|
||||||
|
* @param e 交互事件
|
||||||
|
* @param path 上传的文件所在路径
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
async uploadGroupFile(e, path) {
|
||||||
|
// 判断是否是ICQQ
|
||||||
|
if (e.bot?.sendUni) {
|
||||||
|
await e.group.fs.upload(path);
|
||||||
|
} else {
|
||||||
|
await e.group.sendFile(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1789,8 +1789,7 @@ export class tools extends plugin {
|
|||||||
e.reply([segment.image(coverUrl), `${this.identifyPrefix}识别:网易云音乐,${title}\n当前下载音质: ${audioInfo}\n预估大小: ${AudioSize}MB`]);
|
e.reply([segment.image(coverUrl), `${this.identifyPrefix}识别:网易云音乐,${title}\n当前下载音质: ${audioInfo}\n预估大小: ${AudioSize}MB`]);
|
||||||
}
|
}
|
||||||
// 动态判断后缀名
|
// 动态判断后缀名
|
||||||
const extensionPattern = /\.([a-zA-Z0-9]+)$/;
|
let musicExt = resp.data.data?.[0]?.type
|
||||||
let musicExt = url.match(extensionPattern)?.[0].replace("\.", "");
|
|
||||||
// 下载音乐
|
// 下载音乐
|
||||||
downloadAudio(url, this.getCurDownloadPath(e), title, 'follow', musicExt).then(async path => {
|
downloadAudio(url, this.getCurDownloadPath(e), title, 'follow', musicExt).then(async path => {
|
||||||
// 发送语音
|
// 发送语音
|
||||||
|
@ -23,6 +23,8 @@ biliDownloadMethod: 0 # 哔哩哔哩的下载方式:0默认使用原生稳定
|
|||||||
biliResolution: 1 # 哔哩哔哩的下载画质,0为原画,1为清晰画,2为流畅画(默认为0)
|
biliResolution: 1 # 哔哩哔哩的下载画质,0为原画,1为清晰画,2为流畅画(默认为0)
|
||||||
|
|
||||||
useLocalNeteaseAPI: false # 是否使用网易云解析自建API
|
useLocalNeteaseAPI: false # 是否使用网易云解析自建API
|
||||||
|
useNeteaseSongRequest: false # 是否开启网易云点歌功能
|
||||||
|
songRequestMaxList: 10 # 网易云点歌请求最大列表数
|
||||||
neteaseCookie: '' # 网易云ck
|
neteaseCookie: '' # 网易云ck
|
||||||
neteaseCloudAPIServer: '' # 网易云自建服务器地址
|
neteaseCloudAPIServer: '' # 网易云自建服务器地址
|
||||||
neteaseCloudAudioQuality: exhigh # 网易云解析最高音质 默认exhigh(极高) 分类:standard => 标准,higher => 较高, exhigh=>极高, lossless=>无损, hires=>Hi-Res, jyeffect => 高清环绕声, sky => 沉浸环绕声, dolby => 杜比全景声, jymaster => 超清母带
|
neteaseCloudAudioQuality: exhigh # 网易云解析最高音质 默认exhigh(极高) 分类:standard => 标准,higher => 较高, exhigh=>极高, lossless=>无损, hires=>Hi-Res, jyeffect => 高清环绕声, sky => 沉浸环绕声, dolby => 杜比全景声, jymaster => 超清母带
|
||||||
|
@ -80,6 +80,13 @@ export const REDIS_YUNZAI_ISOVERSEA = "Yz:rconsole:tools:oversea";
|
|||||||
*/
|
*/
|
||||||
export const REDIS_YUNZAI_LAGRANGE = "Yz:rconsole:tools:lagrange";
|
export const REDIS_YUNZAI_LAGRANGE = "Yz:rconsole:tools:lagrange";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存音乐搜索列表
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
export const REDIS_YUNZAI_SONGINFO = "Yz:rconsole:tools:songinfo";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 某些功能的解析白名单
|
* 某些功能的解析白名单
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -195,7 +195,7 @@ export const NETEASE_SONG_DETAIL = "https://neteasecloudmusicapi.vercel.app"
|
|||||||
* 致谢 NeteaseCloudMusicApi:https://gitlab.com/Binaryify/neteasecloudmusicapi
|
* 致谢 NeteaseCloudMusicApi:https://gitlab.com/Binaryify/neteasecloudmusicapi
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
export const NETEASE_API_CN = 'https://www.markingchen.ink';
|
export const NETEASE_API_CN = 'https://zmusic.i9mr.com';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载VIP的临时接口
|
* 下载VIP的临时接口
|
||||||
|
@ -260,6 +260,25 @@ export function supportGuoba() {
|
|||||||
component: "Switch",
|
component: "Switch",
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: "tools.useNeteaseSongRequest",
|
||||||
|
label: "开启网易云点歌功能",
|
||||||
|
bottomHelpMessage:
|
||||||
|
"默认不开启,建议搭配自建网易云API使用,以获得最佳体验",
|
||||||
|
component: "Switch",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "tools.songRequestMaxList",
|
||||||
|
label: "点歌列表长度",
|
||||||
|
bottomHelpMessage:
|
||||||
|
"网易云点歌选择列表长度默认10",
|
||||||
|
component: "InputNumber",
|
||||||
|
required: false,
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "填入长度",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: "tools.neteaseCloudAPIServer",
|
field: "tools.neteaseCloudAPIServer",
|
||||||
label: "自建网易云API地址",
|
label: "自建网易云API地址",
|
||||||
|
17
model/pick-song.js
Normal file
17
model/pick-song.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import Base from './base.js'
|
||||||
|
|
||||||
|
export default class PickSongList extends Base {
|
||||||
|
constructor (e) {
|
||||||
|
super(e)
|
||||||
|
this.model = 'pick-song'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 生成版本信息图片 */
|
||||||
|
async getData (songData) {
|
||||||
|
return {
|
||||||
|
...this.screenData,
|
||||||
|
saveId: 'pick-song',
|
||||||
|
songData: songData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
resources/font/江城月湖体 400W.ttf
Normal file
BIN
resources/font/江城月湖体 400W.ttf
Normal file
Binary file not shown.
106
resources/html/pick-song/pick-song.css
Normal file
106
resources/html/pick-song/pick-song.css
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap');
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'number';
|
||||||
|
src: url("../../font/江城月湖体\ 400W.ttf");
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.songList {
|
||||||
|
min-height: 50vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: #121212ef;
|
||||||
|
position: relative;
|
||||||
|
padding: 0px 40px 20px 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.songListNav {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
margin-top: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navText {
|
||||||
|
font-size: 30px;
|
||||||
|
color: #fff;
|
||||||
|
margin-left: 20px;
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.songName{
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.singerText{
|
||||||
|
font-size: 25px;
|
||||||
|
color: #aaa;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.navInfo {
|
||||||
|
display: flex;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navDuration {
|
||||||
|
color: #aaa;
|
||||||
|
width: 40px;
|
||||||
|
font-size: 25px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navInfo img {
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bgicon {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50% - 100px);
|
||||||
|
left: calc(50% - 85px);
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bgicon img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0.4
|
||||||
|
}
|
||||||
|
|
||||||
|
.number {
|
||||||
|
width: 40px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 40px;
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 25px;
|
||||||
|
margin-left: -20px;
|
||||||
|
font-family: 'number'
|
||||||
|
}
|
34
resources/html/pick-song/pick-song.html
Normal file
34
resources/html/pick-song/pick-song.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>搜索歌单</title>
|
||||||
|
<style>
|
||||||
|
@import url('{{pluResPath}}html/pick-song/pick-song.css');
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="songList">
|
||||||
|
{{each songData info key}}
|
||||||
|
<div class="songListNav">
|
||||||
|
<div class="navInfo">
|
||||||
|
<div class="number">{{ key+1 }}</div>
|
||||||
|
<img src="{{ info.cover }}" alt="">
|
||||||
|
<div class="navText">
|
||||||
|
<div class="songName">{{ info.songName }}</div>
|
||||||
|
<div class="singerText">{{ info.singerName }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navDuration">{{ info.duration }}</div>
|
||||||
|
</div>
|
||||||
|
{{ /each }}
|
||||||
|
<div class="bgicon">
|
||||||
|
<img src="{{pluResPath}}img/icon/neteaseRank.png" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
BIN
resources/img/icon/neteaseRank.png
Normal file
BIN
resources/img/icon/neteaseRank.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
11
utils/other.js
Normal file
11
utils/other.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export function formatTime(timestamp) {
|
||||||
|
const totalSeconds = Math.floor(timestamp / 1000); // 转换为秒
|
||||||
|
const minutes = Math.floor(totalSeconds / 60); // 分钟
|
||||||
|
const seconds = totalSeconds % 60; // 秒钟
|
||||||
|
|
||||||
|
// 补零格式化
|
||||||
|
const formattedMinutes = String(minutes).padStart(2, '0');
|
||||||
|
const formattedSeconds = String(seconds).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${formattedMinutes}:${formattedSeconds}`;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user