mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 08:09:19 +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`]);
|
||||
}
|
||||
// 动态判断后缀名
|
||||
const extensionPattern = /\.([a-zA-Z0-9]+)$/;
|
||||
let musicExt = url.match(extensionPattern)?.[0].replace("\.", "");
|
||||
let musicExt = resp.data.data?.[0]?.type
|
||||
// 下载音乐
|
||||
downloadAudio(url, this.getCurDownloadPath(e), title, 'follow', musicExt).then(async path => {
|
||||
// 发送语音
|
||||
|
@ -78,4 +78,4 @@
|
||||
list:
|
||||
- icon: update
|
||||
title: "#R插件更新"
|
||||
desc: "进行更新R插件"
|
||||
desc: "进行更新R插件"
|
||||
|
@ -23,6 +23,8 @@ biliDownloadMethod: 0 # 哔哩哔哩的下载方式:0默认使用原生稳定
|
||||
biliResolution: 1 # 哔哩哔哩的下载画质,0为原画,1为清晰画,2为流畅画(默认为0)
|
||||
|
||||
useLocalNeteaseAPI: false # 是否使用网易云解析自建API
|
||||
useNeteaseSongRequest: false # 是否开启网易云点歌功能
|
||||
songRequestMaxList: 10 # 网易云点歌请求最大列表数
|
||||
neteaseCookie: '' # 网易云ck
|
||||
neteaseCloudAPIServer: '' # 网易云自建服务器地址
|
||||
neteaseCloudAudioQuality: exhigh # 网易云解析最高音质 默认exhigh(极高) 分类:standard => 标准,higher => 较高, exhigh=>极高, lossless=>无损, hires=>Hi-Res, jyeffect => 高清环绕声, sky => 沉浸环绕声, dolby => 杜比全景声, jymaster => 超清母带
|
||||
|
@ -10,4 +10,4 @@
|
||||
输入<span class="cmd">#R更新</span>更新插件,
|
||||
输入<span class="cmd">#R版本</span>获取插件版本,
|
||||
],
|
||||
}
|
||||
}
|
@ -80,6 +80,13 @@ export const REDIS_YUNZAI_ISOVERSEA = "Yz:rconsole:tools:oversea";
|
||||
*/
|
||||
export const REDIS_YUNZAI_LAGRANGE = "Yz:rconsole:tools:lagrange";
|
||||
|
||||
/**
|
||||
* 缓存音乐搜索列表
|
||||
* @type {string}
|
||||
*/
|
||||
export const REDIS_YUNZAI_SONGINFO = "Yz:rconsole:tools:songinfo";
|
||||
|
||||
|
||||
/**
|
||||
* 某些功能的解析白名单
|
||||
* @type {string}
|
||||
|
@ -195,7 +195,7 @@ export const NETEASE_SONG_DETAIL = "https://neteasecloudmusicapi.vercel.app"
|
||||
* 致谢 NeteaseCloudMusicApi:https://gitlab.com/Binaryify/neteasecloudmusicapi
|
||||
* @type {string}
|
||||
*/
|
||||
export const NETEASE_API_CN = 'https://www.markingchen.ink';
|
||||
export const NETEASE_API_CN = 'https://zmusic.i9mr.com';
|
||||
|
||||
/**
|
||||
* 下载VIP的临时接口
|
||||
|
@ -260,6 +260,25 @@ export function supportGuoba() {
|
||||
component: "Switch",
|
||||
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",
|
||||
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